Como escrevo um decorador que restaura a CWD?
Pergunta
Como escrevo um decorador que restaura o diretório de trabalho atual ao que era antes que a função decorada fosse chamada? Em outras palavras, se eu usar o decorador em uma função que faz um os.chdir()
, a CWD não será alterada depois que a função for chamada.
Solução
o path.py O módulo (que você realmente deve usar se lidar com caminhos em scripts Python) tem um gerente de contexto:
subdir = d / 'subdir' #subdir is a path object, in the path.py module
with subdir:
# here current dir is subdir
#not anymore
(os créditos vão para esta postagem do blog de Roberto Alsina)
Outras dicas
A resposta para um decorador foi dada; Funciona no estágio de definição da função, conforme solicitado.
Com o Python 2.5+, você também tem a opção de fazer isso na função ligar estágio usando um gerenciador de contexto:
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)
que pode ser usado, se necessário, no tempo de chamada da função como:
print "getcwd before:", os.getcwd()
with remember_cwd():
walk_around_the_filesystem()
print "getcwd after:", os.getcwd()
É uma boa opção ter.
EDIT: Adicionei o manuseio de erros, conforme sugerido pelo Codeape. Como minha resposta foi elevada, é justo oferecer uma resposta completa, todos os outros problemas de lado.
As respostas dadas não levam em consideração que a função embrulhada pode aumentar uma exceção. Nesse caso, o diretório nunca será restaurado. O código abaixo adiciona manipulação de exceções às respostas anteriores.
Como decorador:
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
E como gerente de contexto:
@contextlib.contextmanager
def remember_cwd():
curdir = os.getcwd()
try:
yield
finally:
os.chdir(curdir)
def preserve_cwd(function):
def decorator(*args, **kwargs):
cwd = os.getcwd()
result = function(*args, **kwargs)
os.chdir(cwd)
return result
return decorator
Veja como é usado:
@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