Skip to content

Commit 5d12759

Browse files
Merge pull request #9 from brandonwillard/honor-custom-methods-in-cons
Make cons use custom __(r)add__ methods
2 parents c7e355e + a246dab commit 5d12759

File tree

3 files changed

+32
-25
lines changed

3 files changed

+32
-25
lines changed

cons/core.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
from abc import ABCMeta, ABC, abstractmethod
22
from functools import reduce
33
from operator import length_hint
4-
from collections import OrderedDict, UserString
54
from itertools import chain, islice
6-
from collections.abc import Iterator, Sequence, ItemsView, ByteString
5+
from collections import OrderedDict, UserString
6+
from collections.abc import Iterator, Sequence, ItemsView, ByteString, Mapping
77

88
from multipledispatch import dispatch
99

10-
from toolz import last, first
11-
12-
# We can't use this because `islice` drops __length_hint__ info.
13-
# from toolz.itertoolz import rest
10+
# This is the constructor/type used for `ConsNull` value `None`.
11+
default_ConsNull = list
1412

1513

1614
class ConsError(ValueError):
@@ -86,11 +84,7 @@ def __new__(cls, *parts):
8684
if len(parts) > 2:
8785
res = reduce(lambda x, y: ConsPair(y, x), reversed(parts))
8886
elif len(parts) == 2:
89-
car_part = first(parts)
90-
cdr_part = last(parts)
91-
92-
if cdr_part is None:
93-
cdr_part = []
87+
car_part, cdr_part = parts
9488

9589
if isinstance(
9690
cdr_part, (ConsNull, ConsPair, Iterator)
@@ -110,17 +104,22 @@ def __new__(cls, *parts):
110104
@classmethod
111105
def cons_merge(cls, car_part, cdr_part):
112106

113-
if isinstance(cdr_part, OrderedDict):
114-
cdr_part = cdr_part.items()
107+
if cdr_part is None:
108+
cdr_part = default_ConsNull()
115109

116-
res = chain((car_part,), cdr_part)
110+
if isinstance(cdr_part, Mapping):
111+
cdr_part = cdr_part.items()
117112

118113
if isinstance(cdr_part, ItemsView):
119-
res = OrderedDict(res)
120-
elif not isinstance(cdr_part, Iterator):
121-
res = type(cdr_part)(res)
122114

123-
return res
115+
return OrderedDict(chain((car_part,), cdr_part))
116+
117+
elif hasattr(cdr_part, "__add__") or hasattr(cdr_part, "__radd__"):
118+
# TODO: What about adding `list.extend`? We're already
119+
# constructing an instance of the CDR type.
120+
return type(cdr_part)((car_part,)) + cdr_part
121+
122+
return chain((car_part,), cdr_part)
124123

125124
def __hash__(self):
126125
return hash([self.car, self.cdr])
@@ -202,7 +201,7 @@ def car(z):
202201
@dispatch(Sequence)
203202
def _car(z):
204203
try:
205-
return first(z)
204+
return next(iter(z))
206205
except StopIteration:
207206
raise ConsError("Not a cons pair")
208207

@@ -218,7 +217,7 @@ def _car_Iterator(z):
218217
"""
219218
try:
220219
# z, _ = tee(z)
221-
return first(z)
220+
return next(iter(z))
222221
except StopIteration:
223222
raise ConsError("Not a cons pair")
224223

@@ -228,7 +227,7 @@ def _car_OrderedDict(z):
228227
if len(z) == 0:
229228
raise ConsError("Not a cons pair")
230229

231-
return first(z.items())
230+
return next(iter(z.items()))
232231

233232

234233
@_car.register(NonCons)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
name="cons",
1010
version=versioneer.get_version(),
1111
cmdclass=versioneer.get_cmdclass(),
12-
install_requires=["toolz", "logical-unification"],
12+
install_requires=["logical-unification"],
1313
packages=find_packages(exclude=["tests"]),
1414
tests_require=["pytest"],
1515
author="Brandon T. Willard",

tests/test_cons.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from functools import reduce
44
from itertools import chain, cycle
5-
from collections import OrderedDict
5+
from collections import OrderedDict, UserList
66
from collections.abc import Iterator
77

88
from unification import unify, reify, var
@@ -113,6 +113,16 @@ def test_cons_join():
113113
assert cons("a", cons("b", "c")).car == "a"
114114
assert cons("a", cons("b", "c")).cdr == cons("b", "c")
115115

116+
# Make sure that an overridden "append" (via `__add__`) is used and
117+
# that the results are returned unadulterated
118+
clist_res = [1, 2, 3]
119+
120+
class CustomList(UserList):
121+
def __add__(self, a):
122+
return clist_res
123+
124+
assert cons(1, CustomList([2, 3])) is clist_res
125+
116126

117127
def test_cons_str():
118128
assert repr(cons(1, 2)) == "ConsPair(1, 2)"
@@ -208,8 +218,6 @@ def test_car_cdr():
208218
assert cdr(cons(1, cons("a", "b"))) == cons("a", "b")
209219

210220
# We need to make sure that `__getitem__` is actually used.
211-
from collections import UserList
212-
213221
# Also, make sure `cdr` returns the `__getitem__` result unaltered
214222
clist_res = [5]
215223

0 commit comments

Comments
 (0)