저는이 문제로 오래 동안 고심하고 있습니다. 필자가 작성한 코드 중 일부에서는 여러 파일을 작성하고 필요에 따라 선택적으로 디렉토리 트리를 작성해야합니다. 내 생각은 다음과 같습니다 : 예외 IOError를 catch하고 첫 번째 인수가 ENOENT 인 경우 디렉토리 구조를 만들고 파일을 다시 쓰려고 시도합니다.컨텍스트 관리자와 데코레이터를 섞은 예외가 발견되지 않았습니다.
나는 비교적 작은 재시도 함수를 작성했지만, 예외를 던질 수있는 "모든"코드로 일반화하고 싶습니다. 내가 open()
예외를 throw 할 때 실패하는 것을 보여주기 위해 최소한의 코드를 강화했습니다
def retry(f):
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except:
print "Gotcha here!"
return wrapper
def update(file, value):
@contextmanager
@retry
def safeopen(file, mode):
with open(file, mode) as f:
yield f
try:
with safeopen(file, 'w') as f:
f.write(value)
except:
print "Gotcha there!"
update('tests/nonexisting/dummy.txt', 'Dummy line')
: 나는 이런 식으로 뭔가에 올 때까지 그것은 모든 일했다. 이 코드에서 예외는 update()
의 except 블록에서만 잡히고 wrapper()
이 아니므로 항상 Gotcha here
이긴하지만 나는 항상 Gotcha there!
을 얻습니다. 나는 @decorator와 @contextmanager 라인을 교환하려고 시도하지 않았다. 래퍼가 호출되는지 확인하고 확인했습니다. 그냥 그것은 f()
에서 예외를 잡는다.
내가 뭘 잘못하고 있니?
이 자세한 설명은 매우 * 많은 * 감사 : 지금은 오히려 컨텍스트 매니저보다, 발전기 기능을 장식하고 있기 때문에 직접
safeopen
을 장식, 당신은retry
구현이 조금 더 간단 할 수 있습니다. 나는 정말로 이런 것을 의심했다.나는 장식 자의 순서가 중요하기 때문에'retry()'가 ** @ contextManager ** 장식 자에 의해 "포장"되기 전에'safeopen()'만 꾸며야한다고 생각했다. 나는 파이썬이 내가 이해하는 것과 다르게 작동한다고 생각한다. 당신의 예제는 또한 데코레이터가 함수 또는 생성기에서 작동하는지 여부를 알고 있어야한다는 것을 의미합니다. –@Nasha 그렇습니다. 데코레이터는 장식하고있는 기능의 종류를 알고 있어야합니다. 데코레이터의 순서에 관해서도,'contextmanager' 데코레이터 안에'재시도'데코레이터를 넣더라도 컨텍스트 매니저가 아닌 생성기 함수를 꾸미고 있습니다. 함수를 호출 할 때 생성자 함수 본문은 실행되지 않습니다. 실행될 바디에 대해 반환 된 생성자 객체에서'next'를 호출해야합니다. 그래서, 당신이 할 수있는 일은'재 시도'(retry) 안에'return next (f (* args, ** kwargs))'를 호출하는 것입니다. 그건 내 대답에 실제로 사용되는 방법보다 바람직 할 것이다. – dano
@ Nasha 데코레이터를 반대로하여 예제 코드를 보여주기 위해 필자의 대답을 편집했습니다. – dano