Improvando uma substituição de entrega para a declaração "com" para o Python 2.4

StackOverflow https://stackoverflow.com/questions/1547526

  •  20-09-2019
  •  | 
  •  

Pergunta

Você pode sugerir uma maneira de codificar uma substituição de queda para a declaração "com" que funcionará no Python 2.4?

Seria um hack, mas me permitiria portar meu projeto para o Python 2.4 mais bem.

EDITAR:Removido de esboço de metaclasse irrelevante

Foi útil?

Solução

Basta usar o TryFinally.

Realmente, isso pode ser bom como um exercício mental, mas se você realmente o fizer em código, você vai acabar com o código feio e difícil de manter.

Outras dicas

Você poderia (AB) usar decoradores para fazer isso, eu acho. Os seguintes trabalhos, por exemplo:

def execute_with_context_manager(man):
    def decorator(f):
        target = man.__enter__()
        exc = True
        try:
            try:
                f(target)
            except:
                exc = False
                if not man.__exit__(*sys.exc_info()):
                    raise
        finally:
            if exc:
                man.__exit__(None, None, None)
        return None
    return decorator

@execute_with_context_manager(open("/etc/motd"))
def inside(motd_file):
    for line in motd_file:
        print line,

(Bem, nos objetos do arquivo python 2.4 não possuem métodos __enter__ e __Exit__, mas, caso contrário, funciona)

A ideia é que você está substituindo a linha em:

with bar() as foo:
    do_something_with(foo)
    do_something_else_with(foo)
    # etc...

com a função decorada "Declaração" em:

@execute_with_context_manager( bar() )
def dummyname( foo ):
    do_something_with(foo)
    do_something_else_with(foo)
    # etc...

mas obtendo o mesmo comportamento (o código do_something _... executado). Observe o decorador transforma a declaração de função em um invocação imediata o que é mais do que um pouco mal.

Como você precisa sair do gerenciador de contexto durante erros e não erros, não acho que seja possível fazer uma base de gestão genérica com metaclasses, ou de fato. Você precisará tentar/finalmente blocos para isso.

Mas talvez seja possível fazer outra coisa no seu caso. Isso depende do que você usa o gerenciador de contexto.

Usando __del__ Pode ajudar em alguns casos, como o recurso de negociação, mas como você não pode ter certeza de que ele é chamado, ele só pode ser usado para liberar recursos que serão lançados quando o programa sair. Isso também não funcionará se você estiver lidando com exceções no __exit__ método.

Eu acho que o método mais limpo é envolver todo o gerenciamento de contexto em uma espécie de chamada de gerenciamento de contexto e extrair o bloco de código para um método. Algo assim (código não testado, mas principalmente roubado do PEP 343):

def call_as_context_manager(mgr, function):
    exit = mgr.__exit__
    value = mgr.__enter__()
    exc = True
    try:
        try:
            function(value)
        except:
            exc = False
            if not exit(*sys.exc_info()):
                raise
    finally:
        if exc:
            exit(None, None, None)

Que tal agora?

def improvize_context_manager(*args, **kwargs):

    assert (len(args) + len(kwargs)) == 1
    if args:
        context_manager = args[0]
        as_ = None
    else: # It's in kwargs
        (as_, context_manager) = kwargs.items()[0]

    def decorator(f):
        exit_ = context_manager.__exit__  # Not calling it yet
        enter_ = context_manager.__enter__()
        exc = True
        try:
            try:
                if as_:
                    f(*{as_: enter_})
                else:
                    f()
            except:
                exc = False
                if not exit_(*sys.exc_info()):
                    raise
        finally:
            if exc:
            exit_(None, None, None)
        return None
    return decorator

Uso:

@improvize_context_manager(lock)
def null():
    do(stuff)

Que é paralelo ao with palavra -chave sem as.

Ou:

@improvize_context_manager(my_lock=lock)
def null(my_lock):
    do(stuff_with, my_lock)

Que é paralelo ao with palavra -chave com o as.

Se você está bem em usar o DEF apenas para obter um bloco e os decoradores que executam imediatamente, você pode usar a assinatura da função para obter algo mais natural para o caso nomeado.

import sys
def with(func):
    def decorated(body = func):
        contexts = body.func_defaults
        try:
            exc = None, None, None
            try:
                for context in contexts:
                    context.__enter__()
                body()
            except:
                exc = sys.exc_info()
                raise
        finally:
            for context in reversed(contexts):
                context.__exit__(*exc)
    decorated()

class Context(object):
    def __enter__(self):
        print "Enter %s" % self
    def __exit__(self, *args):
        print "Exit %s(%s)" % (self, args)

x = Context()

@with
def _(it = x):
    print "Body %s" % it

@with
def _(it = x):
    print "Body before %s" % it
    raise "Nothing"
    print "Body after %s" % it

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top