44'''Tools for monkeypatching.'''
55
66import sys
7+ import collections
8+ import inspect
79import types
810
11+ from python_toolbox .third_party import funcsigs
12+
913from python_toolbox import misc_tools
14+ from python_toolbox import dict_tools
1015from python_toolbox import decorator_tools
1116from python_toolbox import caching
1217
1318
1419@decorator_tools .helpful_decorator_builder
15- def monkeypatch_method (monkeypatchee , name = None ):
20+ def monkeypatch_method (monkeypatchee , name = None , override_if_exists = True ):
1621 '''
1722 Monkeypatch a method into a class (or an object).
1823
@@ -49,8 +54,8 @@ def decorator(function):
4954 new_method = types .MethodType (function , None , monkeypatchee ) if \
5055 monkeypatchee_is_a_class else types .MethodType (function ,
5156 monkeypatchee , class_of_monkeypatchee )
52- setattr ( monkeypatchee , name_ , new_method )
53- return function
57+ setattr_value = new_method
58+ return_value = function
5459 else :
5560 # `function` is probably some kind of descriptor.
5661 if not monkeypatchee_is_a_class :
@@ -77,8 +82,57 @@ def decorator(function):
7782 )
7883 # #
7984 ### Finished getting name of descriptor. ######################
80- setattr (monkeypatchee , name_ , function )
81- return function
85+ setattr_value = return_value = function
86+
87+ if override_if_exists or not hasattr (monkeypatchee , name_ ):
88+ setattr (monkeypatchee , name_ , setattr_value )
89+ return return_value
8290
8391 return decorator
8492
93+
94+ def change_defaults (function = None , new_defaults = {}):
95+ '''
96+ Change default values of a function.
97+
98+ Include the new defaults in a dict `new_defaults`, with each key being a
99+ keyword name and each value being the new default value.
100+
101+ Note: This changes the actual function!
102+
103+ Can be used both as a straight function and as a decorater to a function to
104+ be changed.
105+ '''
106+ def change_defaults_ (function_ , new_defaults_ ):
107+ signature = funcsigs .Signature .from_function (function_ )
108+ defaults = list (function_ .__defaults__ or ())
109+ non_keyword_only_defaultful_parameters = defaultful_parameters = \
110+ dict_tools .filter_items (
111+ signature .parameters ,
112+ lambda name , parameter : parameter .default != funcsigs ._empty ,
113+ force_dict_type = collections .OrderedDict
114+ )
115+
116+ for i , parameter_name in \
117+ enumerate (non_keyword_only_defaultful_parameters ):
118+ if parameter_name in new_defaults_ :
119+ defaults [i ] = new_defaults_ [parameter_name ]
120+
121+ function_ .__defaults__ = tuple (defaults )
122+
123+ return function_
124+
125+ if not callable (function ):
126+ # Decorator mode:
127+ if function is None :
128+ actual_new_defaults = new_defaults
129+ else :
130+ actual_new_defaults = function
131+ return lambda function_ : change_defaults_ (function_ ,
132+ actual_new_defaults )
133+ else :
134+ # Normal usage mode:
135+ return change_defaults_ (function , new_defaults )
136+
137+
138+
0 commit comments