itercmp 0.0.0.dev0__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: itercmp
3
- Version: 0.0.0.dev0
3
+ Version: 0.1.0
4
4
  Summary: This project accesses a given TOML file.
5
5
  Author-email: Johannes <johannes.programming@gmail.com>
6
6
  License-Expression: MIT
@@ -25,4 +25,4 @@ itercmp
25
25
 
26
26
  Each minor version has its own documentation.
27
27
  These docs can be found as rst-files in the ``docs/`` directory of this project.
28
- They can also be viewed on the website `https://itercmp.johannes-programming.online/ <https://toml-get.johannes-programming.online/>`_.
28
+ They can also be viewed on the website `https://itercmp.johannes-programming.online/ <https://itercmp.johannes-programming.online/>`_.
@@ -4,4 +4,4 @@ itercmp
4
4
 
5
5
  Each minor version has its own documentation.
6
6
  These docs can be found as rst-files in the ``docs/`` directory of this project.
7
- They can also be viewed on the website `https://itercmp.johannes-programming.online/ <https://toml-get.johannes-programming.online/>`_.
7
+ They can also be viewed on the website `https://itercmp.johannes-programming.online/ <https://itercmp.johannes-programming.online/>`_.
@@ -26,7 +26,7 @@ license-files = [
26
26
  name = "itercmp"
27
27
  readme = "README.rst"
28
28
  requires-python = ">=3.11"
29
- version = "0.0.0.dev0"
29
+ version = "0.1.0"
30
30
 
31
31
  [project.urls]
32
32
  Download = "https://pypi.org/project/itercmp/#files"
@@ -0,0 +1,32 @@
1
+ import operator as op
2
+ from collections.abc import Callable, Collection
3
+ from functools import partial
4
+ from typing import Any
5
+
6
+ __all__ = ["ge", "gt", "le", "lt", "ordering"]
7
+
8
+ # def eq(x: Collection[Any], y: Collection[Any], /) -> bool:
9
+ # for a, b in zip(x, y):
10
+ # if a is b or a == b:
11
+ # continue
12
+ # return False
13
+ # return len(x) == len(y)
14
+
15
+
16
+ def ordering(
17
+ operator: Callable[[Any, Any], Any],
18
+ x: Collection[Any],
19
+ y: Collection[Any],
20
+ /,
21
+ ) -> Any:
22
+ for a, b in zip(x, y):
23
+ if a is b or a == b:
24
+ continue
25
+ return operator(x, y)
26
+ return operator(len(x), len(y))
27
+
28
+
29
+ ge = partial(ordering, op.ge)
30
+ gt = partial(ordering, op.gt)
31
+ le = partial(ordering, op.le)
32
+ lt = partial(ordering, op.lt)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: itercmp
3
- Version: 0.0.0.dev0
3
+ Version: 0.1.0
4
4
  Summary: This project accesses a given TOML file.
5
5
  Author-email: Johannes <johannes.programming@gmail.com>
6
6
  License-Expression: MIT
@@ -25,4 +25,4 @@ itercmp
25
25
 
26
26
  Each minor version has its own documentation.
27
27
  These docs can be found as rst-files in the ``docs/`` directory of this project.
28
- They can also be viewed on the website `https://itercmp.johannes-programming.online/ <https://toml-get.johannes-programming.online/>`_.
28
+ They can also be viewed on the website `https://itercmp.johannes-programming.online/ <https://itercmp.johannes-programming.online/>`_.
@@ -9,4 +9,6 @@ src/itercmp/py.typed
9
9
  src/itercmp.egg-info/PKG-INFO
10
10
  src/itercmp.egg-info/SOURCES.txt
11
11
  src/itercmp.egg-info/dependency_links.txt
12
- src/itercmp.egg-info/top_level.txt
12
+ src/itercmp.egg-info/top_level.txt
13
+ src/itercmp/dunder/__init__.py
14
+ tests/test_dunder_le.py
@@ -0,0 +1,466 @@
1
+ import unittest
2
+ from collections.abc import Generator
3
+ from typing import Any, Self
4
+
5
+ import itercmp.dunder
6
+
7
+ __all__ = ["TestDunderLe"]
8
+
9
+
10
+ class TestDunderLe(unittest.TestCase):
11
+ def gen_000(self: Self) -> Generator[tuple[Any, Any], None, None]:
12
+ class A:
13
+ def __eq__(self: Self, other: object, /) -> Any:
14
+ return False
15
+
16
+ def __le__(self: Self, other: object, /) -> Any:
17
+ return NotImplemented
18
+
19
+ class B:
20
+ def __eq__(self: Self, other: object, /) -> Any:
21
+ return False
22
+
23
+ def __ge__(self: Self, other: object, /) -> Any:
24
+ return False
25
+
26
+ yield (A(),), (B(),)
27
+
28
+ def gen_001(self: Self) -> Generator[tuple[Any, Any], None, None]:
29
+ class A:
30
+ def __eq__(self: Self, other: object, /) -> Any:
31
+ return False
32
+
33
+ def __le__(self: Self, other: object, /) -> Any:
34
+ return NotImplemented
35
+
36
+ class B:
37
+ def __eq__(self: Self, other: object, /) -> Any:
38
+ return False
39
+
40
+ def __ge__(self: Self, other: object, /) -> Any:
41
+ return True
42
+
43
+ yield (A(),), (B(),)
44
+
45
+ def gen_002(self: Self) -> Generator[tuple[Any, Any], None, None]:
46
+ # Identity short-circuit: the SAME object in both positions must be
47
+ # treated as equal even when __eq__ says otherwise (the nan case).
48
+ # __le__ returns False so a missing `is` check would give the wrong
49
+ # answer instead of falling through to the length comparison.
50
+ class A:
51
+ def __eq__(self: Self, other: object, /) -> Any:
52
+ return False
53
+
54
+ def __le__(self: Self, other: object, /) -> Any:
55
+ return False
56
+
57
+ a = A()
58
+ yield (a,), (a,) # identity -> equal -> 1 <= 1 -> True
59
+ yield (a, 1), (a, 2) # identity skips, then 1 <= 2 -> True
60
+ yield (a, 2), (a, 1) # identity skips, then 2 <= 1 -> False
61
+
62
+ def gen_003(self: Self) -> Generator[tuple[Any, Any], None, None]:
63
+ # Equal prefix -> result decided purely by length, every direction.
64
+ yield (1, 2), (1, 2, 3) # proper prefix, shorter -> True
65
+ yield (1, 2, 3), (1, 2) # proper prefix, longer -> False
66
+ yield (1, 2), (1, 2) # identical -> True
67
+ yield (), () # both empty -> True
68
+ yield (), (1,) # empty vs non-empty -> True
69
+ yield (1,), () # non-empty vs empty -> False
70
+
71
+ def gen_004(self: Self) -> Generator[tuple[Any, Any], None, None]:
72
+ # Only the first differing pair decides; trailing elements are ignored.
73
+ yield (1, 9, 9), (2, 0, 0) # 1 <= 2 -> True regardless of rest
74
+ yield (2, 0, 0), (1, 9, 9) # 2 <= 1 -> False regardless of rest
75
+ yield (1, 5), (1, 4, 100) # 5 <= 4 -> False even though lhs shorter
76
+
77
+ def gen_005(self: Self) -> Generator[tuple[Any, Any], None, None]:
78
+ # __eq__ may return a non-bool; only its *truthiness* counts.
79
+ class Truthy:
80
+ def __eq__(self: Self, other: object, /) -> Any:
81
+ return "non-empty" # truthy -> treated as equal
82
+
83
+ def __le__(self: Self, other: object, /) -> Any:
84
+ return False # must NOT be reached
85
+
86
+ class Falsy:
87
+ def __eq__(self: Self, other: object, /) -> Any:
88
+ return [] # falsy -> treated as different
89
+
90
+ def __le__(self: Self, other: object, /) -> Any:
91
+ return True
92
+
93
+ yield (Truthy(),), (Truthy(),) # equal -> 1 <= 1 -> True
94
+ yield (Falsy(),), (Falsy(),) # differ -> __le__ -> True
95
+
96
+ def gen_006(self: Self) -> Generator[tuple[Any, Any], None, None]:
97
+ # Reflected fallback: __le__ is absent, so a <= b uses b.__ge__(a).
98
+ class A:
99
+ def __eq__(self: Self, other: object, /) -> Any:
100
+ return False
101
+
102
+ class B:
103
+ def __eq__(self: Self, other: object, /) -> Any:
104
+ return False
105
+
106
+ def __ge__(self: Self, other: object, /) -> Any:
107
+ return True
108
+
109
+ yield (A(),), (B(),) # A() <= B() -> B().__ge__(A()) -> True
110
+
111
+ def gen_007(self: Self) -> Generator[tuple[Any, Any], None, None]:
112
+ # Cross-type numeric elements compared element-wise.
113
+ yield (1, 2.0), (1.0, 2) # all equal -> 2 <= 2 -> True
114
+ yield (True, 0), (1, False) # bool/int equality -> True
115
+ yield (1,), (1.5,) # 1 <= 1.5 -> True
116
+
117
+ def gen_008(self: Self) -> Generator[tuple[Any, Any], None, None]:
118
+ # Strings and nested sequences: the differing element uses its own <=.
119
+ yield ("a", "b"), ("a", "c")
120
+ yield ("abc",), ("abd",)
121
+ yield ((1, 2),), ((1, 3),)
122
+ yield ((1, 2), 9), ((1, 2), 8)
123
+
124
+ def gen_009(self: Self) -> Generator[tuple[Any, Any], None, None]:
125
+ # The motivating real-world case: nan.
126
+ nan = float("nan")
127
+ yield (nan,), (nan,) # same obj -> True
128
+ yield (float("nan"),), (float("nan"),) # distinct -> nan<=nan -> False
129
+ yield (1, nan), (1, nan) # same obj after equal prefix
130
+ yield (1, float("nan")), (
131
+ 1,
132
+ float("nan"),
133
+ ) # distinct after equal prefix
134
+
135
+ def gen_010(self: Self) -> Generator[tuple[Any, Any], None, None]:
136
+ # Both directions NotImplemented -> a <= b raises TypeError; the
137
+ # container __le__ must propagate it identically.
138
+ class A:
139
+ def __eq__(self: Self, other: object, /) -> Any:
140
+ return False
141
+
142
+ def __le__(self: Self, other: object, /) -> Any:
143
+ return NotImplemented
144
+
145
+ class B:
146
+ def __eq__(self: Self, other: object, /) -> Any:
147
+ return False
148
+
149
+ def __ge__(self: Self, other: object, /) -> Any:
150
+ return NotImplemented
151
+
152
+ yield (A(),), (B(),)
153
+
154
+ def gen_011(self: Self) -> Generator[tuple[Any, Any], None, None]:
155
+ # __eq__ raises during the equality scan. The exception must propagate
156
+ # identically (type, repr, str) instead of being swallowed. Distinct
157
+ # objects, so the identity short-circuit does not hide the call.
158
+ class A:
159
+ def __eq__(self: Self, other: object, /) -> Any:
160
+ raise ValueError("eq boom")
161
+
162
+ def __le__(self: Self, other: object, /) -> Any:
163
+ return True
164
+
165
+ yield (A(),), (A(),)
166
+ yield (1, A()), (1, A()) # raise only after an equal prefix
167
+
168
+ def gen_012(self: Self) -> Generator[tuple[Any, Any], None, None]:
169
+ # First pair is unequal, so the FINAL element comparison is reached and
170
+ # its __le__ raises -> must propagate identically.
171
+ class A:
172
+ def __eq__(self: Self, other: object, /) -> Any:
173
+ return False
174
+
175
+ def __le__(self: Self, other: object, /) -> Any:
176
+ raise RuntimeError("le boom")
177
+
178
+ yield (A(),), (A(),)
179
+
180
+ def gen_013(self: Self) -> Generator[tuple[Any, Any], None, None]:
181
+ # __eq__ returns an object whose __bool__ raises (the numpy-array
182
+ # "ambiguous truth value" case). Taking truthiness of the eq result
183
+ # must raise the same way PyObject_IsTrue does at the C level.
184
+ class Ambiguous:
185
+ def __bool__(self: Self, /) -> Any:
186
+ raise ValueError("ambiguous truth value")
187
+
188
+ class A:
189
+ def __eq__(self: Self, other: object, /) -> Any:
190
+ return Ambiguous()
191
+
192
+ def __le__(self: Self, other: object, /) -> Any:
193
+ return True
194
+
195
+ yield (A(),), (A(),)
196
+
197
+ def gen_014(self: Self) -> Generator[tuple[Any, Any], None, None]:
198
+ # Both sides' __eq__ return NotImplemented -> Python falls back to
199
+ # identity (False for distinct objects) -> the pair counts as DIFFERENT
200
+ # and the element __le__ decides. An unresolved __eq__ must not be
201
+ # mistaken for "equal".
202
+ class A:
203
+ def __eq__(self: Self, other: object, /) -> Any:
204
+ return NotImplemented
205
+
206
+ def __le__(self: Self, other: object, /) -> Any:
207
+ return True
208
+
209
+ yield (A(),), (A(),)
210
+
211
+ def gen_015(self: Self) -> Generator[tuple[Any, Any], None, None]:
212
+ # Identity short-circuit must skip a __eq__ that would otherwise raise,
213
+ # proving `is` is tested BEFORE `==`. Same object at the first and at a
214
+ # middle position.
215
+ class Boom:
216
+ def __eq__(self: Self, other: object, /) -> Any:
217
+ raise ValueError("must not be called")
218
+
219
+ def __le__(self: Self, other: object, /) -> Any:
220
+ return False
221
+
222
+ b = Boom()
223
+ yield (b,), (b,) # identity at pos 0 -> 1 <= 1 -> True
224
+ yield (1, b, 2), (1, b, 3) # identity mid, then 2 <= 3 -> True
225
+ yield (1, b, 9), (1, b, 8) # identity mid, then 9 <= 8 -> False
226
+
227
+ def gen_016(self: Self) -> Generator[tuple[Any, Any], None, None]:
228
+ # Neither __le__ nor __ge__ defined; __lt__ is present as a red herring
229
+ # (<= must NOT consult it) -> a <= b raises TypeError. Confirms the
230
+ # identical "not supported between instances" message on both paths.
231
+ class A:
232
+ def __eq__(self: Self, other: object, /) -> Any:
233
+ return False
234
+
235
+ def __lt__(self: Self, other: object, /) -> Any:
236
+ return True
237
+
238
+ class B:
239
+ def __eq__(self: Self, other: object, /) -> Any:
240
+ return False
241
+
242
+ yield (A(),), (B(),)
243
+
244
+ def gen_017(self: Self) -> Generator[tuple[Any, Any], None, None]:
245
+ # Long genuinely-equal prefix, divergence only at the last element.
246
+ yield (1, 2, 3, 4, 5), (1, 2, 3, 4, 6)
247
+ yield (1, 2, 3, 4, 6), (1, 2, 3, 4, 5)
248
+ yield (1, 2, 3, 4, 5), (1, 2, 3, 4, 5)
249
+
250
+ def gen_018(self: Self) -> Generator[tuple[Any, Any], None, None]:
251
+ # Nested sequences whose own <= / length comparison decides the result.
252
+ yield ((1, 2),), ((1, 2, 3),) # inner: proper prefix -> True
253
+ yield ((1, 2, 3),), ((1, 2),) # inner: longer -> False
254
+ yield ((1, 2), (3,)), ((1, 2), (3, 0))
255
+
256
+ def gen_019(self: Self) -> Generator[tuple[Any, Any], None, None]:
257
+ # Reflected fallback returning a NON-bool object must be preserved
258
+ # verbatim (the gen_900 idea, but reached through b.__ge__).
259
+ class A:
260
+ def __eq__(self: Self, other: object, /) -> Any:
261
+ return False
262
+
263
+ class B:
264
+ def __eq__(self: Self, other: object, /) -> Any:
265
+ return False
266
+
267
+ def __ge__(self: Self, other: object, /) -> Any:
268
+ return "reflected-yes"
269
+
270
+ yield (A(),), (B(),)
271
+
272
+ def gen_020(self: Self) -> Generator[tuple[Any, Any], None, None]:
273
+ # __eq__ truthiness resolved via __len__ rather than __bool__: empty
274
+ # result -> falsy -> different; non-empty -> truthy -> equal.
275
+ class WithLen:
276
+ def __init__(self: Self, n: int, /) -> None:
277
+ self.n = n
278
+
279
+ def __len__(self: Self, /) -> int:
280
+ return self.n
281
+
282
+ class Eq0:
283
+ def __eq__(self: Self, other: object, /) -> Any:
284
+ return WithLen(0) # falsy -> treated as different
285
+
286
+ def __le__(self: Self, other: object, /) -> Any:
287
+ return True
288
+
289
+ class Eq1:
290
+ def __eq__(self: Self, other: object, /) -> Any:
291
+ return WithLen(1) # truthy -> treated as equal
292
+
293
+ def __le__(self: Self, other: object, /) -> Any:
294
+ return False # must NOT be reached
295
+
296
+ yield (Eq0(),), (Eq0(),) # differ -> __le__ -> True
297
+ yield (Eq1(),), (Eq1(),) # equal -> 1 <= 1 -> True
298
+
299
+ def gen_021(self: Self) -> Generator[tuple[Any, Any], None, None]:
300
+ # Reflected fallback itself raises: a.__le__ defers (NotImplemented),
301
+ # then b.__ge__ raises -> exception propagates from the element compare.
302
+ class A:
303
+ def __eq__(self: Self, other: object, /) -> Any:
304
+ return False
305
+
306
+ def __le__(self: Self, other: object, /) -> Any:
307
+ return NotImplemented
308
+
309
+ class B:
310
+ def __eq__(self: Self, other: object, /) -> Any:
311
+ return False
312
+
313
+ def __ge__(self: Self, other: object, /) -> Any:
314
+ raise RuntimeError("ge boom")
315
+
316
+ yield (A(),), (B(),)
317
+
318
+ def gen_022(self: Self) -> Generator[tuple[Any, Any], None, None]:
319
+ # Identity short-circuit AFTER a real (==, not identity) equal prefix,
320
+ # combined with a distinct-nan tail; exercises is / == / length together.
321
+ nan = float("nan")
322
+ marker = object()
323
+ yield (1, marker, nan), (1, marker, nan) # same nan object -> True
324
+ yield (1, marker), (1, marker, 0) # equal prefix -> length -> True
325
+
326
+ def gen_900(self: Self) -> Generator[tuple[Any, Any], None, None]:
327
+ class A:
328
+ def __eq__(self: Self, other: object, /) -> Any:
329
+ return False
330
+
331
+ def __le__(self: Self, other: object, /) -> Any:
332
+ return 42
333
+
334
+ class B:
335
+ def __eq__(self: Self, other: object, /) -> Any:
336
+ return False
337
+
338
+ yield (A(),), (B(),)
339
+
340
+ def gen_901(self: Self) -> Generator[tuple[Any, Any], None, None]:
341
+ class A:
342
+ def __eq__(self: Self, other: object, /) -> Any:
343
+ return NotImplemented
344
+
345
+ def __le__(self: Self, other: object, /) -> Any:
346
+ return True
347
+
348
+ class B:
349
+ def __eq__(self: Self, other: object, /) -> Any:
350
+ return True
351
+
352
+ yield (A(),), (B(),)
353
+
354
+ def gen_902(self: Self) -> Generator[tuple[Any, Any], None, None]:
355
+ class A:
356
+ def __eq__(self: Self, other: object, /) -> Any:
357
+ return NotImplemented
358
+
359
+ def __le__(self: Self, other: object, /) -> Any:
360
+ return True
361
+
362
+ class B:
363
+ def __eq__(self: Self, other: object, /) -> Any:
364
+ return False
365
+
366
+ yield (A(),), (B(),)
367
+
368
+ def gen_903(self: Self) -> Generator[tuple[Any, Any], None, None]:
369
+ class A:
370
+ def __eq__(self: Self, other: object, /) -> Any:
371
+ return NotImplemented
372
+
373
+ def __le__(self: Self, other: object, /) -> Any:
374
+ return False
375
+
376
+ class B:
377
+ def __eq__(self: Self, other: object, /) -> Any:
378
+ return True
379
+
380
+ yield (A(),), (B(),)
381
+
382
+ def gen_904(self: Self) -> Generator[tuple[Any, Any], None, None]:
383
+ class A:
384
+ def __eq__(self: Self, other: object, /) -> Any:
385
+ return NotImplemented
386
+
387
+ def __le__(self: Self, other: object, /) -> Any:
388
+ return False
389
+
390
+ class B:
391
+ def __eq__(self: Self, other: object, /) -> Any:
392
+ return False
393
+
394
+ yield (A(),), (B(),)
395
+
396
+ def gen_905(self: Self) -> Generator[tuple[Any, Any], None, None]:
397
+ # Both __eq__ return NotImplemented (distinct objects) -> equality falls
398
+ # back to identity (False) -> different -> a <= b via reflected __ge__.
399
+ class A:
400
+ def __eq__(self: Self, other: object, /) -> Any:
401
+ return NotImplemented
402
+
403
+ class B:
404
+ def __eq__(self: Self, other: object, /) -> Any:
405
+ return NotImplemented
406
+
407
+ def __ge__(self: Self, other: object, /) -> Any:
408
+ return True
409
+
410
+ yield (A(),), (B(),)
411
+
412
+ def go(self: Self, c: Any, x: Any, y: Any, /) -> tuple[Any, Any]:
413
+ try:
414
+ return c(x, y), None
415
+ except Exception as exc:
416
+ return None, exc
417
+
418
+ def test_0_000(self: Self) -> None:
419
+ gens: list[Any]
420
+ t: type[Any]
421
+ x: Any
422
+ x_: Any
423
+ y: Any
424
+ y_: Any
425
+ gens = [
426
+ self.gen_000,
427
+ self.gen_001,
428
+ self.gen_002,
429
+ self.gen_003,
430
+ self.gen_004,
431
+ self.gen_005,
432
+ self.gen_006,
433
+ self.gen_007,
434
+ self.gen_008,
435
+ self.gen_009,
436
+ self.gen_010,
437
+ self.gen_011,
438
+ self.gen_012,
439
+ self.gen_013,
440
+ self.gen_014,
441
+ self.gen_015,
442
+ self.gen_016,
443
+ self.gen_017,
444
+ self.gen_018,
445
+ self.gen_019,
446
+ self.gen_020,
447
+ self.gen_021,
448
+ self.gen_022,
449
+ self.gen_900,
450
+ self.gen_901,
451
+ self.gen_902,
452
+ self.gen_903,
453
+ self.gen_904,
454
+ self.gen_905,
455
+ ]
456
+ for gen in gens:
457
+ for x, y in gen():
458
+ for t in (tuple, list):
459
+ x_ = t(x)
460
+ y_ = t(y)
461
+ ans_old, exc_old = self.go(t.__le__, x_, y_)
462
+ ans_new, exc_new = self.go(itercmp.dunder.le, x_, y_)
463
+ self.assertEqual(ans_old, ans_new)
464
+ self.assertEqual(repr(exc_old), repr(exc_new))
465
+ self.assertEqual(str(exc_old), str(exc_new))
466
+ self.assertIs(type(exc_old), type(exc_new))
File without changes
File without changes
File without changes
File without changes