Skip to content

Commit df291d9

Browse files
committed
-
1 parent c4a6faf commit df291d9

File tree

14 files changed

+228
-62
lines changed

14 files changed

+228
-62
lines changed

source_py2/python_toolbox/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import python_toolbox.version_info
1616
import python_toolbox.monkeypatch_copy_reg
1717
import python_toolbox.monkeypatch_envelopes
18+
import python_toolbox.monkeypatch_pathlib
1819

19-
__version_info__ = python_toolbox.version_info.VersionInfo(0, 6, 7)
20+
__version_info__ = python_toolbox.version_info.VersionInfo(0, 6, 8)
2021
__version__ = __version_info__.version_text
2122

source_py2/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 \
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright 2009-2014 Ram Rachum.
2+
# This program is distributed under the MIT license.
3+
4+
from python_toolbox import monkeypatching_tools
5+
6+
try:
7+
import pathlib
8+
9+
except ImportError:
10+
pass
11+
12+
else:
13+
14+
@monkeypatching_tools.monkeypatch(pathlib.Path, override_if_exists=False)
15+
def read_bytes(self):
16+
"""
17+
Open the file in bytes mode, read it, and close the file.
18+
"""
19+
with self.open(mode='rb') as f:
20+
return f.read()
21+
22+
@monkeypatching_tools.monkeypatch(pathlib.Path, override_if_exists=False)
23+
def read_text(self, encoding=None, errors=None, newline=None):
24+
"""
25+
Open the file in text mode, read it, and close the file.
26+
"""
27+
with self.open(mode='r', encoding=encoding,
28+
errors=errors, newline=newline) as f:
29+
return f.read()
30+
31+
@monkeypatching_tools.monkeypatch(pathlib.Path, override_if_exists=False)
32+
def write_bytes(self, data, append=False, exclusive=False):
33+
"""
34+
Open the file in bytes mode, write to it, and close the file.
35+
"""
36+
if append and exclusive:
37+
raise TypeError('write_bytes does not accept both '
38+
'"append" and "exclusive" mode.')
39+
mode = 'ab' if append else 'xb' if exclusive else 'wb'
40+
with self.open(mode=mode) as f:
41+
return f.write(data)
42+
43+
@monkeypatching_tools.monkeypatch(pathlib.Path, override_if_exists=False)
44+
def write_text(self, data, encoding=None, errors=None,
45+
newline=None, append=False, exclusive=False):
46+
"""
47+
Open the file in text mode, write to it, and close the file.
48+
"""
49+
if append and exclusive:
50+
raise TypeError('write_text does not accept both '
51+
'"append" and "exclusive" mode.')
52+
mode = 'a' if append else 'x' if exclusive else 'w'
53+
with self.open(mode=mode, encoding=encoding,
54+
errors=errors, newline=newline) as f:
55+
return f.write(data)
56+

source_py2/python_toolbox/monkeypatching_tools.py

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

1818

