[docs] [issue17947] Code, test, and doc review for PEP-0435 Enum
Guido van Rossum
report at bugs.python.org
Mon May 13 20:32:50 CEST 2013
Guido van Rossum added the comment:
Here's the promised explanation why I want to keep the getframe hack. I'm sure it won't satisfy everyone, but this will have to do.
There are two parts to my argument. TL;DR: (a) by implementing this hack, we will maximize user happiness; (b) I expect that all Python implementations can provide the functionality needed.
So why does this maximize user happiness? First of all, enums are such simple objects that it's a disgrace to have an enum that isn't picklable. But most users are lazy and lack imagination, so if they find they have to do extra work to ensure that something is picklable, they will make a judgement call -- is the extra work to make it picklable worth it? Unfortunately they will try to make this judgment call in a fraction of a second, and they limited imagination they may well say "I can't imagine ever pickling this", save themselves the work, and move on. If you think more than a second about the decision, you've wasted more time than it takes to type "module=__name__", so it's going to be a split-second decision. But nevertheless, having to think about it and typing it is a distraction, and when you're "in the zone" you may prefer to spend your time thinking about the perfect names for your enum class and values rather than the red tape of making in picklable.
So, in my mind, it's a given that there will be many enums that are missing the module=__name__ keyword argument. Moreover, it's likely that most of the time this will be fine -- you can get through most days just fine without ever reading or writing a pickle. (For example, I very much doubt that I've ever pickled one of the flag values used by the socket module to define e.g. the address family, socket type, or protocol.)
But now you enter a different phase of your project, or one of your collaborators does, or perhaps you've released your code on PyPI and one of your users does. So someone tries to pickle some class instance that happens to contain an unpicklable enum. That's not a great experience. Pickling and unpickling errors are often remarkably hard to debug. (Especially the latter, so I have privately admonished Ethan to ensure that if the getframe hack doesn't work, the pickle failure should happen at pickling time, not at unpickle time.) Once you've tracked down the source, you have to figure out the fix -- hopefully just typing the error message into Google will link back to a StackOverflow answer explaining the need to say "module=__name__". But the damage is done, especially if the person encountering the pickling error is not the original author of the code defining the enum. (Often the person downloading and using a package from PyPI has less advanced Python knowledge than the package author, so they may have a hard time debugging the situation.)
You can see how having the getframe hack in place makes life more pleasant for many people -- the package user won't have to debug the pickling problem, and the package author won't have to deal with the bug report and fix.
But what about other Python implementations? Well, TBH, they have plenty of other issues. The reality is that you can't take a large package that hasn't been tested on Jython, or IronPython, or PyPy, and expect it to just work on any of those. Sure, things are getting better. But there are still tons of differences between the various Python implementations (as there are between different versions of CPython), and whether you like it or not, CPython is still the Python version of choice for most people. The long and short of it is that porting any significant package to another implementation is a bit of work, and keeping the port working probably requires adding some set of additional line items to the style guide used by its developers -- don't use feature X, don't depend on behavior Y, always use pattern Z...
However, I don't expect that "always pass module=__name__ when using the enum convenience API" won't have to be added to that list. sys._getframe() is way more powerful than what's needed. Even on platforms where sys._getframe() is unimplementable (or disabled by default in favor of a 10% speedup), it should still be possible to provide an API that *just* gets the module name of the caller, at least in case the call site is top-level module code (and for anything else, the getframe hack doesn't work anyway). After all, we're talking about a full Python implementation, right? That's a dynamic language with lots of introspection APIs, and any implementation worth its salt will have to deal with that, even if full-fledged sys._getframe() is explicitly excluded.
So I propose that we use sys._getframe() for the time being, and the authors of Jython, IronPython and PyPy can get together and figure out how to implement something like sys.get_calling_module_name(). They have plenty of time -- at least until they pledge support for Python 3.4. To make it easy for them we should probably add that API to CPython 3.4. And it's fine with me if the function only works if the caller is top-level code in a module.
Which reminds me. Nick offered another use case where using sys._getframe() breaks down: a wrapper function that constructs an enum for its caller. First of all, I think this is a pretty rare use case. But additionally, that wrapper could just use sys.get_calling_module_name(), and everything would be fine.
PS. Whoever adds sys.get_calling_module_name() to CPython, please pick a shorter name. :-)
Python tracker <report at bugs.python.org>
More information about the docs