Rename ContextStack to ExitStack and adjust method names

Issue #8 resolved
Nick Coghlan
repo owner created an issue
  • register() -> callback()
  • register_exit() -> push()
  • preserve() -> pop_all()

Comments (12)

  1. Nick Coghlan reporter

    Better idea:

    • Rename ContextStack to CallbackStack
    • Rename register() to push()
    • Rename register_exit() to push_exit()
    • Leave enter_context() method alone (since the "_context" part is no longer redundant w.r.t. the class name)
  2. Brandon Rhodes

    I think that the name enter() still winds up being unambiguous because contexts are the only things we "enter" in Python-land, so someone seeing .enter(something) will assume that the something is a context per usual terminology.

    The name CallbackStack does not quite catch for me that this is not a generic stack of callbacks, that I can invoke over and over again whenever I need to send an I/O event or GUI click to a stack of listeners — which is what people tend to immediately think of when they hear about a "callback" — but that this is a very specific kind of collection of callables that will be triggered when the with statement that instantiated the stack is finally complete. So ContextStack is, as we are observing, not a terribly good name for capturing what is "inside" the stack, because normal callbacks / callables can wind up inside the stack ("on" the stack?) alongside fancier things like context exit functions. But ContextStack is not such a terrible name if instead of reading it as a description of the stack's content ("this is a stack of contexts", well, no, it's not), you think of it as an outward-facing name that describes how the stack as a whole is used ("this stack ought to be used as a context" — well, yes, that hits the nail on the head, rather).

    The name preserve() sounds like it should pickle the callbacks or something so that they can be saved to disk or something. :) If the meaning is simply that they are given to another stack, then defer() or handoff() or shunt() might be more descriptive of the operation that is taking place. Even defuse() would be somewhat clearer, while being a quite fun idea — that one is "removing the trigger" that will otherwise call all of the exit functions.

  3. Nick Coghlan reporter

    The thing is, until you use it as a context manager, a CallbackStack *is* just a stack of callbacks. How you invoke those callbacks is up to the user. (I'm even considering adding a public pop() method to make that capability completely explicit). I think you got it exactly right when you suggested that, if something is called an "<X>Stack", a reasonable user's brain is going to expect to be able to write "stack.push(<example of X>)". In this case, push() accepts callbacks, so the object we're defining is really a callback stack.

    Using the callback stack as a context manager then just becomes a clearly *useful* way to guarantee prompt cleanup of a set of resources.

    The rationale behind the preserve() name is that I wanted to make it clear that it was an operation that cleared the callback stack *without* invoking any of the callbacks. Other options I thought of were either too obscure, or too suggestive of performing cleanup work (such as release()).

    Perhaps the answer is to offer a pop() API, producing the most recently added callback (without invoking it) and a pop_all() API (which hands everything off to a new CallbackStack and returns that - i.e. a new, less ambiguous, name for preserve()).

    I can at least agree with you on enter() being sufficiently unambiguous without the _context() trailer, though :)

  4. Brandon Rhodes

    Ah! I had not caught on to how general you are making the idea, probably because of the overarching contextlib2 name. Something like a callback-stack almost would seem to belong in functools instead of in something related to contextlib — you are essentially creating a way to compose n functions so that they are callable as a single function. But people will probably look for it here, mostly to use inside of with statements.

    I am not sure what the use case of pop() would be. Symmetry? :) But, symmetry is important!

    Glad that I at least succeeded in removing eight characters from a function call that I think I will be using a lot in the future — thanks for having the idea of adding this class to Python!

  5. Nick Coghlan reporter

    Note that, for backwards compatibility purposes, ContextStack name will still be available - it will just be a CallbackStack subclass that also adds the deprecated method names back in.

  6. Nick Coghlan reporter

    Changed the proposed names to reflect the fact that this is specifically a stack of *exit* methods. There are then 3 ways to add new exit methods:

    push() - adds a bound exit method or an exit function directly. It will be called with (exc_type, exc_value, exc_traceback) and can suppress exceptions like any exit method

    callback() - adds a callback function that will be called with (*args, kwds). It is wrapped in an exit function and cannot suppress exceptions

    enter_context() - reverts to original name. The reason is that the bare enter() name is ambiguous with respect to the enter() method of the exit stack itself.

  7. Log in to comment