Skip to content

Commit 1c8a291

Browse files
committed
Merge branch 'dev' of github.com:seperman/deepdiff into dev
2 parents b3a4947 + 8420f42 commit 1c8a291

File tree

3 files changed

+202
-36
lines changed

3 files changed

+202
-36
lines changed

deepdiff/helper.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class np_type:
4141
np_complex64 = np_type # pragma: no cover.
4242
np_complex128 = np_type # pragma: no cover.
4343
np_complex_ = np_type # pragma: no cover.
44+
np_complexfloating = np_type # pragma: no cover.
4445
else:
4546
np_array_factory = np.array
4647
np_ndarray = np.ndarray
@@ -61,13 +62,18 @@ class np_type:
6162
np_complex64 = np.complex64
6263
np_complex128 = np.complex128
6364
np_complex_ = np.complex_
65+
np_complexfloating = np.complexfloating
6466

6567
numpy_numbers = (
6668
np_int8, np_int16, np_int32, np_int64, np_uint8,
6769
np_uint16, np_uint32, np_uint64, np_intp, np_uintp,
6870
np_float32, np_float64, np_float_, np_complex64,
6971
np_complex128, np_complex_,)
7072

73+
numpy_complex_numbers = (
74+
np_complexfloating, np_complex64, np_complex128, np_complex_,
75+
)
76+
7177
numpy_dtypes = set(numpy_numbers)
7278
numpy_dtypes.add(np_bool_)
7379

@@ -102,6 +108,7 @@ class np_type:
102108
strings = (str, bytes) # which are both basestring
103109
unicode_type = str
104110
bytes_type = bytes
111+
only_complex_number = (complex,) + numpy_complex_numbers
105112
only_numbers = (int, float, complex, Decimal) + numpy_numbers
106113
datetimes = (datetime.datetime, datetime.date, datetime.timedelta, datetime.time)
107114
uuids = (uuid.UUID)
@@ -115,8 +122,6 @@ class np_type:
115122

116123
ID_PREFIX = '!>*id'
117124

118-
ZERO_DECIMAL_CHARACTERS = set("-0.")
119-
120125
KEY_TO_VAL_STR = "{}:{}"
121126

