[Python-il] What do you think about my `ContextManager`?

cool-RR cool-rr at cool-rr.com
Thu Jan 6 01:33:00 IST 2011


Thanks for the critique Tal.

I updated the `context_manager` module in response, here is the new version:

https://github.com/cool-RR/GarlicSim/blob/development/garlicsim/garlicsim/general_misc/context_manager.py

(Note that this is a link to my `development` branch, so it will get updated
as I continue working on it.)

In Thu, Jan 6, 2011 at 12:36 AM, Tal Einat <taleinat at gmail.com> wrote:

> How about something like: "A class for writing enhanced context managers.
> Allows using context managers as decorators, a new succinct way to define
> context managers, and some other goodies."
>

Here's the new description I came up, tell me if you think it's good enough:


Defines the `ContextManager` and `ContextManagerType` classes.

These classes allow for greater freedom both when (a) defining context
managers
and when (b) using them. It allows defining context managers either by (1)
using the classic `__enter__` and `__exit__` interface, or (2) using a
stand-alone generator function or (3) using a class that defines a
`manage_context` generator function. In addition, the `ContextManager` class
allows using a context manager as a decorator to a function, which for some
cases is a better alternative than using the `with` keyword.





> I must say, the actual benefits are really unclear. How is using
> @MyContextManager() better than contextlib.ContextDecorator (the latter
> being more explicit)?
>

How is `ContextDecorator` more explicit? The place where you write
"ContextDecorator" is where you *define* you context manager, not where you
*use* it. So when you use your context manager as a decorator using
Foord's `ContextDecorator`, there is no reference to `ContextDecorator`, so
I don't see how it's more explicit.


> How is using the "manage_context()" method better than just defining a
> context manager with a function and contextlib.contextmanager?
>
> - Tal
>

There are two cases that I can think of where it's better. In the version of
`context_manager.py` you saw before I gave an example of the first, and in
the new version that I linked to above I also explain the second.

The first case where this is helpful is when you want a context manager
which calls another context manager. For example, this code:

class MyContextManager(ContextManager):
    def manage_context(self):
        with some_lock:
            yield self


Is much nicer in my opinion than this code, which is its equivalent:

class MyContextManager(ContextManager):
    def __enter__(self):
        some_lock.__enter__()
        return self
    def __exit__(self, *exc):
        return some_lock.__exit__(*exc)


As for the second case, I'll quote the new version of the docstring:

Another advantage of this approach over `__enter__` and `__exit__` is that
it's better at handling exceptions, since any exceptions would be raised
inside `manage_context` where we could `except` them, which is much more
idiomatic than the way `__exit__` handles exceptions, which is by
receiving their type and returning whether to swallow them or not.


I hope this is written clearly enough; Possibly an example for this case
should be included in the docstring. I'm not sure though.

I'd appreciate any opinions and more critiques.



