Как написать декоратор, восстанавливающий cwd?

Как мне написать декоратор, который восстанавливает текущий рабочий каталог до того, каким он был до вызова декорированной функции? Другими словами, если я использую декоратор для функции, которая выполняет os.chdir(), cwd не изменится после вызова функции.


person Daryl Spitzer    schedule 03.10.2008    source источник
comment
А вы задали вопрос и сами ответили за 3 минуты, потому что…? Очевидно, вы получили ответ (который вряд ли можно улучшить) еще до того, как задали вопрос. Я действительно хотел бы знать ваши рассуждения.   -  person tzot    schedule 04.10.2008
comment
В FAQ говорится, что также совершенно нормально задавать вопросы по программированию и отвечать на них. В нем перечислены три обязательных критерия для вопросов, и вы не знаете, что ответ не входит в их число.   -  person Steve Jessop    schedule 04.10.2008
comment
Я написал код, а потом оказалось (после рефакторинга), что он мне не нужен. Я подумал, что stackoverflow - хорошее место для его архивации, и, возможно, другим будет полезно.   -  person Daryl Spitzer    schedule 04.10.2008
comment
Другими словами: ваш вопрос должен быть ясным, что это выдуманный вопрос, и вам не нужна помощь, вы просто хотели поделиться своей мудростью / опытом с остальным миром. Не заставляйте других тратить время на то, чтобы ответить на не-вопрос.   -  person tzot    schedule 04.10.2008
comment
Я думаю, что комментарий Jeopardy просто должен означать, что заголовок все еще должен быть как мне написать декоратор для X ?, а не здесь полезный декоратор для X. Я согласен, что примечание в вопросе сэкономит людям время на дублирование решений, пока вы набираем. Но они могли бы лучше твоего ...   -  person Steve Jessop    schedule 04.10.2008
comment
... например, точка зрения codeape об обработке исключений в данном случае является улучшением решения, предложенного опрашивающим, поэтому время не было потрачено зря.   -  person Steve Jessop    schedule 04.10.2008
comment
Что ж, я рад, что задал вопрос, потому что кодовая лента улучшила мой ответ, и ΤΖΩΤΖΙΟΥ придумал диспетчер контекста, который мне не приходил в голову.   -  person Daryl Spitzer    schedule 08.10.2008


Ответы (4)


Модуль 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

(кредиты принадлежат этому сообщению в блоге Роберто Альсины)

person CharlesB    schedule 24.12.2012
comment
Если path.py теперь встроен, возможно, вам следует ответить на stackoverflow.com/questions/3899761/. - person Daryl Spitzer; 27.12.2012
comment
к сожалению, это не так, но спасибо, я не знал о вопросе - person CharlesB; 27.12.2012
comment
Думаю, я неверно истолковал ваш ответ. Вы имели в виду, что диспетчер контекста встроен в path.py? (Я думал, вы имели в виду, что path.py теперь встроен в Python.) - person Daryl Spitzer; 27.12.2012
comment
Поскольку Дэрил Спитцер предположил, что path.py совпадает с pathlib, вы также можете предположить, что их диспетчеры контекста ведут себя таким же образом, но это не так: pathlib.Path не касается CWD, а вместо этого действует как диспетчер контекста из open() (закрывает потенциально открытый дескриптор файла). - person Pugsley; 03.10.2020

Дан ответ для декоратора; он работает на этапе определения функции по запросу.

В 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()

Это хороший вариант.

РЕДАКТИРОВАТЬ: я добавил обработку ошибок, как это было предложено кодовой лентой. Поскольку за мой ответ проголосовали, будет справедливо предложить исчерпывающий ответ, не считая всех других вопросов.

person tzot    schedule 03.10.2008
comment
И его можно использовать для написания вышеупомянутого декоратора :) - person Constantin; 04.10.2008
comment
Нужна ли для обработки ошибок явная попытка / наконец? Я думал, что смысл менеджеров контекста в том, что всегда вызывается MANAGER .__ exit__. Но я никогда не пробовал использовать декоратор из contextlib, поэтому не знаю, в чем проблема. - person Adrian Ratnapala; 21.10.2011
comment
Нет, явное try/finally не требуется, но вам, скорее всего, понадобится предложение try/except для обработки сбоев. - person tzot; 21.10.2011

В приведенных ответах не учитывается, что обернутая функция может вызвать исключение. В этом случае каталог никогда не будет восстановлен. В приведенном ниже коде к предыдущим ответам добавлена ​​обработка исключений.

как декоратор:

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)
person codeape    schedule 04.10.2008

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
person Daryl Spitzer    schedule 03.10.2008