Skip to content

Commit dd9cedf

Browse files
committed
Merge pull request #242 in LCL/wolframclientforpython from bugfix/396642-immutabledict to master
* commit '75e7631324eaea9b9c90471c4289cb48e71c169c': Fix bad expected PIL result. Update Pandas tests adding test for nested assocs upgrading isort adding immutabledict updating aiohttp
2 parents 78a8301 + 75e7631 commit dd9cedf

7 files changed

Lines changed: 62 additions & 36 deletions

File tree

wolframclient/cli/commands/refactor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Command(SimpleCommand):
1010

1111
modules = ["wolframclient"]
1212

13-
dependencies = (("isort", "4.3.20"), ("autoflake", "1.3"), ("black", "19.3b0"))
13+
dependencies = (("isort", "5.3.2"), ("autoflake", "1.3"), ("black", "19.3b0"))
1414

1515
def _module_args(self, *args):
1616

wolframclient/cli/commands/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def dependencies():
2323
yield ("pandas", "1.0.4")
2424
yield ("unittest-xml-reporting", None)
2525
if not six.PY2:
26-
yield ("aiohttp", "3.4.4")
26+
yield ("aiohttp", "3.6.2")
2727

2828

2929
class Command(SimpleCommand):

wolframclient/deserializers/wxf/wxfconsumer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from wolframclient.serializers.wxfencoder import constants
99
from wolframclient.serializers.wxfencoder.utils import array_to_list
1010
from wolframclient.utils.api import numpy
11+
from wolframclient.utils.datastructures import immutabledict
1112

1213
__all__ = ["WXFConsumer", "WXFConsumerNumpy"]
1314

@@ -112,7 +113,7 @@ def build_function(self, head, arg_list, **kwargs):
112113
"""
113114
return WLFunction(head, *arg_list)
114115

115-
def consume_association(self, current_token, tokens, dict_class=dict, **kwargs):
116+
def consume_association(self, current_token, tokens, dict_class=immutabledict, **kwargs):
116117
"""Consume a :class:`~wolframclient.deserializers.wxf.wxfparser.WXFToken` of type *association*.
117118
118119
By default, return a :class:`dict` made from the rules.

wolframclient/tests/deserializers/wxf_deserialize.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
from wolframclient.tests.configure import skip_for_jython
1919
from wolframclient.utils import six
2020
from wolframclient.utils.api import numpy
21+
from wolframclient.utils.datastructures import immutabledict
2122
from wolframclient.utils.tests import TestCase as BaseTestCase
2223

23-
2424
class TestCase(BaseTestCase):
2525
def test_token_dimensions(self):
2626
token = WXFToken(None)
@@ -199,6 +199,13 @@ def test_compressed_input(self):
199199
res = binary_deserialize(wxf, consumer=WXFConsumer())
200200
self.assertEqual(expr, res)
201201

202+
def test_nested_associations(self):
203+
expr = immutabledict((('a', 2), ('a', 3)))
204+
expr = immutabledict(((expr, expr), (2, 3)))
205+
wxf = export(expr, target_format="wxf", compress=True)
206+
res = binary_deserialize(wxf, consumer=WXFConsumer())
207+
self.assertEqual(expr, res)
208+
202209
# Custom consumers
203210
class BadGreedyConsumer(WXFConsumer):
204211
def consume_function(self, current_token, tokens, **kwargs):

wolframclient/tests/serializers/pandas.py

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,19 @@ def sparse_series_dict(self):
3737
d["b"] = 1
3838
d["a"] = 0
3939
d["c"] = 2
40-
return pandas.SparseSeries(d)
40+
return pandas.Series(d, dtype='Sparse[int]')
4141

4242
def sparse_series_dict_indexed(self):
4343
constructor_dict = {1: 1.0}
4444
index = [0, 1, 2]
4545
# Series with index passed in
46-
series = pandas.Series(constructor_dict)
47-
return pandas.SparseSeries(series, index=index)
48-
49-
def sparse_series_list_fill_zero(self):
50-
return pandas.SparseArray([1, 0, 3, 0], dtype=numpy.int64, fill_value=0)
51-
52-
def sparse_series_date(self):
53-
arr, index = self.create_numpy_data_nan()
54-
date_index = pandas.bdate_range("1/1/2011", periods=len(index))
55-
return pandas.SparseSeries(arr, index=date_index, kind="block", name="bseries")
46+
series = pandas.Series(constructor_dict, dtype='Sparse[float]')
47+
return pandas.Series(series, index=index)
5648

