7 votes

Improvisation d'un remplacement immédiat de l'instruction "with" pour Python 2.4

Pouvez-vous suggérer un moyen de coder un remplacement immédiat de l'instruction "with" qui fonctionnera dans Python 2.4 ?

Il s'agirait d'un hack, mais cela me permettrait de porter mon projet vers Python 2.4 de manière plus agréable.

EDIT : Suppression de l'esquisse de métaclasse non pertinente

7voto

itsadok Points 12971

Utilisez simplement try-finally.

Vraiment, cela peut être agréable comme exercice mental, mais si vous le faites réellement dans un code auquel vous tenez, vous vous retrouverez avec un code laid et difficile à maintenir.

4voto

Anthony Towns Points 1748

Vous pourriez (ab)utiliser des décorateurs pour faire cela, je pense. Ce qui suit fonctionne, par exemple :

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,

(Bon, dans Python 2.4 les objets fichiers n'ont pas de méthodes __enter__ et __exit__, mais sinon ça marche)

L'idée est que vous remplacez la ligne avec dans :

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

avec la fonction décorée "déclaration" dans :

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

mais on obtient le même comportement (le code do_something_... exécuté). Notez que le décorateur change la déclaration de la fonction en une fonction recours immédiat ce qui est plus qu'un peu maléfique.

1voto

Lennart Regebro Points 52510

Puisque vous avez besoin de quitter le gestionnaire de contexte à la fois pendant les erreurs et les non-erreurs, je ne pense pas qu'il soit possible de faire un cas d'utilisation générique avec des métaclasses, ou en fait pas du tout. Vous allez avoir besoin de blocs try/finally pour cela.

Mais il est peut-être possible de faire autre chose dans votre cas. Cela dépend de l'usage que vous faites du gestionnaire de contexte.

Utilisation de __del__ peut aider dans certains cas, comme la désallocation de ressources, mais comme vous ne pouvez pas être sûr qu'elle sera appelée, elle ne peut être utilisée que si vous avez besoin de libérer des ressources qui seront libérées à la fin du programme. Cela ne fonctionnera pas non plus si vous gérez des exceptions dans la fonction __exit__ méthode.

Je pense que la méthode la plus propre est d'envelopper toute la gestion du contexte dans une sorte d'appel de gestion du contexte, et d'extraire le bloc de code dans une méthode. Quelque chose comme ceci (code non testé, mais principalement volé au 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)

1voto

Ram Rachum Points 10233

Que pensez-vous de ça ?

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

Utilisation :

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

Ce qui correspond à la with mot-clé sans as .

O:

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

Ce qui correspond à la with avec le mot-clé as .

1voto

rwms Points 155

Si vous êtes d'accord pour utiliser def juste pour obtenir un bloc, et des décorateurs qui s'exécutent immédiatement, vous pouvez utiliser la signature de la fonction pour obtenir quelque chose de plus naturel pour le cas nommé.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X