1"""Utilities to enable code objects to be pickled.
2
3Any process that import this module will be able to pickle code objects. This
4includes the func_code attribute of any function. Once unpickled, new
5functions can be built using new.function(code, globals()). Eventually
6we need to automate all of this so that functions themselves can be pickled.
7
8Reference: A. Tremols, P Cogolo, "Python Cookbook," p 302-305
9"""
10
11# Copyright (c) IPython Development Team.
12# Distributed under the terms of the Modified BSD License.
13import copyreg
14import inspect
15import sys
16import types
17
18
19def code_ctor(*args):
20 return types.CodeType(*args)
21
22
23# map CodeType constructor args to code co_ attribute names
24# (they _almost_ all match, and new ones probably will)
25_code_attr_map = {
26 "codestring": "code",
27 "constants": "consts",
28}
29# pass every supported arg to the code constructor
30# this should be more forward-compatible
31# (broken on pypy: https://github.com/ipython/ipyparallel/issues/845)
32if sys.version_info >= (3, 10) and not hasattr(sys, "pypy_version_info"):
33 _code_attr_names = tuple(
34 _code_attr_map.get(name, name)
35 for name, param in inspect.signature(types.CodeType).parameters.items()
36 if param.POSITIONAL_ONLY or param.POSITIONAL_OR_KEYWORD
37 )
38else:
39 # can't inspect types.CodeType on Python < 3.10
40 _code_attr_names = [
41 "argcount",
42 "kwonlyargcount",
43 "nlocals",
44 "stacksize",
45 "flags",
46 "code",
47 "consts",
48 "names",
49 "varnames",
50 "filename",
51 "name",
52 "firstlineno",
53 "lnotab",
54 "freevars",
55 "cellvars",
56 ]
57 if hasattr(types.CodeType, "co_posonlyargcount"):
58 _code_attr_names.insert(1, "posonlyargcount")
59
60 _code_attr_names = tuple(_code_attr_names)
61
62
63def reduce_code(obj):
64 """codeobject reducer"""
65 return code_ctor, tuple(getattr(obj, f'co_{name}') for name in _code_attr_names)
66
67
68copyreg.pickle(types.CodeType, reduce_code)