Skip to content

Commit fe56571

Browse files
authored
Merge pull request #314 from eriknw/tlz
`tlz` package: `cytoolz` or `toolz`
2 parents be198a6 + 0ff1118 commit fe56571

File tree

5 files changed

+167
-2
lines changed

5 files changed

+167
-2
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ install:
2121
script:
2222
- export MAJOR_PYTHON_VERSION=`echo $TRAVIS_PYTHON_VERSION | cut -c 1`
2323
- coverage run --source=toolz $(which nosetests)
24-
--with-doctest
24+
--with-doctest toolz/
2525
- if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then coverage report --show-missing --fail-under=100 ; fi
2626
- if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then pep8 --ignore=$PEP8_IGNORE --exclude=conf.py,tests,examples,bench -r --show-source . ; fi
2727

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
keywords='functional utility itertools functools',
1616
packages=['toolz',
1717
'toolz.sandbox',
18-
'toolz.curried'],
18+
'toolz.curried',
19+
'tlz'],
1920
package_data={'toolz': ['tests/*.py']},
2021
long_description=(open('README.rst').read() if exists('README.rst')
2122
else ''),

tlz/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""``tlz`` mirrors the ``toolz`` API and uses ``cytoolz`` if possible.
2+
3+
The ``tlz`` package is installed when ``toolz`` is installed. It provides
4+
a convenient way to use functions from ``cytoolz``--a faster Cython
5+
implementation of ``toolz``--if it is installed, otherwise it uses
6+
functions from ``toolz``.
7+
"""
8+
9+
from . import _build_tlz

tlz/_build_tlz.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import sys
2+
import types
3+
import toolz
4+
from toolz.compatibility import import_module
5+
6+
7+
class TlzLoader(object):
8+
""" Finds and loads ``tlz`` modules when added to sys.meta_path"""
9+
def __init__(self):
10+
self.always_from_toolz = set([
11+
toolz.pipe,
12+
])
13+
14+
def _load_toolz(self, fullname):
15+
rv = {}
16+
package, dot, submodules = fullname.partition('.')
17+
try:
18+
module_name = ''.join(['cytoolz', dot, submodules])
19+
rv['cytoolz'] = import_module(module_name)
20+
except ImportError:
21+
pass
22+
try:
23+
module_name = ''.join(['toolz', dot, submodules])
24+
rv['toolz'] = import_module(module_name)
25+
except ImportError:
26+
pass
27+
if not rv:
28+
raise ImportError(fullname)
29+
return rv
30+
31+
def find_module(self, fullname, path=None): # pragma: py3 no cover
32+
package, dot, submodules = fullname.partition('.')
33+
if package == 'tlz':
34+
return self
35+
36+
def load_module(self, fullname): # pragma: py3 no cover
37+
if fullname in sys.modules: # pragma: no cover
38+
return sys.modules[fullname]
39+
spec = TlzSpec(fullname, self)
40+
module = self.create_module(spec)
41+
sys.modules[fullname] = module
42+
self.exec_module(module)
43+
return module
44+
45+
def find_spec(self, fullname, path, target=None): # pragma: no cover
46+
package, dot, submodules = fullname.partition('.')
47+
if package == 'tlz':
48+
return TlzSpec(fullname, self)
49+
50+
def create_module(self, spec):
51+
return types.ModuleType(spec.name)
52+
53+
def exec_module(self, module):
54+
toolz_mods = self._load_toolz(module.__name__)
55+
fast_mod = toolz_mods.get('cytoolz') or toolz_mods['toolz']
56+
slow_mod = toolz_mods.get('toolz') or toolz_mods['cytoolz']
57+
module.__dict__.update(toolz.merge(fast_mod.__dict__, module.__dict__))
58+
package = fast_mod.__package__
59+
if package is not None:
60+
package, dot, submodules = package.partition('.')
61+
module.__package__ = ''.join(['tlz', dot, submodules])
62+
if not module.__doc__:
63+
module.__doc__ = fast_mod.__doc__
64+
65+
# show file from toolz during introspection
66+
module.__file__ = slow_mod.__file__
67+
68+
for k, v in fast_mod.__dict__.items():
69+
tv = slow_mod.__dict__.get(k)
70+
try:
71+
hash(tv)
72+
except TypeError:
73+
tv = None
74+
if tv in self.always_from_toolz:
75+
module.__dict__[k] = tv
76+
elif (
77+
isinstance(v, types.ModuleType)
78+
and v.__package__ == fast_mod.__name__
79+
):
80+
package, dot, submodules = v.__name__.partition('.')
81+
module_name = ''.join(['tlz', dot, submodules])
82+
submodule = import_module(module_name)
83+
module.__dict__[k] = submodule
84+
85+
86+
class TlzSpec(object):
87+
def __init__(self, name, loader):
88+
self.name = name
89+
self.loader = loader
90+
self.origin = None
91+
self.submodule_search_locations = []
92+
self.loader_state = None
93+
self.cached = None
94+
self.parent = None
95+
self.has_location = False
96+
97+
98+
tlz_loader = TlzLoader()
99+
sys.meta_path.append(tlz_loader)
100+
tlz_loader.exec_module(sys.modules['tlz'])

toolz/tests/test_tlz.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import toolz
2+
3+
4+
def test_tlz():
5+
import tlz
6+
tlz.curry
7+
tlz.functoolz.curry
8+
assert tlz.__package__ == 'tlz'
9+
assert tlz.__name__ == 'tlz'
10+
import tlz.curried
11+
assert tlz.curried.__package__ == 'tlz.curried'
12+
assert tlz.curried.__name__ == 'tlz.curried'
13+
tlz.curried.curry
14+
import tlz.curried.operator
15+
assert tlz.curried.operator.__package__ in (None, 'tlz.curried')
16+
assert tlz.curried.operator.__name__ == 'tlz.curried.operator'
17+
assert tlz.functoolz.__name__ == 'tlz.functoolz'
18+
m1 = tlz.functoolz
19+
import tlz.functoolz as m2
20+
assert m1 is m2
21+
import tlz.sandbox
22+
try:
23+
import tlzthisisabadname.curried
24+
1/0
25+
except ImportError:
26+
pass
27+
try:
28+
import tlz.curry
29+
1/0
30+
except ImportError:
31+
pass
32+
try:
33+
import tlz.badsubmodulename
34+
1/0
35+
except ImportError:
36+
pass
37+
38+
assert toolz.__package__ == 'toolz'
39+
assert toolz.curried.__package__ == 'toolz.curried'
40+
assert toolz.functoolz.__name__ == 'toolz.functoolz'
41+
try:
42+
import cytoolz
43+
assert cytoolz.__package__ == 'cytoolz'
44+
assert cytoolz.curried.__package__ == 'cytoolz.curried'
45+
assert cytoolz.functoolz.__name__ == 'cytoolz.functoolz'
46+
except ImportError:
47+
pass
48+
49+
assert tlz.__file__ == toolz.__file__
50+
assert tlz.functoolz.__file__ == toolz.functoolz.__file__
51+
52+
assert tlz.pipe is toolz.pipe
53+
54+
assert 'tlz' in tlz.__doc__
55+
assert tlz.curried.__doc__ is not None

0 commit comments

Comments
 (0)