Replacement improvisation for the with statement for Python 2.4

Can anyone suggest code to replace the replacement for the "c" operator, which will work in Python 2.4?

It would be a hack, but it would allow me to better port my project to Python 2.4.

EDIT: Removed an irrelevant metaclass sketch

+6
python with-statement
source share
5 answers

Just use try-finally.

Indeed, it can be as enjoyable as a mental exercise, but if you actually do it in code, you make sure that you end up with ugly, hard-to-maintain code.

+6
source share

You could (ab) use decorators for this, I think. The following works, for example:

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, 

(Well, in Python 2.4 file objects, there are no __enter__ and __exit__ methods, but otherwise they work)

The idea is that you replace the string with:

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

with the declared function "declaration" in:

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

but getting the same behavior (do_something _... executable code). Note that the decorator changes the function declaration into an immediate call, which is more than a little evil.

+4
source share

Since you need to exit the context manager as during errors, and not because of errors, I do not think that it is possible to make a common dataset with metaclasses or in general. To do this, you will need try / finally blocks.

But maybe in your case you can do something else. It depends on what you use the context manager for.

Using __del__ can help in some cases, for example, free a resource, but since you cannot be sure of its call, you can use it only to free resources that will be released when the program exits. This will also not work if you handle exceptions in the __exit__ method.

I think the cleanest method is to wrap all context management in some call control context and extract a block of code into the method. Something like this (unverified code, but mostly stolen from 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) 
+1
source share

How about this?

 def improvize_context_manager(*args, **kwargs): assert (len(args) + len(kwargs)) == 1 if args: context_manager = args[0] as_ = None else: # It 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 

Using:

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

Which is parallel to the with keyword without as .

Or:

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

Which is parallel to the with keyword with as .

+1
source share

If you're fine with def only to get the block and decorators that execute immediately, you can use the function signature to get something more natural for the named case.

  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

+1
source share

All Articles