forked from cool-RR/python_toolbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunction_anchoring_type.py
More file actions
67 lines (54 loc) · 2.63 KB
/
function_anchoring_type.py
File metadata and controls
67 lines (54 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# Copyright 2009-2017 Ram Rachum.
# This program is distributed under the MIT license.
'''
Defines the `FunctionAnchoringType` class.
See its documentation for more details.
'''
import sys
import types
from python_toolbox import misc_tools
class FunctionAnchoringType(type):
'''
Metaclass for working around Python's problems with pickling functions.
Python has a hard time pickling functions that are not at module level,
because when unpickling them, Python looks for them only on the module
level.
What we do in this function is create a reference to each of the class's
functions on the module level. We call this "anchoring." Note that we're
only anchoring the *functions*, not the *methods*. Methods *can* be pickled
by Python, but plain functions, like those created by `staticmethod`,
cannot.
This workaround is hacky, yes, but it seems like the best solution until
Python learns how to pickle non-module-level functions.
'''
def __new__(mcls, name, bases, namespace_dict):
my_type = super().__new__(mcls, name, bases, namespace_dict)
# We want the type's `vars`, but we want them "getted," and not in a
# `dict`, so we'll get method objects instead of plain functions.
my_getted_vars = misc_tools.getted_vars(my_type)
# Repeat after me: "Getted, not dict."
functions_to_anchor = [value for key, value in my_getted_vars.items()
if isinstance(value, types.FunctionType) and not
misc_tools.is_magic_variable_name(key)]
for function in functions_to_anchor:
module_name = function.__module__
module = sys.modules[module_name]
function_name = function.__name__
# Since this metaclass is a hacky enough solution as it is, let's
# be careful and ensure no object is already defined by the same
# name in the module level: (todotest)
try:
already_defined_object = getattr(module, function_name)
except AttributeError:
# Good, there is no object defined under our anchor address.
# This is the normal case.
setattr(module, function_name, function)
else:
# Something already exists at the anchor address; let's be
# careful.
if already_defined_object is not function:
raise Exception(
f"An object `{module_name}.{function_name}` already "
f"exists! Can't anchor function."
)
return my_type