Skip to content

Commit 4e2bcfe

Browse files
committed
-
1 parent 92b7e60 commit 4e2bcfe

File tree

6 files changed

+71
-23
lines changed

6 files changed

+71
-23
lines changed

source_py3/python_toolbox/monkeypatch_envelopes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from python_toolbox import monkeypatching_tools
1010

1111

12-
@monkeypatching_tools.monkeypatch_method(envelopes.Envelope)
12+
@monkeypatching_tools.monkeypatch(envelopes.Envelope)
1313
def add_attachment_from_string(self, file_data, file_name,
1414
mimetype='application/octet-stream'):
1515
from python_toolbox.third_party.envelopes.envelope import \

source_py3/python_toolbox/monkeypatching_tools.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@
1515

1616

1717
@decorator_tools.helpful_decorator_builder
18-
def monkeypatch_method(monkeypatchee, name=None, override_if_exists=True):
18+
def monkeypatch(monkeypatchee, name=None, override_if_exists=True):
1919
'''
20-
Monkeypatch a method into a class (or an object).
20+
Monkeypatch a method into a class (or object), or any object into module.
2121
2222
Example:
2323
2424
class A:
2525
pass
2626
27-
@monkeypatch_method(A)
27+
@monkeypatch(A)
2828
def my_method(a):
2929
return (a, 'woo!')
3030
@@ -45,8 +45,11 @@ def my_method(a):
4545

4646
def decorator(function):
4747
# Note that unlike most decorators, this decorator retuns the function
48-
# it was given without modifying it. It modifies the class only.
49-
if isinstance(function, types.FunctionType):
48+
# it was given without modifying it. It modifies the class/module only.
49+
if isinstance(monkeypatchee, types.ModuleType):
50+
name_ = name or function.__name__
51+
setattr_value = return_value = function
52+
elif isinstance(function, types.FunctionType):
5053
name_ = name or function.__name__
5154

5255
new_method = function if monkeypatchee_is_a_class else \
@@ -73,7 +76,7 @@ def decorator(function):
7376
name_ = function.fget.__name__
7477
else:
7578
raise NotImplementedError(
76-
"`monkeypatch_method` doesn't know how to get the "
79+
"`monkeypatch` doesn't know how to get the "
7780
"name of this kind of function automatically, try "
7881
"manually."
7982
)

source_py3/python_toolbox/string_tools/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33

44
'''Defines string-related tools.'''
55

6-
from .string_tools import docstring_trim, get_n_identical_edge_characters
6+
from .string_tools import (docstring_trim, get_n_identical_edge_characters,
7+
rreplace)
78
from . import case_conversions

source_py3/python_toolbox/string_tools/string_tools.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,12 @@ def get_n_identical_edge_characters(string, character=None, head=True):
6161
return len(string)
6262

6363

64+
def rreplace(s, old, new, count=None):
65+
'''
66+
Replace instances of `old` in `s` with `new`, starting from the right.
67+
68+
This function is to `str.replace` what `str.rsplit` is to `str.split`.
69+
'''
70+
return new.join(s.rsplit(old, count) if count is not None
71+
else s.rsplit(old))
72+

source_py3/test_python_toolbox/test_monkeypatching_tools/test_monkeypatch.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,20 @@ def __eq__(self, other):
2121

2222

2323
def test():
24-
'''Test basic workings of `monkeypatch_method`.'''
24+
'''Test basic workings of `monkeypatch`.'''
2525

2626
class A(EqualByIdentity):
2727
pass
2828

29-
@monkeypatching_tools.monkeypatch_method(A)
29+
@monkeypatching_tools.monkeypatch(A)
3030
def meow(a):
3131
return (a, 1)
3232

3333
a = A()
3434

3535
assert a.meow() == meow(a) == (a, 1)
3636

37-
@monkeypatching_tools.monkeypatch_method(A, 'roar')
37+
@monkeypatching_tools.monkeypatch(A, 'roar')
3838
def woof(a):
3939
return (a, 2)
4040

@@ -51,7 +51,7 @@ class A(EqualByIdentity):
5151
def booga(self):
5252
return 'Old method'
5353

54-
@monkeypatching_tools.monkeypatch_method(A, override_if_exists=False)
54+
@monkeypatching_tools.monkeypatch(A, override_if_exists=False)
5555
def meow(a):
5656
return (a, 1)
5757

@@ -60,7 +60,7 @@ def meow(a):
6060
assert a.meow() == meow(a) == (a, 1)
6161

6262

63-
@monkeypatching_tools.monkeypatch_method(A, override_if_exists=False)
63+
@monkeypatching_tools.monkeypatch(A, override_if_exists=False)
6464
def booga():
6565
raise RuntimeError('Should never be called.')
6666

@@ -75,7 +75,7 @@ def test_monkeypatch_property():
7575
class A(EqualByIdentity):
7676
pass
7777

78-
@monkeypatching_tools.monkeypatch_method(A)
78+
@monkeypatching_tools.monkeypatch(A)
7979
@property
8080
def meow(a):
8181
return (type(a), 'bark')
@@ -90,7 +90,7 @@ def test_monkeypatch_cached_property():
9090
class A(EqualByIdentity):
9191
pass
9292

93-
@monkeypatching_tools.monkeypatch_method(A)
93+
@monkeypatching_tools.monkeypatch(A)
9494
@caching.CachedProperty
9595
def meow(a):
9696
return (type(a), uuid.uuid4().hex)
@@ -110,13 +110,13 @@ def test_helpful_message_when_forgetting_parentheses():
110110
'''Test user gets a helpful exception when when forgetting parentheses.'''
111111

112112
def confusedly_forget_parentheses():
113-
@monkeypatching_tools.monkeypatch_method
113+
@monkeypatching_tools.monkeypatch
114114
def f(): pass
115115

116116
with cute_testing.RaiseAssertor(
117117
TypeError,
118118
'It seems that you forgot to add parentheses after '
119-
'`@monkeypatch_method` when decorating the `f` function.'
119+
'`@monkeypatch` when decorating the `f` function.'
120120
):
121121

122122
confusedly_forget_parentheses()
@@ -129,7 +129,7 @@ class A(EqualByIdentity):
129129
def my_static_method(x):
130130
raise 'Flow should never reach here.'
131131

132-
@monkeypatching_tools.monkeypatch_method(A)
132+
@monkeypatching_tools.monkeypatch(A)
133133
@staticmethod
134134
def my_static_method(x):
135135
return (x, 'Success')
@@ -151,7 +151,7 @@ class A(EqualByIdentity):
151151
def my_class_method(cls):
152152
raise 'Flow should never reach here.'
153153

154-
@monkeypatching_tools.monkeypatch_method(A)
154+
@monkeypatching_tools.monkeypatch(A)
155155
@classmethod
156156
def my_class_method(cls):
157157
return cls
@@ -169,7 +169,7 @@ def my_class_method(cls):
169169

170170
def test_monkeypatch_classmethod_subclass():
171171
'''
172-
Test `monkeypatch_method` on a subclass of `classmethod`.
172+
Test `monkeypatch` on a subclass of `classmethod`.
173173
174174
This is useful in Django, that uses its own `classmethod` subclass.
175175
'''
@@ -181,7 +181,7 @@ class A(EqualByIdentity):
181181
def my_funky_class_method(cls):
182182
raise 'Flow should never reach here.'
183183

184-
@monkeypatching_tools.monkeypatch_method(A)
184+
@monkeypatching_tools.monkeypatch(A)
185185
@FunkyClassMethod
186186
def my_funky_class_method(cls):
187187
return cls
@@ -206,11 +206,11 @@ def woof(self):
206206
a0 = A()
207207
a1 = A()
208208

209-
@monkeypatching_tools.monkeypatch_method(a0)
209+
@monkeypatching_tools.monkeypatch(a0)
210210
def meow(a):
211211
return 'not meow'
212212

213-
@monkeypatching_tools.monkeypatch_method(a0)
213+
@monkeypatching_tools.monkeypatch(a0)
214214
def woof(a):
215215
return 'not woof'
216216

@@ -226,3 +226,21 @@ def woof(a):
226226

227227
assert A.woof(a0) == (a0, 'woof')
228228

229+
230+
def test_monkeypatch_module():
231+
module = types.ModuleType('module')
232+
assert not hasattr(module, 'meow')
233+
@monkeypatching_tools.monkeypatch(module)
234+
def meow():
235+
return 'First meow'
236+
assert module.meow() == 'First meow'
237+
238+
@monkeypatching_tools.monkeypatch(module, override_if_exists=False)
239+
def meow():
240+
return 'Second meow'
241+
assert module.meow() == 'First meow'
242+
243+
@monkeypatching_tools.monkeypatch(module, name='woof', override_if_exists=False)
244+
def meow():
245+
return 'Third meow'
246+
assert module.woof() == 'Third meow'
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2009-2014 Ram Rachum.
2+
# This program is distributed under the MIT license.
3+
4+
from python_toolbox.string_tools import rreplace
5+
6+
7+
def test():
8+
assert rreplace('meow meow meow', 'meow', 'woof') == \
9+
rreplace('meow meow meow', 'meow', 'woof', 3) == \
10+
rreplace('meow meow meow', 'meow', 'woof', 3000) == 'woof woof woof'
11+
12+
assert rreplace('meow meow meow', 'meow', 'woof', 2) == 'meow woof woof'
13+
assert rreplace('meow meow meow', 'meow', 'woof', 1) == 'meow meow woof'
14+
assert rreplace('meow meow meow', 'meow', 'woof', 0) == 'meow meow meow'
15+
16+
assert rreplace('aaa', 'aa', 'AA') == rreplace('aaa', 'aa', 'AA', 1) == \
17+
'aAA'

0 commit comments

Comments
 (0)