вручную открыть диспетчер контекста

У меня вопрос, как я могу запустить любой диспетчер контекста без использования with?


В Python есть идея диспетчеров контекста,

вместо того

file = open('some_file', 'w')
try:
    file.write('Hola!')
finally:
    file.close()
# end try

ты можешь написать

with open('some_file', 'w') as opened_file:
    opened_file.write('Hola!')
# end with

В то время как в большинстве случаев второй вариант является идеальным решением для тестирования и изучения в интерактивной консоли, первое может быть гораздо лучше использовано, так как вы можете писать его построчно.

>>> file = open('some_file', 'w')
>>> file.write('Hola!')
>>> file.close()

Мой вопрос в том, как я могу выполнить любой with диспетчер контекста, подобный этому, который лучше всего подходит для изучения?


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

import flask

app = flask.Flask(__name__)

with app.test_request_context('/?name=Peter'):
    assert flask.request.path == '/'
    assert flask.request.args['name'] == 'Peter'

из документации Flask


person luckydonald    schedule 06.08.2018    source источник


Ответы (2)


Вы по-прежнему можете использовать синтаксис with в интерактивной консоли, однако контекст основан на двух магических методах __enter__ и __exit__, поэтому вы можете просто использовать их:

class MyCtx(object):
  def __init__(self, f):
    self.f = f

  def __enter__(self):
    print("Enter")
    return self.f

  def __exit__(*args, **kwargs):
    print("Exit")

def foo():
  print("Hello")

обычно вы делаете:

with MyCtx(foo) as f:
  f()

Такой же как:

ctx = MyCtx(foo)
f = ctx.__enter__()
f() 
ctx.__exit__()

Здесь у вас есть живой пример

Помните, что метод context __exit__ используется для управления ошибками в контексте, поэтому большинство из них имеют сигнатуру __exit__(exception_type, exception_value, traceback), если вам не нужно обрабатывать его для тестов, просто укажите ему несколько None значений:

__exit__(None, None, None)
person Netwave    schedule 06.08.2018
comment
Пример работает очень хорошо, но в моем случае с фляжкой я получаю TypeError: __exit__() missing 3 required positional arguments: 'exc_type', 'exc_value', and 'tb'. Я читал об отсутствии аргументов exception_type, exception_value и traceback для функции __exit__ рассматривается как анти-шаблон. Не могли бы вы рассказать об этом подробнее и как с этим справиться? - person luckydonald; 06.08.2018
comment
@luckydonald, для этого вам нужно будет использовать блок try/except после __enter __ и захватить исключение, также использовать трассировку и передать результаты методу __exit__. - person Netwave; 06.08.2018
comment
но в случае отсутствия исключения это было бы (None, None, None)? - person luckydonald; 06.08.2018
comment
@luckydonald, точно - person Netwave; 06.08.2018
comment
не могли бы вы обновить свой ответ этой дополнительной информацией, поскольку потребность в этих параметрах, похоже, существует в большинстве __exit__ реализаций. - person luckydonald; 06.08.2018
comment
@luckydonald, ofc, добавил - person Netwave; 06.08.2018

Вы можете вызвать app.test_request.context('/?name=Peter') переменной (например, ctx), а затем вызвать ctx.__enter__() для входа в диспетчер контекста и ctx.__exit__(None, None, None) для выполнения очистки. Обратите внимание, что вы теряете гарантии безопасности контекстных менеджеров, если не поместите ctx.__exit__ в предложение finally.

person L3viathan    schedule 06.08.2018