Skip to content

Commit 0163ffa

Browse files
committed
-
1 parent d73880e commit 0163ffa

File tree

4 files changed

+67
-22
lines changed

4 files changed

+67
-22
lines changed

python_toolbox/misc_tools/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
_ascii_variable_pattern, is_legal_ascii_variable_name,
99
is_magic_variable_name, get_actual_type, is_number, identity_function,
1010
do_nothing, OwnNameDiscoveringDescriptor, find_clear_place_on_circle,
11-
general_sum, general_product
11+
general_sum, general_product, is_type
1212
)
1313
from . import name_mangling

python_toolbox/misc_tools/misc_tools.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,6 @@ def general_product(things, start=None):
254254
else:
255255
return reduce(operator.mul, things, start)
256256

257-
257+
258+
def is_type(thing):
259+
return isinstance(thing, (type, types.ClassType))

python_toolbox/monkeypatching_tools.py

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55

66
import types
77

8+
from python_toolbox import misc_tools
89
from python_toolbox import decorator_tools
910
from python_toolbox import caching
1011

1112

1213
@decorator_tools.helpful_decorator_builder
13-
def monkeypatch_method(class_, name=None):
14+
def monkeypatch_method(monkeypatchee, name=None):
1415
'''
16+
blocktododoc
1517
Monkeypatch a method into a class.
1618
1719
Example:
@@ -32,28 +34,44 @@ def my_method(a):
3234
3335
You can also use this to monkeypatch a `CachedProperty` into a class.
3436
'''
37+
38+
monkeypatchee_is_a_class = misc_tools.is_type(monkeypatchee)
39+
class_of_monkeypatchee = monkeypatchee if monkeypatchee_is_a_class else \
40+
misc_tools.get_actual_type(monkeypatchee)
41+
3542
def decorator(function):
3643
# Note that unlike most decorators, this decorator retuns the function
3744
# it was given without modifying it. It modifies the class only.
3845
if isinstance(function, types.FunctionType):
3946
name_ = name or function.__name__
40-
new_method = types.MethodType(function, None, class_)
47+
48+
new_method = types.MethodType(function, None, monkeypatchee) if \
49+
monkeypatchee_is_a_class else types.MethodType(function,
50+
monkeypatchee, class_of_monkeypatchee)
4151
# todo: Last line was: `new_method = types.MethodType(function,
4252
# class_)`, is subtly wrong, make tests to prove
43-
setattr(class_, name_, new_method)
44-
return function
45-
elif isinstance(function, caching.CachedProperty):
46-
cached_property = function
47-
if not isinstance(cached_property.getter, types.FunctionType):
48-
raise NotImplemented
49-
name_ = cached_property.getter.__name__
50-
setattr(class_, name_, cached_property)
51-
return cached_property
52-
elif isinstance(function, (classmethod, staticmethod)):
53-
name_ = function.__func__.__name__
54-
setattr(class_, name_, function)
53+
setattr(monkeypatchee, name_, new_method)
5554
return function
5655
else:
57-
raise NotImplemented("`monkeypatch_method` doesn't know how to "
58-
"handle this kind of function.")
59-
return decorator
56+
# `function` is probably some kind of descriptor.
57+
if not monkeypatchee_is_a_class:
58+
raise NotImplemented("I don't know how to monkeypatch a "
59+
"descriptor onto a non-class object.")
60+
### Getting name of descriptor: ###################################
61+
# #
62+
if isinstance(function, caching.CachedProperty):
63+
if not isinstance(function.getter, types.FunctionType):
64+
raise NotImplemented
65+
name_ = function.getter.__name__
66+
elif isinstance(function, (classmethod, staticmethod)):
67+
name_ = function.__func__.__name__
68+
else:
69+
raise NotImplemented("`monkeypatch_method` doesn't know how "
70+
"to handle this kind of function.")
71+
# #
72+
### Finished getting name of descriptor. ##########################
73+
setattr(monkeypatchee, name_, function)
74+
return function
75+
76+
return decorator
77+

test_python_toolbox/test_monkeypatching_tools.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,33 @@ def my_funky_class_method(cls):
153153
a0 = A()
154154
assert a0.my_funky_class_method() == A
155155

156-
157-
158-
156+
157+
def test_directly_on_object():
158+
159+
class A(object):
160+
def woof(self):
161+
return 'woof'
162+
163+
a0 = A()
164+
a1 = A()
165+
166+
@monkeypatching_tools.monkeypatch_method(a0)
167+
def meow(a):
168+
return 'not meow'
169+
170+
@monkeypatching_tools.monkeypatch_method(a0)
171+
def woof(a):
172+
return 'not woof'
173+
174+
assert a0.meow() == 'not meow'
175+
assert a0.woof() == 'not woof'
159176

177+
assert a1.woof() == 'woof'
178+
179+
with cute_testing.RaiseAssertor(AttributeError):
180+
A.meow()
181+
with cute_testing.RaiseAssertor(AttributeError):
182+
a1.meow()
183+
184+
assert A.woof(a0) == 'woof'
160185

0 commit comments

Comments
 (0)