assertpy2 2.0.0__py3-none-any.whl

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.
assertpy2/numeric.py ADDED
@@ -0,0 +1,555 @@
1
+ # Copyright (c) 2015-2019, Activision Publishing, Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # 3. Neither the name of the copyright holder nor the names of its contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+ from __future__ import annotations
30
+
31
+ import datetime
32
+ import math
33
+ import numbers
34
+ from typing import TYPE_CHECKING
35
+
36
+ if TYPE_CHECKING:
37
+ from typing_extensions import Self
38
+
39
+ __tracebackhide__ = True
40
+
41
+
42
+ class NumericMixin:
43
+ """Numeric assertions mixin."""
44
+
45
+ _NUMERIC_COMPAREABLE = frozenset({datetime.datetime, datetime.timedelta, datetime.date, datetime.time})
46
+ _NUMERIC_NON_COMPAREABLE = frozenset({complex})
47
+
48
+ def _validate_compareable(self, other):
49
+ self_type = type(self.val)
50
+ other_type = type(other)
51
+
52
+ if self_type in self._NUMERIC_NON_COMPAREABLE:
53
+ raise TypeError("ordering is not defined for type <%s>" % self_type.__name__)
54
+ if self_type in self._NUMERIC_COMPAREABLE:
55
+ if other_type is not self_type:
56
+ raise TypeError("given arg must be <%s>, but was <%s>" % (self_type.__name__, other_type.__name__))
57
+ return
58
+ if isinstance(self.val, numbers.Number):
59
+ if not isinstance(other, numbers.Number):
60
+ raise TypeError("given arg must be a number, but was <%s>" % other_type.__name__)
61
+ return
62
+ try:
63
+ _ = self.val < other
64
+ except TypeError:
65
+ raise TypeError("ordering is not defined for type <%s>" % self_type.__name__) from None
66
+
67
+ def _validate_number(self):
68
+ """Raise TypeError if val is not numeric."""
69
+ if isinstance(self.val, numbers.Number) is False:
70
+ raise TypeError("val is not numeric")
71
+
72
+ def _validate_real(self):
73
+ """Raise TypeError if val is not real number."""
74
+ if isinstance(self.val, numbers.Real) is False:
75
+ raise TypeError("val is not real number")
76
+
77
+ def is_zero(self) -> Self:
78
+ """Asserts that val is numeric and is zero.
79
+
80
+ Examples:
81
+ Usage::
82
+
83
+ assert_that(0).is_zero()
84
+
85
+ Returns:
86
+ AssertionBuilder: returns this instance to chain to the next assertion
87
+
88
+ Raises:
89
+ AssertionError: if val is **not** zero
90
+ """
91
+ self._validate_number()
92
+ return self.is_equal_to(0)
93
+
94
+ def is_not_zero(self) -> Self:
95
+ """Asserts that val is numeric and is *not* zero.
96
+
97
+ Examples:
98
+ Usage::
99
+
100
+ assert_that(1).is_not_zero()
101
+ assert_that(123.4).is_not_zero()
102
+
103
+ Returns:
104
+ AssertionBuilder: returns this instance to chain to the next assertion
105
+
106
+ Raises:
107
+ AssertionError: if val **is** zero
108
+ """
109
+ self._validate_number()
110
+ return self.is_not_equal_to(0)
111
+
112
+ def is_nan(self) -> Self:
113
+ """Asserts that val is real number and is ``NaN`` (not a number).
114
+
115
+ Examples:
116
+ Usage::
117
+
118
+ assert_that(float('nan')).is_nan()
119
+ assert_that(float('inf') * 0).is_nan()
120
+
121
+ Returns:
122
+ AssertionBuilder: returns this instance to chain to the next assertion
123
+
124
+ Raises:
125
+ AssertionError: if val is **not** NaN
126
+ """
127
+ self._validate_number()
128
+ self._validate_real()
129
+ if not math.isnan(self.val):
130
+ return self.error("Expected <%s> to be <NaN>, but was not." % self.val)
131
+ return self
132
+
133
+ def is_not_nan(self) -> Self:
134
+ """Asserts that val is real number and is *not* ``NaN`` (not a number).
135
+
136
+ Examples:
137
+ Usage::
138
+
139
+ assert_that(0).is_not_nan()
140
+ assert_that(123.4).is_not_nan()
141
+ assert_that(float('inf')).is_not_nan()
142
+
143
+ Returns:
144
+ AssertionBuilder: returns this instance to chain to the next assertion
145
+
146
+ Raises:
147
+ AssertionError: if val **is** NaN
148
+ """
149
+ self._validate_number()
150
+ self._validate_real()
151
+ if math.isnan(self.val):
152
+ return self.error("Expected not <NaN>, but was.")
153
+ return self
154
+
155
+ def is_inf(self) -> Self:
156
+ """Asserts that val is real number and is ``Inf`` (infinity).
157
+
158
+ Examples:
159
+ Usage::
160
+
161
+ assert_that(float('inf')).is_inf()
162
+ assert_that(float('inf') * 1).is_inf()
163
+
164
+ Returns:
165
+ AssertionBuilder: returns this instance to chain to the next assertion
166
+
167
+ Raises:
168
+ AssertionError: if val is **not** Inf
169
+ """
170
+ self._validate_number()
171
+ self._validate_real()
172
+ if not math.isinf(self.val):
173
+ return self.error("Expected <%s> to be <Inf>, but was not." % self.val)
174
+ return self
175
+
176
+ def is_not_inf(self) -> Self:
177
+ """Asserts that val is real number and is *not* ``Inf`` (infinity).
178
+
179
+ Examples:
180
+ Usage::
181
+
182
+ assert_that(0).is_not_inf()
183
+ assert_that(123.4).is_not_inf()
184
+ assert_that(float('nan')).is_not_inf()
185
+
186
+ Returns:
187
+ AssertionBuilder: returns this instance to chain to the next assertion
188
+
189
+ Raises:
190
+ AssertionError: if val **is** Inf
191
+ """
192
+ self._validate_number()
193
+ self._validate_real()
194
+ if math.isinf(self.val):
195
+ return self.error("Expected not <Inf>, but was.")
196
+ return self
197
+
198
+ def is_greater_than(self, other) -> Self:
199
+ """Asserts that val is numeric and is greater than other.
200
+
201
+ Args:
202
+ other: the other date, expected to be less than val
203
+
204
+ Examples:
205
+ Usage::
206
+
207
+ assert_that(1).is_greater_than(0)
208
+ assert_that(123.4).is_greater_than(111.1)
209
+
210
+ For dates, behavior is identical to :meth:`~assertpy.date.DateMixin.is_after`::
211
+
212
+ import datetime
213
+
214
+ today = datetime.datetime.now()
215
+ yesterday = today - datetime.timedelta(days=1)
216
+
217
+ assert_that(today).is_greater_than(yesterday)
218
+
219
+ Returns:
220
+ AssertionBuilder: returns this instance to chain to the next assertion
221
+
222
+ Raises:
223
+ AssertionError: if val is **not** greater than other
224
+ """
225
+ self._validate_compareable(other)
226
+ if self.val <= other:
227
+ if type(self.val) is datetime.datetime:
228
+ return self.error(
229
+ "Expected <%s> to be greater than <%s>, but was not."
230
+ % (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
231
+ )
232
+ else:
233
+ return self.error("Expected <%s> to be greater than <%s>, but was not." % (self.val, other))
234
+ return self
235
+
236
+ def is_greater_than_or_equal_to(self, other) -> Self:
237
+ """Asserts that val is numeric and is greater than or equal to other.
238
+
239
+ Args:
240
+ other: the other date, expected to be less than or equal to val
241
+
242
+ Examples:
243
+ Usage::
244
+
245
+ assert_that(1).is_greater_than_or_equal_to(0)
246
+ assert_that(1).is_greater_than_or_equal_to(1)
247
+ assert_that(123.4).is_greater_than_or_equal_to(111.1)
248
+
249
+ For dates, behavior is identical to :meth:`~assertpy.date.DateMixin.is_after` *except* when equal::
250
+
251
+ import datetime
252
+
253
+ today = datetime.datetime.now()
254
+ yesterday = today - datetime.timedelta(days=1)
255
+
256
+ assert_that(today).is_greater_than_or_equal_to(yesterday)
257
+ assert_that(today).is_greater_than_or_equal_to(today)
258
+
259
+ Returns:
260
+ AssertionBuilder: returns this instance to chain to the next assertion
261
+
262
+ Raises:
263
+ AssertionError: if val is **not** greater than or equal to other
264
+ """
265
+ self._validate_compareable(other)
266
+ if self.val < other:
267
+ if type(self.val) is datetime.datetime:
268
+ return self.error(
269
+ "Expected <%s> to be greater than or equal to <%s>, but was not."
270
+ % (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
271
+ )
272
+ else:
273
+ return self.error("Expected <%s> to be greater than or equal to <%s>, but was not." % (self.val, other))
274
+ return self
275
+
276
+ def is_less_than(self, other) -> Self:
277
+ """Asserts that val is numeric and is less than other.
278
+
279
+ Args:
280
+ other: the other date, expected to be greater than val
281
+
282
+ Examples:
283
+ Usage::
284
+
285
+ assert_that(0).is_less_than(1)
286
+ assert_that(123.4).is_less_than(555.5)
287
+
288
+ For dates, behavior is identical to :meth:`~assertpy.date.DateMixin.is_before`::
289
+
290
+ import datetime
291
+
292
+ today = datetime.datetime.now()
293
+ yesterday = today - datetime.timedelta(days=1)
294
+
295
+ assert_that(yesterday).is_less_than(today)
296
+
297
+ Returns:
298
+ AssertionBuilder: returns this instance to chain to the next assertion
299
+
300
+ Raises:
301
+ AssertionError: if val is **not** less than other
302
+ """
303
+ self._validate_compareable(other)
304
+ if self.val >= other:
305
+ if type(self.val) is datetime.datetime:
306
+ return self.error(
307
+ "Expected <%s> to be less than <%s>, but was not."
308
+ % (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
309
+ )
310
+ else:
311
+ return self.error("Expected <%s> to be less than <%s>, but was not." % (self.val, other))
312
+ return self
313
+
314
+ def is_less_than_or_equal_to(self, other) -> Self:
315
+ """Asserts that val is numeric and is less than or equal to other.
316
+
317
+ Args:
318
+ other: the other date, expected to be greater than or equal to val
319
+
320
+ Examples:
321
+ Usage::
322
+
323
+ assert_that(1).is_less_than_or_equal_to(0)
324
+ assert_that(1).is_less_than_or_equal_to(1)
325
+ assert_that(123.4).is_less_than_or_equal_to(100.0)
326
+
327
+ For dates, behavior is identical to :meth:`~assertpy.date.DateMixin.is_before` *except* when equal::
328
+
329
+ import datetime
330
+
331
+ today = datetime.datetime.now()
332
+ yesterday = today - datetime.timedelta(days=1)
333
+
334
+ assert_that(yesterday).is_less_than_or_equal_to(today)
335
+ assert_that(today).is_less_than_or_equal_to(today)
336
+
337
+ Returns:
338
+ AssertionBuilder: returns this instance to chain to the next assertion
339
+
340
+ Raises:
341
+ AssertionError: if val is **not** less than or equal to other
342
+ """
343
+ self._validate_compareable(other)
344
+ if self.val > other:
345
+ if type(self.val) is datetime.datetime:
346
+ return self.error(
347
+ "Expected <%s> to be less than or equal to <%s>, but was not."
348
+ % (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"))
349
+ )
350
+ else:
351
+ return self.error("Expected <%s> to be less than or equal to <%s>, but was not." % (self.val, other))
352
+ return self
353
+
354
+ def is_positive(self) -> Self:
355
+ """Asserts that val is numeric and is greater than zero.
356
+
357
+ Examples:
358
+ Usage::
359
+
360
+ assert_that(1).is_positive()
361
+ assert_that(123.4).is_positive()
362
+
363
+ Returns:
364
+ AssertionBuilder: returns this instance to chain to the next assertion
365
+
366
+ Raises:
367
+ AssertionError: if val is **not** positive
368
+ """
369
+ return self.is_greater_than(0)
370
+
371
+ def is_negative(self) -> Self:
372
+ """Asserts that val is numeric and is less than zero.
373
+
374
+ Examples:
375
+ Usage::
376
+
377
+ assert_that(-1).is_negative()
378
+ assert_that(-123.4).is_negative()
379
+
380
+ Returns:
381
+ AssertionBuilder: returns this instance to chain to the next assertion
382
+
383
+ Raises:
384
+ AssertionError: if val is **not** negative
385
+ """
386
+ return self.is_less_than(0)
387
+
388
+ def is_between(self, low, high) -> Self:
389
+ """Asserts that val is numeric and is between low and high.
390
+
391
+ Args:
392
+ low: the low value
393
+ high: the high value
394
+
395
+ Examples:
396
+ Usage::
397
+
398
+ assert_that(1).is_between(0, 2)
399
+ assert_that(123.4).is_between(111.1, 222.2)
400
+
401
+ For dates, works as expected::
402
+
403
+ import datetime
404
+
405
+ today = datetime.datetime.now()
406
+ middle = today - datetime.timedelta(hours=12)
407
+ yesterday = today - datetime.timedelta(days=1)
408
+
409
+ assert_that(middle).is_between(yesterday, today)
410
+
411
+ Returns:
412
+ AssertionBuilder: returns this instance to chain to the next assertion
413
+
414
+ Raises:
415
+ AssertionError: if val is **not** between low and high
416
+ """
417
+ val_type = type(self.val)
418
+ self._validate_between_args(val_type, low, high)
419
+
420
+ if self.val < low or self.val > high:
421
+ if val_type is datetime.datetime:
422
+ return self.error(
423
+ "Expected <%s> to be between <%s> and <%s>, but was not."
424
+ % (
425
+ self.val.strftime("%Y-%m-%d %H:%M:%S"),
426
+ low.strftime("%Y-%m-%d %H:%M:%S"),
427
+ high.strftime("%Y-%m-%d %H:%M:%S"),
428
+ )
429
+ )
430
+ else:
431
+ return self.error("Expected <%s> to be between <%s> and <%s>, but was not." % (self.val, low, high))
432
+ return self
433
+
434
+ def is_not_between(self, low, high) -> Self:
435
+ """Asserts that val is numeric and is *not* between low and high.
436
+
437
+ Args:
438
+ low: the low value
439
+ high: the high value
440
+
441
+ Examples:
442
+ Usage::
443
+
444
+ assert_that(1).is_not_between(2, 3)
445
+ assert_that(1.1).is_not_between(2.2, 3.3)
446
+
447
+ Returns:
448
+ AssertionBuilder: returns this instance to chain to the next assertion
449
+
450
+ Raises:
451
+ AssertionError: if val **is** between low and high
452
+ """
453
+ val_type = type(self.val)
454
+ self._validate_between_args(val_type, low, high)
455
+
456
+ if self.val >= low and self.val <= high:
457
+ if val_type is datetime.datetime:
458
+ return self.error(
459
+ "Expected <%s> to not be between <%s> and <%s>, but was."
460
+ % (
461
+ self.val.strftime("%Y-%m-%d %H:%M:%S"),
462
+ low.strftime("%Y-%m-%d %H:%M:%S"),
463
+ high.strftime("%Y-%m-%d %H:%M:%S"),
464
+ )
465
+ )
466
+ else:
467
+ return self.error("Expected <%s> to not be between <%s> and <%s>, but was." % (self.val, low, high))
468
+ return self
469
+
470
+ def is_close_to(self, other, tolerance) -> Self:
471
+ """Asserts that val is numeric and is close to other within tolerance.
472
+
473
+ Args:
474
+ other: the other value, expected to be close to val within tolerance
475
+ tolerance: the tolerance
476
+
477
+ Examples:
478
+ Usage::
479
+
480
+ assert_that(123).is_close_to(100, 25)
481
+ assert_that(123.4).is_close_to(123, 0.5)
482
+
483
+ For dates, works as expected::
484
+
485
+ import datetime
486
+
487
+ today = datetime.datetime.now()
488
+ yesterday = today - datetime.timedelta(days=1)
489
+
490
+ assert_that(today).is_close_to(yesterday, datetime.timedelta(hours=36))
491
+
492
+ Returns:
493
+ AssertionBuilder: returns this instance to chain to the next assertion
494
+
495
+ Raises:
496
+ AssertionError: if val is **not** close to other within tolerance
497
+ """
498
+ self._validate_close_to_args(self.val, other, tolerance)
499
+
500
+ if type(self.val) is not datetime.datetime and (math.isnan(self.val) or math.isnan(other)):
501
+ return self.error(
502
+ "Expected <%s> to be close to <%s> within tolerance <%s>, but was not." % (self.val, other, tolerance)
503
+ )
504
+ if self.val < (other - tolerance) or self.val > (other + tolerance):
505
+ if type(self.val) is datetime.datetime:
506
+ tolerance_seconds = tolerance.days * 86400 + tolerance.seconds + tolerance.microseconds / 1000000
507
+ h, rem = divmod(tolerance_seconds, 3600)
508
+ m, s = divmod(rem, 60)
509
+ return self.error(
510
+ "Expected <%s> to be close to <%s> within tolerance <%d:%02d:%02d>, but was not."
511
+ % (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"), h, m, s)
512
+ )
513
+ else:
514
+ return self.error(
515
+ "Expected <%s> to be close to <%s> within tolerance <%s>, but was not."
516
+ % (self.val, other, tolerance)
517
+ )
518
+ return self
519
+
520
+ def is_not_close_to(self, other, tolerance) -> Self:
521
+ """Asserts that val is numeric and is *not* close to other within tolerance.
522
+
523
+ Args:
524
+ other: the other value
525
+ tolerance: the tolerance
526
+
527
+ Examples:
528
+ Usage::
529
+
530
+ assert_that(123).is_not_close_to(100, 22)
531
+ assert_that(123.4).is_not_close_to(123, 0.1)
532
+
533
+ Returns:
534
+ AssertionBuilder: returns this instance to chain to the next assertion
535
+
536
+ Raises:
537
+ AssertionError: if val **is** close to other within tolerance
538
+ """
539
+ self._validate_close_to_args(self.val, other, tolerance)
540
+
541
+ if self.val >= (other - tolerance) and self.val <= (other + tolerance):
542
+ if type(self.val) is datetime.datetime:
543
+ tolerance_seconds = tolerance.days * 86400 + tolerance.seconds + tolerance.microseconds / 1000000
544
+ h, rem = divmod(tolerance_seconds, 3600)
545
+ m, s = divmod(rem, 60)
546
+ return self.error(
547
+ "Expected <%s> to not be close to <%s> within tolerance <%d:%02d:%02d>, but was."
548
+ % (self.val.strftime("%Y-%m-%d %H:%M:%S"), other.strftime("%Y-%m-%d %H:%M:%S"), h, m, s)
549
+ )
550
+ else:
551
+ return self.error(
552
+ "Expected <%s> to not be close to <%s> within tolerance <%s>, but was."
553
+ % (self.val, other, tolerance)
554
+ )
555
+ return self
assertpy2/py.typed ADDED
File without changes