Как мне написать декоратор, который восстанавливает cwd?
Как мне написать декоратор, который восстанавливает текущий рабочий каталог до того, что было до вызова декорированной функции? Другими словами, если я использую декоратор на функцию, которая делает os.chdir()
cwd не будет изменен после вызова функции.
5 ответов
Модуль path.py (который вы действительно должны использовать, если имеете дело с путями в скриптах Python) имеет менеджер контекста:
subdir = d / 'subdir' #subdir is a path object, in the path.py module
with subdir:
# here current dir is subdir
#not anymore
(кредиты идут к этому сообщению в блоге от Роберто Альсина)
Ответ для декоратора был дан; он работает на этапе определения функции в соответствии с запросом.
В Python 2.5+ у вас также есть возможность сделать это на этапе вызова функции с помощью диспетчера контекста:
from __future__ import with_statement # needed for 2.5 ≤ Python < 2.6
import contextlib, os
@contextlib.contextmanager
def remember_cwd():
curdir= os.getcwd()
try: yield
finally: os.chdir(curdir)
который может быть использован при необходимости во время вызова функции как:
print "getcwd before:", os.getcwd()
with remember_cwd():
walk_around_the_filesystem()
print "getcwd after:", os.getcwd()
Это хороший вариант, чтобы иметь.
РЕДАКТИРОВАТЬ: я добавил обработку ошибок, как предложено Codeape. Поскольку за мой ответ проголосовали, было бы справедливо предложить полный ответ, за исключением всех других вопросов.
Приведенные ответы не учитывают, что упакованная функция может вызвать исключение. В этом случае каталог никогда не будет восстановлен. Код ниже добавляет обработку исключений к предыдущим ответам.
в качестве декоратора:
def preserve_cwd(function):
@functools.wraps(function)
def decorator(*args, **kwargs):
cwd = os.getcwd()
try:
return function(*args, **kwargs)
finally:
os.chdir(cwd)
return decorator
и как менеджер контекста:
@contextlib.contextmanager
def remember_cwd():
curdir = os.getcwd()
try:
yield
finally:
os.chdir(curdir)
Вам не нужно писать это для вас. С Python 3.11 разработчики написали его для вас. Ознакомьтесь с их кодом на github.com/python/cpython. Это в модуле contextlib.
import contextlib
with contextlib.chdir('/path/to/cwd/to'):
pass
def preserve_cwd(function):
def decorator(*args, **kwargs):
cwd = os.getcwd()
result = function(*args, **kwargs)
os.chdir(cwd)
return result
return decorator
Вот как это используется:
@preserve_cwd
def test():
print 'was:',os.getcwd()
os.chdir('/')
print 'now:',os.getcwd()
>>> print os.getcwd()
/Users/dspitzer
>>> test()
was: /Users/dspitzer
now: /
>>> print os.getcwd()
/Users/dspitzer