Context manager, atexit processing, and PEP 3143 DaemonContext.close

Ben Finney ben+python at benfinney.id.au
Sat May 16 20:50:23 EDT 2009


Howdy all,

I'm slowly developing PEP 3143 and, in parallel, its reference
implementation, the ‘python-daemon’ library
<URL:http://pypi.python.org/pypi/python-daemon/>. Feedback continues to
be welcome from people wanting to make a program become a daemon; please
try it out and critique the specification and/or the implementation.

Today a flaw has been uncovered by Pavol Babinčák. In diagnosing an
issue, we doscovered that PEP 3143 currently leads, in some common
circumstances, to exiting a context manager twice.

The current specification of the `DaemonContext` class says:

=====
close()
    Return:     None

    Close the daemon context. This performs the following step:

        * If the pidfile attribute is not None, exit its context
          manager.
=====

and:

=====
__exit__(exc_type, exc_value, exc_traceback)
    Return:        True or False as defined by the context manager
    protocol

    Call the instance's close() method, then return True if the
    exception was handled or False if it was not.
=====

but also:

=====
open()
    Return:     None

    Open the daemon context, turning the current program into a daemon
    process. This performs the following steps:
[…]

        * Register the close method to be called during Python's exit
          processing.
=====


This leads to the situation where the `DaemonContext.close` method can
be called twice:

    pidfile = FunkyLockFile()
    daemon_context = daemon.DaemonContext(pidfile=pidfile)
    with daemon_context:
        main_processing()

According to the current specification, this will cause
`daemon_context.close()` to be called on exiting its context manager (at
the end of the `with` suite) *and* on program exit (via `atexit`
processing).

This will cause the `pidfile` context manager to be exited twice in
succession, which surely can't be good and is at least undefined AFAICT.


How should a context manager be implemented to handle situations like
this? One possible way is to raise an exception when trying to close a
not-open context manager. Another is to return immediately if the
attempt is made, making it safe to try closing a context manager
multiple times.

The “Register the close method to be called during Python's exit
processing” is there to ensure clean-up occurs even if the program
isn't using the DaemonContext as a context manager. Ideally, what I'd
like to do is *un*-register the `close` method after having already
closed it, so that exit processing *won't* call it. The `atexit` module,
though, doesn't appear to give me that option.

Ideas? How should this be addressed both Pythonically and respecting the
intent of PEP 3143?

-- 
 \       “For fast acting relief, try slowing down.” —Jane Wagner, via |
  `\                                                       Lily Tomlin |
_o__)                                                                  |
Ben Finney



More information about the Python-list mailing list