5749
def multiindex_series(self):
5850
mi = pandas.MultiIndex(
5951
levels=[["a", "b"], ["x", "y"], [0]],
60-
labels=[
52+
codes=[
6153
[1, 1, 1, 1, 0, 0, 0, 0],
6254
[0, 0, 1, 1, 0, 0, 1, 1],
6355
[0, -1, 0, -1, 0, -1, 0, -1],
@@ -192,24 +184,6 @@ def test_sparse_from_dict_indexed_as_dataset(self):
192184
pandas_series_head="dataset",
193185
)
194186

195-
# sparse array with zeros
196-
def test_sparse_array_as_numpy(self):
197-
sparse_s = self.sparse_series_list_fill_zero()
198-
self.export_compare(
199-
sparse_s,
200-
wl=b'BinaryDeserialize[ByteArray["ODrCAwEEAQAAAAAAAAADAAAAAAAAAA=="]]',
201-
wxf=b"8:\xc2\x03\x01\x04\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00",
202-
)
203-
204-
# timeseries
205-
def test_timeseries(self):
206-
ts = self.sparse_series_date()
207-
self.export_compare(
208-
ts,
209-
wl=b'TimeSeries[{{DateObject[{2011, 1, 3, 0, 0, 0.}, "Instant", "Gregorian", $TimeZone], 0.}, {DateObject[{2011, 1, 4, 0, 0, 0.}, "Instant", "Gregorian", $TimeZone], Indeterminate}, {DateObject[{2011, 1, 5, 0, 0, 0.}, "Instant", "Gregorian", $TimeZone], 2.}, {DateObject[{2011, 1, 6, 0, 0, 0.}, "Instant", "Gregorian", $TimeZone], Indeterminate}, {DateObject[{2011, 1, 7, 0, 0, 0.}, "Instant", "Gregorian", $TimeZone], 4.}}]',
210-
wxf=b"8:f\x01s\nTimeSeriesf\x05s\x04Listf\x02s\x04Listf\x04s\nDateObjectf\x06s\x04Listj\xdb\x07C\x01C\x03C\x00C\x00r\x00\x00\x00\x00\x00\x00\x00\x00S\x07InstantS\tGregorians\t$TimeZoner\x00\x00\x00\x00\x00\x00\x00\x00f\x02s\x04Listf\x04s\nDateObjectf\x06s\x04Listj\xdb\x07C\x01C\x04C\x00C\x00r\x00\x00\x00\x00\x00\x00\x00\x00S\x07InstantS\tGregorians\t$TimeZones\rIndeterminatef\x02s\x04Listf\x04s\nDateObjectf\x06s\x04Listj\xdb\x07C\x01C\x05C\x00C\x00r\x00\x00\x00\x00\x00\x00\x00\x00S\x07InstantS\tGregorians\t$TimeZoner\x00\x00\x00\x00\x00\x00\x00@f\x02s\x04Listf\x04s\nDateObjectf\x06s\x04Listj\xdb\x07C\x01C\x06C\x00C\x00r\x00\x00\x00\x00\x00\x00\x00\x00S\x07InstantS\tGregorians\t$TimeZones\rIndeterminatef\x02s\x04Listf\x04s\nDateObjectf\x06s\x04Listj\xdb\x07C\x01C\x07C\x00C\x00r\x00\x00\x00\x00\x00\x00\x00\x00S\x07InstantS\tGregorians\t$TimeZoner\x00\x00\x00\x00\x00\x00\x10@",
211-
)
212-
213187
# multi index
214188
def test_multiindex_as_assoc(self):
215189
s = self.multiindex_series()

wolframclient/tests/serializers/wl_pil.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_bool_img(self):
3333
out = export(img, target_format="wl")
3434
self.assertTrue(
3535
out
36-
== b'Image[BinaryDeserialize[ByteArray["ODrCEAICAgEAAAA="]], "Bit", Rule[ColorSpace, Automatic], Rule[Interleaving, True]]'
36+
== b'Image[BinaryDeserialize[ByteArray["ODrCEAICAgEAAAE="]], "Bit", Rule[ColorSpace, Automatic], Rule[Interleaving, True]]'
3737
or out
38-
== b'Image[BinaryDeserialize[ByteArray["ODrCEAICAgEAAAA="]], "Bit", Rule[Interleaving, True], Rule[ColorSpace, Automatic]]'
38+
== b'Image[BinaryDeserialize[ByteArray["ODrCEAICAgEAAAE="]], "Bit", Rule[Interleaving, True], Rule[ColorSpace, Automatic]]'
3939
)

wolframclient/utils/datastructures.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,50 @@ def __repr__(self):
1010
return dict.__repr__(self)
1111

1212

13+
def _fail(self, *args, **opts):
14+
raise TypeError("{0} does not support item assignment".format(self.__class__.__name__))
15+
16+
17+
class immutabledict(dict):
18+
"""
19+
hashable dict implementation, suitable for use as a key into
20+
other dicts.
21+
22+
>>> h1 = immutabledict({"apples": 1, "bananas":2})
23+
>>> h2 = immutabledict({"bananas": 3, "mangoes": 5})
24+
>>> h1+h2
25+
immutabledict(apples=1, bananas=3, mangoes=5)
26+
>>> d1 = {}
27+
>>> d1[h1] = "salad"
28+
>>> d1[h1]
29+
'salad'
30+
>>> d1[h2]
31+
Traceback (most recent call last):
32+
...
33+
KeyError: immutabledict(bananas=3, mangoes=5)
34+
35+
based on answers from
36+
http://stackoverflow.com/questions/1151658/python-hashable-dicts
37+
38+
"""
39+
40+
def __hash__(self):
41+
return hash(tuple(self.items()))
42+
43+
__setitem__ = _fail
44+
__delitem__ = _fail
45+
clear = _fail
46+
pop = _fail
47+
popitem = _fail
48+
setdefault = _fail
49+
update = _fail
50+
51+
def __add__(self, right):
52+
result = hashdict(self)
53+
dict.update(result, right)
54+
return result
55+
56+
1357
class Settings(dict):
1458
"""
1559
Dictionary subclass enabling attribute lookup/assignment of keys/values.

0 commit comments

Comments
 (0)