>
> On Wed, Jan 5, 2011 at 5:13 PM, cool-RR <cool-rr at cool-rr.com> wrote:
>
>> Yes, I agree that's kind of an opaque description. But I haven't been able
>> to come up with a description which (a) explains enough and (b)
>> is succinct enough. Do you have a suggestion?
>>
>> Also, I think you should be sending critiques on the list; Other people
>> may have interesting opinions.
>>
>>
>> On Wed, Jan 5, 2011 at 9:29 AM, Tal Einat <taleinat at gmail.com> wrote:
>>
>>> "Allows greater freedom" isn't much of a description. I had to read
>>> through the entire docstring to understand what it does. You should make it
>>> very clear what new functionality this supplies compared to the basic stuff.
>>>
>>> On Tue, Jan 4, 2011 at 7:55 PM, cool-RR <cool-rr at cool-rr.com> wrote:
>>>
>>>> Hello folks.
>>>>
>>>> Ever since Michael Foord talked about `ContextDecorator` in python-ideas
>>>> I've been kicking around an idea for my own take on it. It's a
>>>> `ContextManager` class which provides the same thing that Foord's
>>>> `ContextDecorator` does, but also provides a few more goodies, chief of
>>>> which being the `manage_context` method.
>>>>
>>>> I've been working on this for a few days and I think it's ready for
>>>> review. It's well-tested and extensively documented. I started using it
>>>> wherever I have context managers in GarlicSim.
>>>>
>>>> I'll be happy to get your opinions on my approach and any critiques you
>>>> may have. If there are no problems with this approach, I'll probably release
>>>> it with GarlicSim 0.6.1 and blog about it.
>>>>
>>>> Here is my `context_manager` module<https://github.com/cool-RR/GarlicSim/blob/first_context_manager_review/garlicsim/garlicsim/general_misc/context_manager.py>.
>>>> Here are its tests<https://github.com/cool-RR/GarlicSim/tree/first_context_manager_review/garlicsim/test_garlicsim/test_general_misc/test_context_manager>
>>>> .
>>>>
>>>> Following is the module's docstring which explains the module in more
>>>> detail.
>>>>
>>>> Ram.
>>>>
>>>> Defines the `ContextManager` and `ContextManagerType` classes.
>>>>
>>>> These classes allow for greater freedom both when (a) defining context
>>>> managers
>>>> and when (b) using them.
>>>>
>>>> Inherit all your context managers from `ContextManager` (or decorate
>>>> your
>>>> generator functions with `ContextManagerType`) to enjoy all the benefits
>>>> described below.
>>>>
>>>>
>>>> Defining context managers
>>>> -------------------------
>>>>
>>>> There are 3 different ways in which context managers can be defined, and
>>>> each
>>>> has their own advantages and disadvantages over the others.
>>>>
>>>>  1. The classic way to define a context manager is to define a class
>>>> with
>>>>     `__enter__` and `__exit__` methods. This is allowed, and if you do
>>>> this
>>>>     you should still inherit from `ContextManager`. Example:
>>>>
>>>>         class MyContextManager(ContextManager):
>>>>             def __enter__(self):
>>>>                 pass # preparation
>>>>             def __exit__(self, type_=None, value=None, traceback=None):
>>>>                 pass # cleanup
>>>>
>>>>  2. As a decorated generator, like so:
>>>>
>>>>         @ContextManagerType
>>>>          def MyContextManager():
>>>>             try:
>>>>                 yield
>>>>             finally:
>>>>                 pass # clean-up
>>>>
>>>>     This usage is nothing new; It's also available when using the
>>>> standard
>>>>     library's `contextlib.contextmanager` decorator. One thing that is
>>>> allowed
>>>>     here that `contextlib` doesn't allow is to yield the context manager
>>>> itself
>>>>     by doing `yield SelfHook`.
>>>>
>>>>  3. The third and novel way is by defining a class with a
>>>> `manage_context`
>>>>     method which returns a decorator. Example:
>>>>
>>>>         class MyContextManager(ContextManager):
>>>>             def manage_context(self):
>>>>                 do_some_preparation()
>>>>                 try:
>>>>                     with some_lock:
>>>>                         yield self
>>>>                  finally:
>>>>                     do_some_cleanup()
>>>>
>>>>     This approach is sometimes cleaner than defining `__enter__` and
>>>>     `__exit__`; Especially when using another context manager inside
>>>>     `manage_context`. In our example we did `with some_lock` in our
>>>>     `manage_context`, which is shorter and more idiomatic than calling
>>>>     `some_lock.__enter__` in an `__enter__` method and
>>>> `some_lock.__exit__` in
>>>>     an `__exit__` method.
>>>>
>>>>
>>>> These were the different ways of *defining* a context manager. Now let's
>>>> see
>>>> the different ways of *using* a context manager:
>>>>
>>>>
>>>> Using context managers
>>>> ----------------------
>>>>
>>>> There are 2 different ways in which context managers can be used:
>>>>
>>>>  1. The plain old honest-to-Guido `with` keyword:
>>>>
>>>>        with MyContextManager() as my_context_manager:
>>>>            do_stuff()
>>>>
>>>>  2. As a decorator to a function
>>>>
>>>>         @MyContextManager()
>>>>         def do_stuff():
>>>>            pass # doing stuff
>>>>
>>>>     When the `do_stuff` function will be called, the context manager
>>>> will be
>>>>     used. This functionality is also available in the standard library
>>>> of
>>>>     Python 3.2+ by using `contextlib.ContextDecorator`, but here it is
>>>> combined
>>>>     with all the other goodies given by `ContextManager`.
>>>>
>>>>
>>>> That's it. Inherit all your context managers from `ContextManager` (or
>>>> decorate
>>>> your generator functions with `ContextManagerType`) to enjoy all these
>>>> benefits.
>>>>
>>>>
>>>> --
>>>> Sincerely,
>>>> Ram Rachum
>>>>
>>>> _______________________________________________
>>>> Python-il mailing list
>>>> Python-il at hamakor.org.il
>>>> http://hamakor.org.il/cgi-bin/mailman/listinfo/python-il
>>>>
>>>>
>>>
>>
>>
>> --
>> Sincerely,
>> Ram Rachum
>>
>
>


-- 
Sincerely,
Ram Rachum
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://hamakor.org.il/pipermail/python-il/attachments/20110106/09d95a58/attachment.htm>


More information about the Python-il mailing list