Skip to content

Commit 368489c

Browse files
committed
Merge branch 'origin/development'
2 parents d974d1f + 9bf3bf3 commit 368489c

File tree

43 files changed

+416
-98
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+416
-98
lines changed

README.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Please keep in mind that Python Toolbox is still in alpha stage, and that backwa
3434

3535
## Present ##
3636

37-
Python Toolbox is at version 0.6.9, which is an alpha release. It's being used in production every day, but backward compatibility isn't guaranteed yet.
37+
Python Toolbox is at version 0.6.10, which is an alpha release. It's being used in production every day, but backward compatibility isn't guaranteed yet.
3838

3939
## Next tasks ##
4040

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
# built documents.
4646
#
4747
# The short X.Y version.
48-
version = '0.6.9'
48+
version = '0.6.10'
4949
# The full version, including alpha/beta/rc tags.
50-
release = '0.6.9'
50+
release = '0.6.10'
5151

5252
# The language for content autogenerated by Sphinx. Refer to documentation
5353
# for a list of supported languages.

misc/IDE files/Wing/python_toolbox_py3.wpr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ proj.directory-list = [{'dirloc': loc('../../..'),
3434
'watch_for_changes': True}]
3535
proj.file-type = 'shared'
3636
proj.home-dir = loc('../../..')
37-
proj.launch-config = {loc('../../../../../../Dropbox/Desktop/run_in_bash.py'): (''\
37+
proj.launch-config = {loc('../../../source_py3/python_toolbox/cute_iter_tools.py'): (''\
3838
'project',
3939
(u'',
4040
'launch-OHU716PSo2P5T54y'))}

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def get_packages():
117117
Present
118118
-------
119119
120-
Python Toolbox is at version 0.6.9, which is an alpha release. It's being used
120+
Python Toolbox is at version 0.6.10, which is an alpha release. It's being used
121121
in production every day, but backward compatibility isn't guaranteed yet.
122122
123123
Next tasks
@@ -153,7 +153,7 @@ def get_packages():
153153

154154
setuptools.setup(
155155
name='python_toolbox',
156-
version='0.6.9',
156+
version='0.6.10',
157157
test_suite='nose.collector',
158158
install_requires=install_requires,
159159
tests_require=['nose>=1.0.0',

source_py2/python_toolbox/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
import python_toolbox.monkeypatch_envelopes
1818
import python_toolbox.monkeypatch_pathlib
1919

20-
__version_info__ = python_toolbox.version_info.VersionInfo(0, 6, 9)
20+
__version_info__ = python_toolbox.version_info.VersionInfo(0, 6, 10)
2121
__version__ = __version_info__.version_text
2222

source_py2/python_toolbox/address_tools/object_to_string.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import types
77
import re
88

9-
from python_toolbox import import_tools
109
from python_toolbox import dict_tools
1110
from python_toolbox import caching
1211

source_py2/python_toolbox/address_tools/string_to_object.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55

66
import types
77

8-
from python_toolbox import import_tools
98
from python_toolbox import dict_tools
109
from python_toolbox import re_tools
1110

12-
# from . import object_to_string (at bottom of file.)
1311
from .shared import (_contained_address_pattern, _address_pattern,
1412
_get_parent_and_dict_from_namespace)
1513

@@ -90,6 +88,8 @@ def get_object_by_address(address, root=None, namespace={}):
9088
# todo: should know what exception this will raise if the address is bad /
9189
# object doesn't exist.
9290

91+
from python_toolbox import import_tools # Avoiding circular import.
92+
9393
if not _address_pattern.match(address):
9494
raise ValueError("'%s' is not a legal address." % address)
9595

source_py2/python_toolbox/caching/decorators.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import functools
1212
import datetime as datetime_module
1313

14+
from python_toolbox import misc_tools
1415
from python_toolbox import binary_search
1516
from python_toolbox import decorator_tools
1617
from python_toolbox.sleek_reffing import SleekCallArgs
@@ -93,7 +94,6 @@ def decorator(function):
9394

9495
if time_to_keep:
9596

96-
cache_dict = OrderedDict()
9797
sorting_key_function = lambda sleek_call_args: \
9898
cached._cache[sleek_call_args][1]
9999

@@ -111,11 +111,11 @@ def remove_expired_entries():
111111
for key in cached._cache.keys()[:cutting_point]:
112112
del cached._cache[key]
113113

114-
114+
@misc_tools.set_attributes(_cache=OrderedDict())
115115
def cached(function, *args, **kwargs):
116116
remove_expired_entries()
117117
sleek_call_args = \
118-
SleekCallArgs(cache_dict, function, *args, **kwargs)
118+
SleekCallArgs(cached._cache, function, *args, **kwargs)
119119
try:
120120
return cached._cache[sleek_call_args][0]
121121
except KeyError:
@@ -129,11 +129,10 @@ def cached(function, *args, **kwargs):
129129

130130
else: # not time_to_keep
131131

132-
cache_dict = {}
133-
132+
@misc_tools.set_attributes(_cache={})
134133
def cached(function, *args, **kwargs):
135134
sleek_call_args = \
136-
SleekCallArgs(cache_dict, function, *args, **kwargs)
135+
SleekCallArgs(cached._cache, function, *args, **kwargs)
137136
try:
138137
return cached._cache[sleek_call_args]
139138
except KeyError:
@@ -143,11 +142,10 @@ def cached(function, *args, **kwargs):
143142

144143
else: # max_size < infinity
145144

146-
cache_dict = OrderedDict()
147-
145+
@misc_tools.set_attributes(_cache=OrderedDict())
148146
def cached(function, *args, **kwargs):
149147
sleek_call_args = \
150-
SleekCallArgs(cache_dict, function, *args, **kwargs)
148+
SleekCallArgs(cached._cache, function, *args, **kwargs)
151149
try:
152150
result = cached._cache[sleek_call_args]
153151
cached._cache.move_to_end(sleek_call_args)
@@ -159,7 +157,6 @@ def cached(function, *args, **kwargs):
159157
cached._cache.popitem(last=False)
160158
return value
161159

162-
cached._cache = cache_dict
163160

164161
result = decorator_tools.decorator(cached, function)
165162

source_py2/python_toolbox/cute_iter_tools.py

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
infinity = float('inf')
1616

1717

18-
def iterate_overlapping_subsequences(iterable, length=2, wrap_around=False):
18+
def iterate_overlapping_subsequences(iterable, length=2, wrap_around=False,
19+
lazy_tuple=False):
1920
'''
2021
Iterate over overlapping subsequences from the iterable.
2122
@@ -28,7 +29,22 @@ def iterate_overlapping_subsequences(iterable, length=2, wrap_around=False):
2829
2930
If `wrap_around=True`, the result would be `[(0, 1, 2), (1,
3031
2, 3), (2, 3, 0), (3, 0, 1)]`.
32+
33+
If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
3134
'''
35+
iterator = _iterate_overlapping_subsequences(
36+
iterable=iterable, length=length, wrap_around=wrap_around
37+
)
38+
39+
if lazy_tuple:
40+
from python_toolbox import nifty_collections # Avoiding circular import.
41+
return nifty_collections.LazyTuple(iterator)
42+
else:
43+
return iterator
44+
45+
46+
def _iterate_overlapping_subsequences(iterable, length, wrap_around):
47+
3248
if length == 1:
3349
for item in iterable:
3450
yield item
@@ -68,15 +84,27 @@ def iterate_overlapping_subsequences(iterable, length=2, wrap_around=False):
6884
yield tuple(deque)
6985

7086

71-
def shorten(iterable, n):
87+
def shorten(iterable, n, lazy_tuple=False):
7288
'''
7389
Shorten an iterable to length `n`.
7490
7591
Iterate over the given iterable, but stop after `n` iterations (Or when the
7692
iterable stops iteration by itself.)
7793
7894
`n` may be infinite.
95+
96+
If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
7997
'''
98+
iterator = _shorten(iterable=iterable, n=n)
99+
100+
if lazy_tuple:
101+
from python_toolbox import nifty_collections # Avoiding circular import.
102+
return nifty_collections.LazyTuple(iterator)
103+
else:
104+
return iterator
105+
106+
107+
def _shorten(iterable, n):
80108

81109
if n == infinity:
82110
for thing in iterable:
@@ -94,15 +122,27 @@ def shorten(iterable, n):
94122
raise StopIteration
95123

96124

97-
def enumerate(reversible, reverse_index=False):
125+
def enumerate(reversible, reverse_index=False, lazy_tuple=False):
98126
'''
99127
Iterate over `(i, item)` pairs, where `i` is the index number of `item`.
100128
101129
This is an extension of the builtin `enumerate`. What it allows is to get a
102130
reverse index, by specifying `reverse_index=True`. This causes `i` to count
103131
down to zero instead of up from zero, so the `i` of the last member will be
104132
zero.
133+
134+
If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
105135
'''
136+
iterator = _enumerate(reversible=reversible, reverse_index=reverse_index)
137+
138+
if lazy_tuple:
139+
from python_toolbox import nifty_collections # Avoiding circular import.
140+
return nifty_collections.LazyTuple(iterator)
141+
else:
142+
return iterator
143+
144+
145+
def _enumerate(reversible, reverse_index):
106146
if reverse_index is False:
107147
return __builtin__.enumerate(reversible)
108148
else:
@@ -136,20 +176,31 @@ def get_length(iterable):
136176
return i
137177

138178

139-
def iter_with(iterable, context_manager):
140-
'''Iterate on `iterable`, `with`ing the context manager on every `next`.'''
179+
def iter_with(iterable, context_manager, lazy_tuple=False):
180+
'''
181+
Iterate on `iterable`, `with`ing the context manager on every `next`.
182+
183+
If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
184+
'''
185+
iterator = _iter_with(iterable=iterable, context_manager=context_manager)
186+
187+
if lazy_tuple:
188+
from python_toolbox import nifty_collections # Avoiding circular import.
189+
return nifty_collections.LazyTuple(iterator)
190+
else:
191+
return iterator
192+
193+
194+
def _iter_with(iterable, context_manager):
141195

142196
iterator = iter(iterable)
143197

144198
while True:
145199

146200
with context_manager:
147201
next_item = next(iterator)
148-
# You may notice that we are not `except`ing a `StopIteration`
149-
# here; If we get one, it'll just get propagated and end *this*
150-
# iterator. todo: I just realized this will probably cause a bug
151-
# where `__exit__` will get the `StopIteration`! Make failing tests
152-
# and fix.
202+
# Recycling `StopIteration` exception. (Assuming the context
203+
# manager doesn't have special treatment for it.)
153204

154205
yield next_item
155206

@@ -167,13 +218,18 @@ def get_items(iterable, n, container_type=tuple):
167218
return container_type(shorten(iterable, n))
168219

169220

170-
def double_filter(filter_function, iterable):
221+
def double_filter(filter_function, iterable, lazy_tuple=False):
171222
'''
172-
Filter an `iterable` into two lists according to a `filter_function`.
223+
Filter an `iterable` into two iterables according to a `filter_function`.
173224
174225
This is similar to the builtin `filter`, except it returns a tuple of two
175226
iterators, the first iterating on items that passed the filter function,
176227
and the second iterating on items that didn't.
228+
229+
Note that this function is not thread-safe. (You may not consume the two
230+
iterators on two separate threads.)
231+
232+
If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
177233
'''
178234
iterator = iter(iterable)
179235

@@ -202,7 +258,13 @@ def make_false_iterator():
202258
else:
203259
yield value
204260

205-
return (make_true_iterator(), make_false_iterator())
261+
iterators = (make_true_iterator(), make_false_iterator())
262+
263+
if lazy_tuple:
264+
from python_toolbox import nifty_collections # Avoiding circular import.
265+
return tuple(map(nifty_collections.LazyTuple, iterators))
266+
else:
267+
return iterators
206268

207269

208270

@@ -221,7 +283,7 @@ def get_ratio(filter_function, iterable):
221283

222284

223285
def fill(iterable, fill_value=None, fill_value_maker=None, length=infinity,
224-
sequence_type=None):
286+
sequence_type=None, lazy_tuple=False):
225287
'''
226288
Iterate on `iterable`, and after it's exhaused, yield fill values.
227289
@@ -232,19 +294,26 @@ def fill(iterable, fill_value=None, fill_value_maker=None, length=infinity,
232294
If `length` is given, shortens the iterator to that length.
233295
234296
If `sequence_type` is given, instead of returning an iterator, this
235-
function will return a sequence of that type.
297+
function will return a sequence of that type. If `lazy_tuple=True`, uses a
298+
`LazyTuple`. (Can't use both options together.)
236299
'''
300+
# Validating user input:
301+
assert (sequence_type is None) or (lazy_tuple is False)
302+
237303
iterator = _fill(iterable, fill_value=fill_value,
238304
fill_value_maker=fill_value_maker,
239305
length=length)
240306

241-
if sequence_type is None:
307+
if lazy_tuple:
308+
from python_toolbox import nifty_collections # Avoiding circular import.
309+
return nifty_collections.LazyTuple(iterator)
310+
elif sequence_type is None:
242311
return iterator
243312
else:
244313
return sequence_type(iterator)
245314

246315

247-
def _fill(iterable, fill_value=None, fill_value_maker=None, length=infinity):
316+
def _fill(iterable, fill_value, fill_value_maker, length):
248317
if fill_value_maker is not None:
249318
assert fill_value is None
250319
else:
@@ -267,6 +336,30 @@ def _fill(iterable, fill_value=None, fill_value_maker=None, length=infinity):
267336
yield fill_value_maker()
268337

269338

339+
def call_until_exception(function, exception, lazy_tuple=False):
340+
'''
341+
Iterate on values returned from `function` until getting `exception`.
342+
343+
If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
344+
'''
345+
iterator = _call_until_exception(function, exception)
346+
if lazy_tuple:
347+
from python_toolbox import nifty_collections # Avoiding circular import.
348+
return nifty_collections.LazyTuple(iterator)
349+
else:
350+
return iterator
351+
352+
353+
def _call_until_exception(function, exception):
354+
from python_toolbox import sequence_tools
355+
exceptions = sequence_tools.to_tuple(exception, item_type=type)
356+
try:
357+
while True:
358+
yield function()
359+
except exceptions:
360+
raise StopIteration
361+
362+
270363
def get_single_if_any(iterable,
271364
exception_on_multiple=Exception('More than one value '
272365
'not allowed.')):

source_py2/python_toolbox/misc_tools/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
is_magic_variable_name, get_actual_type, is_number, identity_function,
1010
do_nothing, OwnNameDiscoveringDescriptor, find_clear_place_on_circle,
1111
general_sum, general_product, is_legal_email_address, is_type, NonInstatiable,
12-
repeat_getattr, add_extension_if_plain
12+
repeat_getattr, add_extension_if_plain, set_attributes, pocket
1313
)
1414
from . import name_mangling
1515
from .proxy_property import ProxyProperty

0 commit comments

Comments
 (0)