122127
TREE_VIEW = 'tree'
@@ -321,20 +326,51 @@ def number_to_string(number, significant_digits, number_format_notation="f"):
321326
using = number_formatting[number_format_notation]
322327
except KeyError:
323328
raise ValueError("number_format_notation got invalid value of {}. The valid values are 'f' and 'e'".format(number_format_notation)) from None
324-
if isinstance(number, Decimal):
325-
tup = number.as_tuple()
329+
330+
if not isinstance(number, numbers):
331+
return number
332+
elif isinstance(number, Decimal):
326333
with localcontext() as ctx:
327-
ctx.prec = len(tup.digits) + tup.exponent + significant_digits
334+
# Precision = number of integer digits + significant_digits
335+
# Using number//1 to get the integer part of the number
336+
ctx.prec = len(str(abs(number // 1))) + significant_digits
328337
number = number.quantize(Decimal('0.' + '0' * significant_digits))
329-
elif not isinstance(number, numbers):
330-
return number
338+
elif isinstance(number, only_complex_number):
339+
# Case for complex numbers.
340+
number = number.__class__(
341+
"{real}+{imag}j".format(
342+
real=number_to_string(
343+
number=number.real,
344+
significant_digits=significant_digits,
345+
number_format_notation=number_format_notation
346+
),
347+
imag=number_to_string(
348+
number=number.imag,
349+
significant_digits=significant_digits,
350+
number_format_notation=number_format_notation
351+
)
352+
)
353+
)
354+
else:
355+
number = round(number=number, ndigits=significant_digits)
356+
357+
if significant_digits == 0:
358+
number = int(number)
359+
360+
if number == 0.0:
361+
# Special case for 0: "-0.xx" should compare equal to "0.xx"
362+
number = abs(number)
363+
364+
# Cast number to string
331365
result = (using % significant_digits).format(number)
332-
# Special case for 0: "-0.00" should compare equal to "0.00"
333-
if set(result) <= ZERO_DECIMAL_CHARACTERS:
334-
result = "0.00"
335366
# https://bugs.python.org/issue36622
336-
if number_format_notation == 'e' and isinstance(number, float):
337-
result = result.replace('+0', '+')
367+
if number_format_notation == 'e':
368+
# Removing leading 0 for exponential part.
369+
result = re.sub(
370+
pattern=r'(?<=e(\+|\-))0(?=\d)+',
371+
repl=r'',
372+
string=result
373+
)
338374
return result
339375

340376

tests/test_hash.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ def test_same_sets_same_hash(self):
357357
assert t1_hash[get_id(t1)] == t2_hash[get_id(t2)]
358358

359359
@pytest.mark.parametrize("t1, t2, significant_digits, number_format_notation, result", [
360-
({0.012, 0.98}, {0.013, 0.99}, 1, "f", 'set:float:0.00,float:1.0'),
361-
(100000, 100021, 3, "e", 'int:1.000e+05'),
360+
({0.012, 0.98}, {0.013, 0.99}, 1, "f", 'set:float:0.0,float:1.0'),
361+
(100000, 100021, 3, "e", 'int:1.000e+5'),
362362
])
363363
def test_similar_significant_hash(self, t1, t2, significant_digits,
364364
number_format_notation, result):

tests/test_helper.py

Lines changed: 152 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,160 @@ def test_short_repr_when_long(self):
3333
output = short_repr(item)
3434
assert output == "{'Eat more':...}"
3535

36-
@pytest.mark.parametrize("t1, t2, significant_digits, expected_result",
36+
@pytest.mark.parametrize("t1, t2, significant_digits, number_format_notation, expected_result",
3737
[
38-
(10, 10.0, 5, True),
39-
(10, 10.2, 5, ('10.00000', '10.20000')),
40-
(10, 10.2, 0, True),
41-
(Decimal(10), 10, 0, True),
42-
(Decimal(10), 10, 10, True),
43-
(Decimal(10), 10.0, 0, True),
44-
(Decimal(10), 10.0, 10, True),
45-
(Decimal('10.0'), 10.0, 5, True),
46-
(Decimal('10.01'), 10.01, 1, True),
47-
(Decimal('10.01'), 10.01, 2, True),
48-
(Decimal('10.01'), 10.01, 5, True),
49-
(Decimal('10.01'), 10.01, 8, True),
50-
(Decimal('10.010'), 10.01, 3, True),
51-
(Decimal('100000.1'), 100000.1, 0, True),
52-
(Decimal('100000.1'), 100000.1, 1, True),
53-
(Decimal('100000.1'), 100000.1, 5, True),
54-
(Decimal('100000'), 100000.1, 0, True),
55-
(Decimal('100000'), 100000.1, 1, ('100000.0', '100000.1')),
38+
(10, 10.0, 5, "f", True),
39+
(10, 10.0, 5, "e", True),
40+
(10, 10.2, 5, "f", ('10.00000', '10.20000')),
41+
(10, 10.2, 5, "e", ('1.00000e+1', '1.02000e+1')),
42+
(10, 10.2, 0, "f", True),
43+
(10, 10.2, 0, "e", True),
44+
(Decimal(10), 10, 0, "f", True),
45+
(Decimal(10), 10, 0, "e", True),
46+
(Decimal(10), 10, 10, "f", True),
47+
(Decimal(10), 10, 10, "e", True),
48+
(Decimal(10), 10.0, 0, "f", True),
49+
(Decimal(10), 10.0, 0, "e", True),
50+
(Decimal(10), 10.0, 10, "f", True),
51+
(Decimal(10), 10.0, 10, "e", True),
52+
(Decimal('10.0'), 10.0, 5, "f", True),
53+
(Decimal('10.0'), 10.0, 5, "e", True),
54+
(Decimal('10.01'), 10.01, 1, "f", True),
55+
(Decimal('10.01'), 10.01, 1, "e", True),
56+
(Decimal('10.01'), 10.01, 2, "f", True),
57+
(Decimal('10.01'), 10.01, 2, "e", True),
58+
(Decimal('10.01'), 10.01, 5, "f", True),
59+
(Decimal('10.01'), 10.01, 5, "e", True),
60+
(Decimal('10.01'), 10.01, 8, "f", True),
61+
(Decimal('10.01'), 10.01, 8, "e", True),
62+
(Decimal('10.010'), 10.01, 3, "f", True),
63+
(Decimal('10.010'), 10.01, 3, "e", True),
64+
(Decimal('100000.1'), 100000.1, 0, "f", True),
65+
(Decimal('100000.1'), 100000.1, 0, "e", True),
66+
(Decimal('100000.1'), 100000.1, 1, "f", True),
67+
(Decimal('100000.1'), 100000.1, 1, "e", True),
68+
(Decimal('100000.1'), 100000.1, 5, "f", True),
69+
(Decimal('100000.1'), 100000.1, 5, "e", True),
70+
(Decimal('100000'), 100000.1, 0, "f", True),
71+
(Decimal('100000'), 100000.1, 0, "e", True),
72+
(Decimal('100000'), 100000.1, 1, "f", ('100000.0', '100000.1')),
73+
(Decimal('100000'), 100000.1, 1, "e", True),
74+
(Decimal('-100000'), 100000.1, 1, "f", ('-100000.0', '100000.1')),
75+
(Decimal('-100000'), 100000.1, 1, "e", ("-1.0e+5","1.0e+5")),
76+
(0, 0.0, 5, "f", True),
77+
(0, 0.0, 5, "e", True),
78+
(0, 0.2, 5, "f", ('0.00000', '0.20000')),
79+
(0, 0.2, 5, "e", ('0.00000e+0', '2.00000e-1')),
80+
(0, 0.2, 0, "f", True),
81+
(0, 0.2, 0, "e", True),
82+
(Decimal(0), 0, 0, "f", True),
83+
(Decimal(0), 0, 0, "e", True),
84+
(Decimal(0), 0, 10, "f", True),
85+
(Decimal(0), 0, 10, "e", True),
86+
(Decimal(0), 0.0, 0, "f", True),
87+
(Decimal(0), 0.0, 0, "e", True),
88+
(Decimal(0), 0.0, 10, "f", True),
89+
(Decimal(0), 0.0, 10, "e", True),
90+
(Decimal('0.0'), 0.0, 5, "f", True),
91+
(Decimal('0.0'), 0.0, 5, "e", True),
92+
(Decimal('0.01'), 0.01, 1, "f", True),
93+
(Decimal('0.01'), 0.01, 1, "e", True),
94+
(Decimal('0.01'), 0.01, 2, "f", True),
95+
(Decimal('0.01'), 0.01, 2, "e", True),
96+
(Decimal('0.01'), 0.01, 5, "f", True),
97+
(Decimal('0.01'), 0.01, 5, "e", True),
98+
(Decimal('0.01'), 0.01, 8, "f", True),
99+
(Decimal('0.01'), 0.01, 8, "e", True),
100+
(Decimal('0.010'), 0.01, 3, "f", True),
101+
(Decimal('0.010'), 0.01, 3, "e", True),
102+
(Decimal('0.00002'), 0.00001, 0, "f", True),
103+
(Decimal('0.00002'), 0.00001, 0, "e", True),
104+
(Decimal('0.00002'), 0.00001, 1, "f", True),
105+
(Decimal('0.00002'), 0.00001, 1, "e", True),
106+
(Decimal('0.00002'), 0.00001, 5, "f", ('0.00002', '0.00001')),
107+
(Decimal('0.00002'), 0.00001, 5, "e", ('2.00000e-5', '1.00000e-5')),
108+
(Decimal('0.00002'), 0.00001, 6, "f", ('0.000020', '0.000010')),
109+
(Decimal('0.00002'), 0.00001, 6, "e", ('2.000000e-5', '1.000000e-5')),
110+
(Decimal('0'), 0.1, 0, "f", True),
111+
(Decimal('0'), 0.1, 0, "e", True),
112+
(Decimal('0'), 0.1, 1, "f", ('0.0', '0.1')),
113+
(Decimal('0'), 0.1, 1, "e", ('0.0e+0', '1.0e-1')),
114+
(-0, 0.0, 5, "f", True),
115+
(-0, 0.0, 5, "e", True),
116+
(-0, 0.2, 5, "f", ('0.00000', '0.20000')),
117+
(-0, 0.2, 5, "e", ('0.00000e+0', '2.00000e-1')),
118+
(-0, 0.2, 0, "f", True),
119+
(-0, 0.2, 0, "e", True),
120+
(Decimal(-0), 0, 0, "f", True),
121+
(Decimal(-0), 0, 0, "e", True),
122+
(Decimal(-0), 0, 10, "f", True),
123+
(Decimal(-0), 0, 10, "e", True),
124+
(Decimal(-0), 0.0, 0, "f", True),
125+
(Decimal(-0), 0.0, 0, "e", True),
126+
(Decimal(-0), 0.0, 10, "f", True),
127+
(Decimal(-0), 0.0, 10, "e", True),
128+
(Decimal('-0.0'), 0.0, 5, "f", True),
129+
(Decimal('-0.0'), 0.0, 5, "e", True),
130+
(Decimal('-0.01'), 0.01, 1, "f", True),
131+
(Decimal('-0.01'), 0.01, 1, "e", True),
132+
(Decimal('-0.01'), 0.01, 2, "f", ('-0.01', '0.01')),
133+
(Decimal('-0.01'), 0.01, 2, "e", ('-1.00e-2', '1.00e-2')),
134+
(Decimal('-0.00002'), 0.00001, 0, "f", True),
135+
(Decimal('-0.00002'), 0.00001, 0, "e", True),
136+
(Decimal('-0.00002'), 0.00001, 1, "f", True),
137+
(Decimal('-0.00002'), 0.00001, 1, "e", True),
138+
(Decimal('-0.00002'), 0.00001, 5, "f", ('-0.00002', '0.00001')),
139+
(Decimal('-0.00002'), 0.00001, 5, "e", ('-2.00000e-5', '1.00000e-5')),
140+
(Decimal('-0.00002'), 0.00001, 6, "f", ('-0.000020', '0.000010')),
141+
(Decimal('-0.00002'), 0.00001, 6, "e", ('-2.000000e-5', '1.000000e-5')),
142+
(Decimal('-0'), 0.1, 0, "f", True),
143+
(Decimal('-0'), 0.1, 0, "e", True),
144+
(Decimal('-0'), 0.1, 1, "f", ('0.0', '0.1')),
145+
(Decimal('-0'), 0.1, 1, "e", ('0.0e+0', '1.0e-1')),
56146
])
57-
def test_number_to_string_decimal_digits(self, t1, t2, significant_digits, expected_result):
58-
st1 = number_to_string(t1, significant_digits=significant_digits, number_format_notation="f")
59-
st2 = number_to_string(t2, significant_digits=significant_digits, number_format_notation="f")
147+
def test_number_to_string_decimal_digits(self, t1, t2, significant_digits, number_format_notation, expected_result):
148+
st1 = number_to_string(t1, significant_digits=significant_digits, number_format_notation=number_format_notation)
149+
st2 = number_to_string(t2, significant_digits=significant_digits, number_format_notation=number_format_notation)
150+
if expected_result is True:
151+
assert st1 == st2
152+
else:
153+
assert st1 == expected_result[0]
154+
assert st2 == expected_result[1]
155+
156+
@pytest.mark.parametrize("t1, t2, significant_digits, number_format_notation, expected_result",
157+
[
158+
(10j, 10.0j, 5, "f", True),
159+
(10j, 10.0j, 5, "e", True),
160+
(4+10j, 4.0000002+10.0000002j, 5, "f", True),
161+
(4+10j, 4.0000002+10.0000002j, 5, "e", True),
162+
(4+10j, 4.0000002+10.0000002j, 7, "f", ('4.0000000+10.0000000j', '4.0000002+10.0000002j')),
163+
(4+10j, 4.0000002+10.0000002j, 7, "e", ('4.0000000e+0+1.0000000e+1j', '4.0000002e+0+1.0000000e+1j')),
164+
(0.00002+0.00002j, 0.00001+0.00001j, 0, "f", True),
165+
(0.00002+0.00002j, 0.00001+0.00001j, 0, "e", True),
166+
(0.00002+0.00002j, 0.00001+0.00001j, 5, "f", ('0.00002+0.00002j', '0.00001+0.00001j')),
167+
(0.00002+0.00002j, 0.00001+0.00001j, 5, "e", ('2.00000e-5+2.00000e-5j', '1.00000e-5+1.00000e-5j')),
168+
(-0.00002-0.00002j, 0.00001+0.00001j, 0, "f", True),
169+
(-0.00002-0.00002j, 0.00001+0.00001j, 0, "e", True),
170+
(10j, 10.2j, 5, "f", ('0.00000+10.00000j', '0.00000+10.20000j')),
171+
(10j, 10.2j, 5, "e", ('0.00000e+0+1.00000e+1j', '0.00000e+0+1.02000e+1j')),
172+
(10j, 10.2j, 0, "f", True),
173+
(10j, 10.2j, 0, "e", True),
174+
(0j, 0.0j, 5, "f", True),
175+
(0j, 0.0j, 5, "e", True),
176+
(0j, 0.2j, 5, "f", ('0.00000', '0.00000+0.20000j')),
177+
(0j, 0.2j, 5, "e", ('0.00000e+0', '0.00000e+0+2.00000e-1j')),
178+
(0j, 0.2j, 0, "f", True),
179+
(0j, 0.2j, 0, "e", True),
180+
(-0j, 0.0j, 5, "f", True),
181+
(-0j, 0.0j, 5, "e", True),
182+
(-0j, 0.2j, 5, "f", ('0.00000', '0.00000+0.20000j')),
183+
(-0j, 0.2j, 5, "e", ('0.00000e+0', '0.00000e+0+2.00000e-1j')),
184+
(-0j, 0.2j, 0, "f", True),
185+
(-0j, 0.2j, 0, "e", True),
186+
])
187+
def test_number_to_string_complex_digits(self, t1, t2, significant_digits, number_format_notation, expected_result):
188+
st1 = number_to_string(t1, significant_digits=significant_digits, number_format_notation=number_format_notation)
189+
st2 = number_to_string(t2, significant_digits=significant_digits, number_format_notation=number_format_notation)
60190
if expected_result is True:
61191
assert st1 == st2
62192
else:

0 commit comments

Comments
 (0)