1919
@decorator_tools.helpful_decorator_builder
20-
def monkeypatch_method(monkeypatchee, name=None, override_if_exists=True):
20+
def monkeypatch(monkeypatchee, name=None, override_if_exists=True):
2121
'''
22-
Monkeypatch a method into a class (or an object).
22+
Monkeypatch a method into a class (or object), or any object into module.
2323
2424
Example:
2525
2626
class A(object):
2727
pass
2828
29-
@monkeypatch_method(A)
29+
@monkeypatch(A)
3030
def my_method(a):
3131
return (a, 'woo!')
3232
@@ -47,8 +47,11 @@ def my_method(a):
4747

4848
def decorator(function):
4949
# Note that unlike most decorators, this decorator retuns the function
50-
# it was given without modifying it. It modifies the class only.
51-
if isinstance(function, types.FunctionType):
50+
# it was given without modifying it. It modifies the class/module only.
51+
if isinstance(monkeypatchee, types.ModuleType):
52+
name_ = name or function.__name__
53+
setattr_value = return_value = function
54+
elif isinstance(function, types.FunctionType):
5255
name_ = name or function.__name__
5356

5457
new_method = types.MethodType(function, None, monkeypatchee) if \
@@ -76,7 +79,7 @@ def decorator(function):
7679
name_ = function.fget.__name__
7780
else:
7881
raise NotImplementedError(
79-
"`monkeypatch_method` doesn't know how to get the "
82+
"`monkeypatch` doesn't know how to get the "
8083
"name of this kind of function automatically, try "
8184
"manually."
8285
)

source_py2/python_toolbox/string_tools/__init__.py

Lines changed: 3 additions & 2 deletions
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
7-
from . import conversions
6+
from .string_tools import (docstring_trim, get_n_identical_edge_characters,
7+
rreplace)
8+
from . import case_conversions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright 2009-2014 Ram Rachum.
2+
# This program is distributed under the MIT license.
3+
4+
'''Defines functions for converting between different string conventions.'''
5+
6+
import sys
7+
import re
8+
9+
10+
def camelcase_to_spacecase(s):
11+
'''
12+
Convert a string from camelcase to spacecase.
13+
14+
Example: camelcase_to_underscore('HelloWorld') == 'Hello world'
15+
'''
16+
if s == '': return s
17+
process_character = lambda c: (' ' + c.lower()) if c.isupper() else c
18+
return s[0] + ''.join(process_character(c) for c in s[1:])
19+
20+
21+
def camel_case_to_lower_case(s):
22+
'''
23+
Convert a string from camel-case to lower-case.
24+
25+
Example:
26+
27+
camel_case_to_lower_case('HelloWorld') == 'hello_world'
28+
29+
'''
30+
return re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', '_\\1', s). \
31+
lower().strip('_')
32+
33+
34+
def lower_case_to_camel_case(s):
35+
'''
36+
Convert a string from lower-case to camel-case.
37+
38+
Example:
39+
40+
camel_case_to_lower_case('hello_world') == 'HelloWorld'
41+
42+
'''
43+
s = s.capitalize()
44+
while '_' in s:
45+
head, tail = s.split('_', 1)
46+
s = head + tail.capitalize()
47+
return s
48+
49+
50+
def camel_case_to_upper_case(s):
51+
'''
52+
Convert a string from camel-case to upper-case.
53+
54+
Example:
55+
56+
camel_case_to_lower_case('HelloWorld') == 'HELLO_WORLD'
57+
58+
'''
59+
return camel_case_to_lower_case(s).upper()
60+
61+
62+
def upper_case_to_camel_case(s):
63+
'''
64+
Convert a string from upper-case to camel-case.
65+
66+
Example:
67+
68+
camel_case_to_lower_case('HELLO_WORLD') == 'HelloWorld'
69+
70+
'''
71+
return lower_case_to_camel_case(s.lower())

source_py2/python_toolbox/string_tools/conversions.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

source_py2/python_toolbox/string_tools/string_tools.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,14 @@ def get_n_identical_edge_characters(string, character=None, head=True):
5858
if c != character:
5959
return i
6060
else:
61-
return len(string)
61+
return len(string)
62+
63+
def rreplace(s, old, new, count=None):
64+
'''
65+
Replace instances of `old` in `s` with `new`, starting from the right.
66+
67+
This function is to `str.replace` what `str.rsplit` is to `str.split`.
68+
'''
69+
return new.join(s.rsplit(old, count) if count is not None
70+
else s.rsplit(old))
71+

source_py2/test_python_toolbox/test_address_tools/test_describe.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ def test_on_locally_defined_class():
2727
# Testing for locally defined class:
2828

2929

30-
if python_toolbox.__version_info__ <= (0, 6, 7, 'release'):
30+
if python_toolbox.__version_info__ <= (0, 6, 8, 'release'):
3131
raise nose.SkipTest("This test doesn't pass in `python_toolbox` "
32-
"version 0.6.7 and below, because `describe` :"
32+
"version 0.6.8 and below, because `describe` :"
3333
"doesn't support nested classes yet.")
3434

3535
result = describe(A.B)
@@ -247,9 +247,9 @@ def test_bad_module_name():
247247

248248
def test_function_in_something():
249249
'''Test `describe` doesn't fail when describing `{1: sum}`.'''
250-
if python_toolbox.__version_info__ <= (0, 6, 7, 'release'):
250+
if python_toolbox.__version_info__ <= (0, 6, 8, 'release'):
251251
raise nose.SkipTest("This test doesn't pass in `python_toolbox`"
252-
"version 0.6.7 and below.")
252+
"version 0.6.8 and below.")
253253
assert describe({1: sum}) == '{1: sum}'
254254
assert describe((sum, sum, list, chr)) == '(sum, sum, list, chr)'
255255

source_py2/test_python_toolbox/test_context_management/test_external.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def woohoo():
8585
self.assertEqual(state, [1, 42, 999])
8686

8787
def _create_contextmanager_attribs(self):
88-
if python_toolbox.__version_info__ <= (0, 6, 7, 'release'):
88+
if python_toolbox.__version_info__ <= (0, 6, 8, 'release'):
8989
raise nose.SkipTest
9090
def attribs(**kw):
9191
def decorate(func):
@@ -107,7 +107,7 @@ def test_contextmanager_attribs(self):
107107
@unittest2.skipIf(hasattr(sys, 'flags') and sys.flags.optimize >= 2,
108108
"Docstrings are omitted with -O2 and above")
109109
def test_contextmanager_doc_attrib(self):
110-
if python_toolbox.__version_info__ <= (0, 6, 7, 'release'):
110+
if python_toolbox.__version_info__ <= (0, 6, 8, 'release'):
111111
raise nose.SkipTest('Not sure what to do about this.')
112112
baz = self._create_contextmanager_attribs()
113113
self.assertEqual(baz.__doc__, "Whee!")
@@ -212,7 +212,7 @@ def method(self, a, b, c=None):
212212

213213

214214
def test_typo_enter(self):
215-
if python_toolbox.__version_info__ <= (0, 6, 7, 'release'):
215+
if python_toolbox.__version_info__ <= (0, 6, 8, 'release'):
216216
raise nose.SkipTest
217217
class MyContextManager(ContextManager):
218218
def __unter__(self):
@@ -226,7 +226,7 @@ def __exit__(self, *exc):
226226

227227

228228
def test_typo_exit(self):
229-
if python_toolbox.__version_info__ <= (0, 6, 7, 'release'):
229+
if python_toolbox.__version_info__ <= (0, 6, 8, 'release'):
230230
raise nose.SkipTest
231231
class MyContextManager(ContextManager):
232232
def __enter__(self):

0 commit comments

Comments
 (0)