bpo-28869: Set class module to caller module in ABCMeta.new by alegonz · Pull Request #14126 · python/cpython (original) (raw)
Hello, this is a PR to the bug reported in bpo-28869 (https://bugs.python.org/issue28869).
Issues:
__module__
attribute is set differently depending on whether a metaclass is explicitly called or invoked in a class statement.- This causes a pickling error when dumping an instance of a class that was defined by calling
ABCMeta
directly, because it attempts to look for the class inabc
rather than the module the class was defined in.
Solution:
If the __module__
variable is not found in the namespace variable, inspect and use the module of the caller. This follows the same pattern used in namedtuple
. Values set via __init_subclass__
take precedence.
There were concerns that inspection might slow down further ABCs creation, so I also did some benchmarking and got these results:
C implementation
Absolute difference: 0.00115500006359 ms
Slowdown: 4.61538495086 %
Py implementation
Absolute difference: 0.00129000000015 ms
Slowdown: 3.67301613313 %
Notes:
- The values reported above are the median execution times measured using
timeit.repeat
with 50,000 repetitions. - I used
ABCMeta('ABC', (), {'__module__': __name__})
as a proxy for the original implementation. - Script was run on a laptop with Intel Core i7-3537U CPU @ 2.00GHz, 8GB RAM, running Ubuntu 16.04.
Is 3~5% considered a significant drop in performance?
I temporarily commited the script for benchmarking in the root directory for sharing purposes. This will be removed in a future commit.
Also, following the discussion in the bpo thread regarding the pickle documentation, since this issue is not limited to ABCMeta
, I propose to add the following paragraph (highlighted in bold) in the "What can be pickled and unpickled?" section:
...
Note that functions (built-in and user-defined) are pickled by "fully qualified"
name reference, not by value. [2] This means that only the function name is
pickled, along with the name of the module the function is defined in. Neither
the function's code, nor any of its function attributes are pickled. Thus the
defining module must be importable in the unpickling environment, and the module
must contain the named object, otherwise an exception will be raised. [3]When pickling instances of classes defined dynamically (i.e. classes defined by calling
type()
(or any other metaclass) directly) you might need to set the__module__
attribute of the class to the module where the class was defined in. This is the case when the class creation and the call totype()
reside in different modules.
Would this be an appropriate explanation?