dycw-utilities 0.146.2__py3-none-any.whl → 0.178.1__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.

Potentially problematic release.


This version of dycw-utilities might be problematic. Click here for more details.

Files changed (89) hide show
  1. dycw_utilities-0.178.1.dist-info/METADATA +34 -0
  2. dycw_utilities-0.178.1.dist-info/RECORD +105 -0
  3. dycw_utilities-0.178.1.dist-info/WHEEL +4 -0
  4. {dycw_utilities-0.146.2.dist-info → dycw_utilities-0.178.1.dist-info}/entry_points.txt +1 -0
  5. utilities/__init__.py +1 -1
  6. utilities/altair.py +10 -7
  7. utilities/asyncio.py +129 -50
  8. utilities/atomicwrites.py +1 -1
  9. utilities/atools.py +64 -4
  10. utilities/cachetools.py +9 -6
  11. utilities/click.py +144 -49
  12. utilities/concurrent.py +1 -1
  13. utilities/contextlib.py +4 -2
  14. utilities/contextvars.py +20 -1
  15. utilities/cryptography.py +3 -3
  16. utilities/dataclasses.py +15 -28
  17. utilities/docker.py +387 -0
  18. utilities/enum.py +2 -2
  19. utilities/errors.py +17 -3
  20. utilities/fastapi.py +8 -3
  21. utilities/fpdf2.py +2 -2
  22. utilities/functions.py +20 -297
  23. utilities/git.py +19 -0
  24. utilities/grp.py +28 -0
  25. utilities/hypothesis.py +361 -79
  26. utilities/importlib.py +17 -1
  27. utilities/inflect.py +1 -1
  28. utilities/iterables.py +33 -58
  29. utilities/jinja2.py +148 -0
  30. utilities/json.py +1 -1
  31. utilities/libcst.py +7 -7
  32. utilities/logging.py +131 -93
  33. utilities/math.py +8 -4
  34. utilities/more_itertools.py +4 -6
  35. utilities/operator.py +1 -1
  36. utilities/orjson.py +86 -34
  37. utilities/os.py +49 -2
  38. utilities/packaging.py +115 -0
  39. utilities/parse.py +2 -2
  40. utilities/pathlib.py +66 -34
  41. utilities/permissions.py +298 -0
  42. utilities/platform.py +5 -4
  43. utilities/polars.py +934 -420
  44. utilities/polars_ols.py +1 -1
  45. utilities/postgres.py +317 -153
  46. utilities/pottery.py +10 -86
  47. utilities/pqdm.py +3 -3
  48. utilities/pwd.py +28 -0
  49. utilities/pydantic.py +4 -51
  50. utilities/pydantic_settings.py +240 -0
  51. utilities/pydantic_settings_sops.py +76 -0
  52. utilities/pyinstrument.py +5 -5
  53. utilities/pytest.py +100 -126
  54. utilities/pytest_plugins/pytest_randomly.py +1 -1
  55. utilities/pytest_plugins/pytest_regressions.py +7 -3
  56. utilities/pytest_regressions.py +27 -8
  57. utilities/random.py +11 -6
  58. utilities/re.py +1 -1
  59. utilities/redis.py +101 -64
  60. utilities/sentinel.py +10 -0
  61. utilities/shelve.py +4 -1
  62. utilities/shutil.py +25 -0
  63. utilities/slack_sdk.py +9 -4
  64. utilities/sqlalchemy.py +422 -352
  65. utilities/sqlalchemy_polars.py +28 -52
  66. utilities/string.py +1 -1
  67. utilities/subprocess.py +1977 -0
  68. utilities/tempfile.py +112 -4
  69. utilities/testbook.py +50 -0
  70. utilities/text.py +174 -42
  71. utilities/throttle.py +158 -0
  72. utilities/timer.py +2 -2
  73. utilities/traceback.py +59 -38
  74. utilities/types.py +68 -22
  75. utilities/typing.py +479 -19
  76. utilities/uuid.py +42 -5
  77. utilities/version.py +27 -26
  78. utilities/whenever.py +663 -178
  79. utilities/zoneinfo.py +80 -22
  80. dycw_utilities-0.146.2.dist-info/METADATA +0 -41
  81. dycw_utilities-0.146.2.dist-info/RECORD +0 -99
  82. dycw_utilities-0.146.2.dist-info/WHEEL +0 -4
  83. dycw_utilities-0.146.2.dist-info/licenses/LICENSE +0 -21
  84. utilities/aiolimiter.py +0 -25
  85. utilities/eventkit.py +0 -388
  86. utilities/period.py +0 -237
  87. utilities/python_dotenv.py +0 -101
  88. utilities/streamlit.py +0 -105
  89. utilities/typed_settings.py +0 -144
utilities/hypothesis.py CHANGED
@@ -32,6 +32,7 @@ from hypothesis.strategies import (
32
32
  sampled_from,
33
33
  sets,
34
34
  text,
35
+ timezones,
35
36
  uuids,
36
37
  )
37
38
  from hypothesis.utils.conventions import not_set
@@ -50,26 +51,35 @@ from whenever import (
50
51
  ZonedDateTime,
51
52
  )
52
53
 
53
- from utilities.functions import ensure_int, ensure_str
54
+ from utilities.functions import ensure_int, ensure_str, max_nullable, min_nullable
54
55
  from utilities.math import (
55
56
  MAX_FLOAT32,
56
57
  MAX_FLOAT64,
58
+ MAX_INT8,
59
+ MAX_INT16,
57
60
  MAX_INT32,
58
61
  MAX_INT64,
62
+ MAX_UINT8,
63
+ MAX_UINT16,
59
64
  MAX_UINT32,
60
65
  MAX_UINT64,
61
66
  MIN_FLOAT32,
62
67
  MIN_FLOAT64,
68
+ MIN_INT8,
69
+ MIN_INT16,
63
70
  MIN_INT32,
64
71
  MIN_INT64,
72
+ MIN_UINT8,
73
+ MIN_UINT16,
65
74
  MIN_UINT32,
66
75
  MIN_UINT64,
67
76
  is_zero,
68
77
  )
69
78
  from utilities.os import get_env_var
70
79
  from utilities.pathlib import module_path, temp_cwd
71
- from utilities.platform import IS_WINDOWS
72
- from utilities.sentinel import Sentinel, sentinel
80
+ from utilities.permissions import Permissions
81
+ from utilities.platform import IS_LINUX
82
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
73
83
  from utilities.tempfile import TEMP_DIR, TemporaryDirectory
74
84
  from utilities.version import Version
75
85
  from utilities.whenever import (
@@ -86,21 +96,27 @@ from utilities.whenever import (
86
96
  DAY,
87
97
  TIME_DELTA_MAX,
88
98
  TIME_DELTA_MIN,
99
+ DatePeriod,
100
+ TimePeriod,
101
+ ZonedDateTimePeriod,
102
+ get_now,
89
103
  to_date_time_delta,
90
104
  to_days,
91
105
  to_nanoseconds,
92
106
  )
93
- from utilities.zoneinfo import UTC, ensure_time_zone
107
+ from utilities.zoneinfo import UTC, to_zone_info
94
108
 
95
109
  if TYPE_CHECKING:
96
110
  from collections.abc import Collection, Hashable, Iterable, Iterator
111
+ from zoneinfo import ZoneInfo
97
112
 
98
113
  from hypothesis.database import ExampleDatabase
99
114
  from libcst import Import, ImportFrom
100
115
  from numpy.random import RandomState
116
+ from sqlalchemy import URL
101
117
 
102
118
  from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
103
- from utilities.types import Number, TimeZoneLike
119
+ from utilities.types import Number, TimeZone, TimeZoneLike
104
120
 
105
121
 
106
122
  type MaybeSearchStrategy[_T] = _T | SearchStrategy[_T]
@@ -175,14 +191,14 @@ def date_deltas(
175
191
  min_value_ = DATE_DELTA_MIN
176
192
  case DateDelta():
177
193
  ...
178
- case _ as never:
194
+ case never:
179
195
  assert_never(never)
180
196
  match max_value_:
181
197
  case None:
182
198
  max_value_ = DATE_DELTA_MAX
183
199
  case DateDelta():
184
200
  ...
185
- case _ as never:
201
+ case never:
186
202
  assert_never(never)
187
203
  min_days = to_days(min_value_)
188
204
  max_days = to_days(max_value_)
@@ -196,6 +212,26 @@ def date_deltas(
196
212
  ##
197
213
 
198
214
 
215
+ @composite
216
+ def date_periods(
217
+ draw: DrawFn,
218
+ /,
219
+ *,
220
+ min_value: MaybeSearchStrategy[Date | None] = None,
221
+ max_value: MaybeSearchStrategy[Date | None] = None,
222
+ two_digit: MaybeSearchStrategy[bool] = False,
223
+ ) -> DatePeriod:
224
+ """Strategy for generating date periods."""
225
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
226
+ two_digit_ = draw2(draw, two_digit)
227
+ strategy = dates(min_value=min_value_, max_value=max_value_, two_digit=two_digit_)
228
+ start, end = draw(pairs(strategy, sorted=True))
229
+ return DatePeriod(start, end)
230
+
231
+
232
+ ##
233
+
234
+
199
235
  @composite
200
236
  def date_time_deltas(
201
237
  draw: DrawFn,
@@ -204,6 +240,7 @@ def date_time_deltas(
204
240
  min_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
205
241
  max_value: MaybeSearchStrategy[DateTimeDelta | None] = None,
206
242
  parsable: MaybeSearchStrategy[bool] = False,
243
+ nativable: MaybeSearchStrategy[bool] = False,
207
244
  ) -> DateTimeDelta:
208
245
  """Strategy for generating date deltas."""
209
246
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
@@ -212,20 +249,26 @@ def date_time_deltas(
212
249
  min_value_ = DATE_TIME_DELTA_MIN
213
250
  case DateTimeDelta():
214
251
  ...
215
- case _ as never:
252
+ case never:
216
253
  assert_never(never)
217
254
  match max_value_:
218
255
  case None:
219
256
  max_value_ = DATE_TIME_DELTA_MAX
220
257
  case DateTimeDelta():
221
258
  ...
222
- case _ as never:
259
+ case never:
223
260
  assert_never(never)
224
261
  min_nanos, max_nanos = map(to_nanoseconds, [min_value_, max_value_])
225
262
  if draw2(draw, parsable):
226
263
  min_nanos = max(min_nanos, to_nanoseconds(DATE_TIME_DELTA_PARSABLE_MIN))
227
264
  max_nanos = min(max_nanos, to_nanoseconds(DATE_TIME_DELTA_PARSABLE_MAX))
228
- nanos = draw(integers(min_value=min_nanos, max_value=max_nanos))
265
+ if draw2(draw, nativable):
266
+ min_micros, _ = divmod(min_nanos, 1000)
267
+ max_micros, _ = divmod(max_nanos, 1000)
268
+ micros = draw(integers(min_value=min_micros + 1, max_value=max_micros))
269
+ nanos = 1000 * micros
270
+ else:
271
+ nanos = draw(integers(min_value=min_nanos, max_value=max_nanos))
229
272
  return to_date_time_delta(nanos)
230
273
 
231
274
 
@@ -248,14 +291,14 @@ def dates(
248
291
  min_value_ = Date.MIN
249
292
  case Date():
250
293
  ...
251
- case _ as never:
294
+ case never:
252
295
  assert_never(never)
253
296
  match max_value_:
254
297
  case None:
255
298
  max_value_ = Date.MAX
256
299
  case Date():
257
300
  ...
258
- case _ as never:
301
+ case never:
259
302
  assert_never(never)
260
303
  if draw2(draw, two_digit):
261
304
  min_value_ = max(min_value_, DATE_TWO_DIGIT_YEAR_MIN)
@@ -324,21 +367,21 @@ def draw2[T](
324
367
  return value
325
368
  case None, SearchStrategy(), False:
326
369
  value2 = draw(default)
327
- if isinstance(value2, Sentinel):
370
+ if is_sentinel(value2):
328
371
  raise _Draw2DefaultGeneratedSentinelError
329
372
  return value2
330
373
  case Sentinel(), None, _:
331
374
  raise _Draw2InputResolvedToSentinelError
332
375
  case Sentinel(), SearchStrategy(), True:
333
376
  value2 = draw(default)
334
- if isinstance(value2, Sentinel):
377
+ if is_sentinel(value2):
335
378
  raise _Draw2DefaultGeneratedSentinelError
336
379
  return value2
337
380
  case Sentinel(), SearchStrategy(), False:
338
381
  raise _Draw2InputResolvedToSentinelError
339
382
  case _, _, _:
340
383
  return value
341
- case _ as never:
384
+ case never:
342
385
  assert_never(never)
343
386
 
344
387
 
@@ -368,16 +411,27 @@ def float32s(
368
411
  draw: DrawFn,
369
412
  /,
370
413
  *,
371
- min_value: MaybeSearchStrategy[float] = MIN_FLOAT32,
372
- max_value: MaybeSearchStrategy[float] = MAX_FLOAT32,
414
+ min_value: MaybeSearchStrategy[float | None] = None,
415
+ max_value: MaybeSearchStrategy[float | None] = None,
416
+ exclude_min: MaybeSearchStrategy[bool] = False,
417
+ exclude_max: MaybeSearchStrategy[bool] = False,
373
418
  ) -> float:
374
419
  """Strategy for generating float32s."""
375
420
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
376
- min_value_ = max(min_value_, MIN_FLOAT32)
377
- max_value_ = min(max_value_, MAX_FLOAT32)
421
+ min_value_ = max_nullable([min_value_, MIN_FLOAT32])
422
+ max_value_ = min_nullable([max_value_, MAX_FLOAT32])
378
423
  if is_zero(min_value_) and is_zero(max_value_):
379
424
  min_value_ = max_value_ = 0.0
380
- return draw(floats(min_value_, max_value_, width=32))
425
+ exclude_min_, exclude_max_ = [draw2(draw, e) for e in [exclude_min, exclude_max]]
426
+ return draw(
427
+ floats(
428
+ min_value_,
429
+ max_value_,
430
+ width=32,
431
+ exclude_min=exclude_min_,
432
+ exclude_max=exclude_max_,
433
+ )
434
+ )
381
435
 
382
436
 
383
437
  @composite
@@ -385,16 +439,27 @@ def float64s(
385
439
  draw: DrawFn,
386
440
  /,
387
441
  *,
388
- min_value: MaybeSearchStrategy[float] = MIN_FLOAT64,
389
- max_value: MaybeSearchStrategy[float] = MAX_FLOAT64,
442
+ min_value: MaybeSearchStrategy[float | None] = None,
443
+ max_value: MaybeSearchStrategy[float | None] = None,
444
+ exclude_min: MaybeSearchStrategy[bool] = False,
445
+ exclude_max: MaybeSearchStrategy[bool] = False,
390
446
  ) -> float:
391
447
  """Strategy for generating float64s."""
392
448
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
393
- min_value_ = max(min_value_, MIN_FLOAT64)
394
- max_value_ = min(max_value_, MAX_FLOAT64)
449
+ min_value_ = max_nullable([min_value_, MIN_FLOAT64])
450
+ max_value_ = min_nullable([max_value_, MAX_FLOAT64])
395
451
  if is_zero(min_value_) and is_zero(max_value_):
396
452
  min_value_ = max_value_ = 0.0
397
- return draw(floats(min_value_, max_value_, width=64))
453
+ exclude_min_, exclude_max_ = [draw2(draw, e) for e in [exclude_min, exclude_max]]
454
+ return draw(
455
+ floats(
456
+ min_value_,
457
+ max_value_,
458
+ width=64,
459
+ exclude_min=exclude_min_,
460
+ exclude_max=exclude_max_,
461
+ )
462
+ )
398
463
 
399
464
 
400
465
  ##
@@ -596,18 +661,48 @@ def int_arrays(
596
661
  ##
597
662
 
598
663
 
664
+ @composite
665
+ def int8s(
666
+ draw: DrawFn,
667
+ /,
668
+ *,
669
+ min_value: MaybeSearchStrategy[int | None] = None,
670
+ max_value: MaybeSearchStrategy[int | None] = None,
671
+ ) -> int:
672
+ """Strategy for generating int8s."""
673
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
674
+ min_value_ = max_nullable([min_value_, MIN_INT8])
675
+ max_value_ = min_nullable([max_value_, MAX_INT8])
676
+ return draw(integers(min_value=min_value_, max_value=max_value_))
677
+
678
+
679
+ @composite
680
+ def int16s(
681
+ draw: DrawFn,
682
+ /,
683
+ *,
684
+ min_value: MaybeSearchStrategy[int | None] = None,
685
+ max_value: MaybeSearchStrategy[int | None] = None,
686
+ ) -> int:
687
+ """Strategy for generating int16s."""
688
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
689
+ min_value_ = max_nullable([min_value_, MIN_INT16])
690
+ max_value_ = min_nullable([max_value_, MAX_INT16])
691
+ return draw(integers(min_value=min_value_, max_value=max_value_))
692
+
693
+
599
694
  @composite
600
695
  def int32s(
601
696
  draw: DrawFn,
602
697
  /,
603
698
  *,
604
- min_value: MaybeSearchStrategy[int] = MIN_INT32,
605
- max_value: MaybeSearchStrategy[int] = MAX_INT32,
699
+ min_value: MaybeSearchStrategy[int | None] = None,
700
+ max_value: MaybeSearchStrategy[int | None] = None,
606
701
  ) -> int:
607
702
  """Strategy for generating int32s."""
608
703
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
609
- min_value_ = max(min_value_, MIN_INT32)
610
- max_value_ = min(max_value_, MAX_INT32)
704
+ min_value_ = max_nullable([min_value_, MIN_INT32])
705
+ max_value_ = min_nullable([max_value_, MAX_INT32])
611
706
  return draw(integers(min_value_, max_value_))
612
707
 
613
708
 
@@ -616,13 +711,13 @@ def int64s(
616
711
  draw: DrawFn,
617
712
  /,
618
713
  *,
619
- min_value: MaybeSearchStrategy[int] = MIN_INT64,
620
- max_value: MaybeSearchStrategy[int] = MAX_INT64,
714
+ min_value: MaybeSearchStrategy[int | None] = None,
715
+ max_value: MaybeSearchStrategy[int | None] = None,
621
716
  ) -> int:
622
717
  """Strategy for generating int64s."""
623
718
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
624
- min_value_ = max(min_value_, MIN_INT64)
625
- max_value_ = min(max_value_, MAX_INT64)
719
+ min_value_ = max_nullable([min_value_, MIN_INT64])
720
+ max_value_ = min_nullable([max_value_, MAX_INT64])
626
721
  return draw(integers(min_value_, max_value_))
627
722
 
628
723
 
@@ -667,14 +762,14 @@ def month_days(
667
762
  min_value_ = MonthDay.MIN
668
763
  case MonthDay():
669
764
  ...
670
- case _ as never:
765
+ case never:
671
766
  assert_never(never)
672
767
  match max_value_:
673
768
  case None:
674
769
  max_value_ = MonthDay.MAX
675
770
  case MonthDay():
676
771
  ...
677
- case _ as never:
772
+ case never:
678
773
  assert_never(never)
679
774
  min_date, max_date = [m.in_year(2000) for m in [min_value_, max_value_]]
680
775
  date = draw(dates(min_value=min_date, max_value=max_date))
@@ -753,20 +848,16 @@ def paths(
753
848
  ) -> Path:
754
849
  """Strategy for generating `Path`s."""
755
850
  min_depth_, max_depth_ = [draw2(draw, d) for d in [min_depth, max_depth]]
756
- parts = draw(
757
- lists(
758
- _path_parts(),
759
- min_size=0 if min_depth_ is None else min_depth_,
760
- max_size=max_depth_,
761
- )
762
- )
851
+ min_depth_ = max_nullable([min_depth_, 0])
852
+ max_depth_ = min_nullable([max_depth_, 10])
853
+ parts = draw(lists(_path_parts(), min_size=min_depth_, max_size=max_depth_))
763
854
  return Path(*parts)
764
855
 
765
856
 
766
857
  @composite
767
858
  def _path_parts(draw: DrawFn, /) -> str:
768
859
  part = draw(text_ascii(min_size=1, max_size=10))
769
- reserved = {"AUX", "NUL", "nuL"}
860
+ reserved = {"AUX", "NUL", "nuL", "pRn"}
770
861
  _ = assume(part not in reserved)
771
862
  return part
772
863
 
@@ -775,7 +866,39 @@ def _path_parts(draw: DrawFn, /) -> str:
775
866
 
776
867
 
777
868
  @composite
778
- def plain_datetimes(
869
+ def permissions(
870
+ draw: DrawFn,
871
+ /,
872
+ *,
873
+ user_read: MaybeSearchStrategy[bool | None] = None,
874
+ user_write: MaybeSearchStrategy[bool | None] = None,
875
+ user_execute: MaybeSearchStrategy[bool | None] = None,
876
+ group_read: MaybeSearchStrategy[bool | None] = None,
877
+ group_write: MaybeSearchStrategy[bool | None] = None,
878
+ group_execute: MaybeSearchStrategy[bool | None] = None,
879
+ others_read: MaybeSearchStrategy[bool | None] = None,
880
+ others_write: MaybeSearchStrategy[bool | None] = None,
881
+ others_execute: MaybeSearchStrategy[bool | None] = None,
882
+ ) -> Permissions:
883
+ """Strategy for generating `Permissions`."""
884
+ return Permissions(
885
+ user_read=draw2(draw, user_read, booleans()),
886
+ user_write=draw2(draw, user_write, booleans()),
887
+ user_execute=draw2(draw, user_execute, booleans()),
888
+ group_read=draw2(draw, group_read, booleans()),
889
+ group_write=draw2(draw, group_write, booleans()),
890
+ group_execute=draw2(draw, group_execute, booleans()),
891
+ others_read=draw2(draw, others_read, booleans()),
892
+ others_write=draw2(draw, others_write, booleans()),
893
+ others_execute=draw2(draw, others_execute, booleans()),
894
+ )
895
+
896
+
897
+ ##
898
+
899
+
900
+ @composite
901
+ def plain_date_times(
779
902
  draw: DrawFn,
780
903
  /,
781
904
  *,
@@ -789,14 +912,14 @@ def plain_datetimes(
789
912
  min_value_ = PlainDateTime.MIN
790
913
  case PlainDateTime():
791
914
  ...
792
- case _ as never:
915
+ case never:
793
916
  assert_never(never)
794
917
  match max_value_:
795
918
  case None:
796
919
  max_value_ = PlainDateTime.MAX
797
920
  case PlainDateTime():
798
921
  ...
799
- case _ as never:
922
+ case never:
800
923
  assert_never(never)
801
924
  py_datetime = draw(
802
925
  datetimes(
@@ -828,6 +951,27 @@ def py_datetimes(
828
951
  ##
829
952
 
830
953
 
954
+ def quadruples[T](
955
+ strategy: SearchStrategy[T],
956
+ /,
957
+ *,
958
+ unique: MaybeSearchStrategy[bool] = False,
959
+ sorted: MaybeSearchStrategy[bool] = False, # noqa: A002
960
+ ) -> SearchStrategy[tuple[T, T, T, T]]:
961
+ """Strategy for generating quadruples of elements."""
962
+ return lists_fixed_length(strategy, 4, unique=unique, sorted=sorted).map(
963
+ _quadruples_map
964
+ )
965
+
966
+
967
+ def _quadruples_map[T](elements: list[T], /) -> tuple[T, T, T, T]:
968
+ first, second, third, fourth = elements
969
+ return first, second, third, fourth
970
+
971
+
972
+ ##
973
+
974
+
831
975
  @composite
832
976
  def random_states(
833
977
  draw: DrawFn, /, *, seed: MaybeSearchStrategy[int | None] = None
@@ -835,7 +979,7 @@ def random_states(
835
979
  """Strategy for generating `numpy` random states."""
836
980
  from numpy.random import RandomState
837
981
 
838
- seed_ = draw2(draw, seed, integers(0, MAX_UINT32))
982
+ seed_ = draw2(draw, seed, uint32s())
839
983
  return RandomState(seed=seed_)
840
984
 
841
985
 
@@ -882,7 +1026,7 @@ def setup_hypothesis_profiles(
882
1026
  return 100
883
1027
  case Profile.ci:
884
1028
  return 1000
885
- case _ as never:
1029
+ case never:
886
1030
  assert_never(never)
887
1031
 
888
1032
  @property
@@ -892,11 +1036,11 @@ def setup_hypothesis_profiles(
892
1036
  return Verbosity.quiet
893
1037
  case Profile.ci:
894
1038
  return Verbosity.verbose
895
- case _ as never:
1039
+ case never:
896
1040
  assert_never(never)
897
1041
 
898
1042
  phases = {Phase.explicit, Phase.reuse, Phase.generate, Phase.target}
899
- if "HYPOTHESIS_NO_SHRINK" not in environ:
1043
+ if "HYPOTHESIS_NO_SHRINK" not in environ: # pragma: no cover
900
1044
  phases.add(Phase.shrink)
901
1045
  for profile in Profile:
902
1046
  try:
@@ -1017,9 +1161,7 @@ def temp_dirs(draw: DrawFn, /) -> TemporaryDirectory:
1017
1161
  """Search strategy for temporary directories."""
1018
1162
  _TEMP_DIR_HYPOTHESIS.mkdir(exist_ok=True)
1019
1163
  uuid = draw(uuids())
1020
- return TemporaryDirectory(
1021
- prefix=f"{uuid}__", dir=_TEMP_DIR_HYPOTHESIS, ignore_cleanup_errors=IS_WINDOWS
1022
- )
1164
+ return TemporaryDirectory(prefix=f"{uuid}__", dir=_TEMP_DIR_HYPOTHESIS)
1023
1165
 
1024
1166
 
1025
1167
  ##
@@ -1149,14 +1291,14 @@ def time_deltas(
1149
1291
  min_value_ = TIME_DELTA_MIN
1150
1292
  case TimeDelta():
1151
1293
  ...
1152
- case _ as never:
1294
+ case never:
1153
1295
  assert_never(never)
1154
1296
  match max_value_:
1155
1297
  case None:
1156
1298
  max_value_ = TIME_DELTA_MAX
1157
1299
  case TimeDelta():
1158
1300
  ...
1159
- case _ as never:
1301
+ case never:
1160
1302
  assert_never(never)
1161
1303
  py_time = draw(
1162
1304
  hypothesis.strategies.timedeltas(
@@ -1169,6 +1311,24 @@ def time_deltas(
1169
1311
  ##
1170
1312
 
1171
1313
 
1314
+ @composite
1315
+ def time_periods(
1316
+ draw: DrawFn,
1317
+ /,
1318
+ *,
1319
+ min_value: MaybeSearchStrategy[Time | None] = None,
1320
+ max_value: MaybeSearchStrategy[Time | None] = None,
1321
+ ) -> TimePeriod:
1322
+ """Strategy for generating time periods."""
1323
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1324
+ strategy = times(min_value=min_value_, max_value=max_value_)
1325
+ start, end = draw(pairs(strategy, sorted=True))
1326
+ return TimePeriod(start, end)
1327
+
1328
+
1329
+ ##
1330
+
1331
+
1172
1332
  @composite
1173
1333
  def times(
1174
1334
  draw: DrawFn,
@@ -1181,17 +1341,17 @@ def times(
1181
1341
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1182
1342
  match min_value_:
1183
1343
  case None:
1184
- min_value_ = Time()
1344
+ min_value_ = Time.MIN
1185
1345
  case Time():
1186
1346
  ...
1187
- case _ as never:
1347
+ case never:
1188
1348
  assert_never(never)
1189
1349
  match max_value_:
1190
1350
  case None:
1191
1351
  max_value_ = Time.MAX
1192
1352
  case Time():
1193
1353
  ...
1194
- case _ as never:
1354
+ case never:
1195
1355
  assert_never(never)
1196
1356
  py_time = draw(
1197
1357
  hypothesis.strategies.times(
@@ -1225,18 +1385,48 @@ def _triples_map[T](elements: list[T], /) -> tuple[T, T, T]:
1225
1385
  ##
1226
1386
 
1227
1387
 
1388
+ @composite
1389
+ def uint8s(
1390
+ draw: DrawFn,
1391
+ /,
1392
+ *,
1393
+ min_value: MaybeSearchStrategy[int | None] = None,
1394
+ max_value: MaybeSearchStrategy[int | None] = None,
1395
+ ) -> int:
1396
+ """Strategy for generating uint8s."""
1397
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1398
+ min_value_ = max_nullable([min_value_, MIN_UINT8])
1399
+ max_value_ = min_nullable([max_value_, MAX_UINT8])
1400
+ return draw(integers(min_value=min_value_, max_value=max_value_))
1401
+
1402
+
1403
+ @composite
1404
+ def uint16s(
1405
+ draw: DrawFn,
1406
+ /,
1407
+ *,
1408
+ min_value: MaybeSearchStrategy[int | None] = None,
1409
+ max_value: MaybeSearchStrategy[int | None] = None,
1410
+ ) -> int:
1411
+ """Strategy for generating uint16s."""
1412
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1413
+ min_value_ = max_nullable([min_value_, MIN_UINT16])
1414
+ max_value_ = min_nullable([max_value_, MAX_UINT16])
1415
+ return draw(integers(min_value=min_value_, max_value=max_value_))
1416
+
1417
+
1228
1418
  @composite
1229
1419
  def uint32s(
1230
1420
  draw: DrawFn,
1231
1421
  /,
1232
1422
  *,
1233
- min_value: MaybeSearchStrategy[int] = MIN_UINT32,
1234
- max_value: MaybeSearchStrategy[int] = MAX_UINT32,
1423
+ min_value: MaybeSearchStrategy[int | None] = None,
1424
+ max_value: MaybeSearchStrategy[int | None] = None,
1235
1425
  ) -> int:
1236
1426
  """Strategy for generating uint32s."""
1237
1427
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1238
- min_value_ = max(min_value_, MIN_UINT32)
1239
- max_value_ = min(max_value_, MAX_UINT32)
1428
+ min_value_ = max_nullable([min_value_, MIN_UINT32])
1429
+ max_value_ = min_nullable([max_value_, MAX_UINT32])
1240
1430
  return draw(integers(min_value=min_value_, max_value=max_value_))
1241
1431
 
1242
1432
 
@@ -1245,19 +1435,54 @@ def uint64s(
1245
1435
  draw: DrawFn,
1246
1436
  /,
1247
1437
  *,
1248
- min_value: MaybeSearchStrategy[int] = MIN_UINT64,
1249
- max_value: MaybeSearchStrategy[int] = MAX_UINT64,
1438
+ min_value: MaybeSearchStrategy[int | None] = None,
1439
+ max_value: MaybeSearchStrategy[int | None] = None,
1250
1440
  ) -> int:
1251
1441
  """Strategy for generating uint64s."""
1252
1442
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1253
- min_value_ = max(min_value_, MIN_UINT64)
1254
- max_value_ = min(max_value_, MAX_UINT64)
1443
+ min_value_ = max_nullable([min_value_, MIN_UINT64])
1444
+ max_value_ = min_nullable([max_value_, MAX_UINT64])
1255
1445
  return draw(integers(min_value=min_value_, max_value=max_value_))
1256
1446
 
1257
1447
 
1258
1448
  ##
1259
1449
 
1260
1450
 
1451
+ @composite
1452
+ def urls(
1453
+ draw: DrawFn,
1454
+ /,
1455
+ *,
1456
+ all_: MaybeSearchStrategy[bool] = False,
1457
+ username: MaybeSearchStrategy[bool] = False,
1458
+ password: MaybeSearchStrategy[bool] = False,
1459
+ host: MaybeSearchStrategy[bool] = False,
1460
+ port: MaybeSearchStrategy[bool] = False,
1461
+ database: MaybeSearchStrategy[bool] = False,
1462
+ ) -> URL:
1463
+ from sqlalchemy import URL
1464
+
1465
+ have_all, have_username, have_password, have_host, have_port, have_database = [
1466
+ draw2(draw, b) for b in [all_, username, password, host, port, database]
1467
+ ]
1468
+ username_use = draw(text_ascii(min_size=1)) if have_all or have_username else None
1469
+ password_use = draw(text_ascii(min_size=1)) if have_all or have_password else None
1470
+ host_use = draw(text_ascii(min_size=1)) if have_all or have_host else None
1471
+ port_use = draw(integers(min_value=1)) if have_all or have_port else None
1472
+ database_use = draw(text_ascii(min_size=1)) if have_all or have_database else None
1473
+ return URL.create(
1474
+ drivername="sqlite",
1475
+ username=username_use,
1476
+ password=password_use,
1477
+ host=host_use,
1478
+ port=port_use,
1479
+ database=database_use,
1480
+ )
1481
+
1482
+
1483
+ ##
1484
+
1485
+
1261
1486
  @composite
1262
1487
  def versions(draw: DrawFn, /, *, suffix: MaybeSearchStrategy[bool] = False) -> Version:
1263
1488
  """Strategy for generating versions."""
@@ -1286,14 +1511,14 @@ def year_months(
1286
1511
  min_value_ = YearMonth.MIN
1287
1512
  case YearMonth():
1288
1513
  ...
1289
- case _ as never:
1514
+ case never:
1290
1515
  assert_never(never)
1291
1516
  match max_value_:
1292
1517
  case None:
1293
1518
  max_value_ = YearMonth.MAX
1294
1519
  case YearMonth():
1295
1520
  ...
1296
- case _ as never:
1521
+ case never:
1297
1522
  assert_never(never)
1298
1523
  min_date, max_date = [m.on_day(1) for m in [min_value_, max_value_]]
1299
1524
  date = draw(dates(min_value=min_date, max_value=max_date, two_digit=two_digit))
@@ -1304,7 +1529,48 @@ def year_months(
1304
1529
 
1305
1530
 
1306
1531
  @composite
1307
- def zoned_datetimes(
1532
+ def zone_infos(draw: DrawFn, /) -> ZoneInfo:
1533
+ """Strategy for generating time-zones."""
1534
+ time_zone = draw(timezones())
1535
+ if IS_LINUX: # skipif-not-linux
1536
+ _ = assume(time_zone.key not in _LINUX_DISALLOW_TIME_ZONES)
1537
+ with assume_does_not_raise(TimeZoneNotFoundError):
1538
+ _ = get_now(time_zone)
1539
+ return time_zone
1540
+
1541
+
1542
+ _LINUX_DISALLOW_TIME_ZONES: set[TimeZone | Literal["localtime"]] = {
1543
+ "Etc/UTC",
1544
+ "localtime",
1545
+ }
1546
+
1547
+ ##
1548
+
1549
+
1550
+ @composite
1551
+ def zoned_date_time_periods(
1552
+ draw: DrawFn,
1553
+ /,
1554
+ *,
1555
+ min_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
1556
+ max_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
1557
+ time_zone: MaybeSearchStrategy[TimeZoneLike] = UTC,
1558
+ ) -> ZonedDateTimePeriod:
1559
+ """Strategy for generating zoned date-time periods."""
1560
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1561
+ time_zone_: TimeZoneLike = draw2(draw, time_zone)
1562
+ strategy = zoned_date_times(
1563
+ min_value=min_value_, max_value=max_value_, time_zone=time_zone_
1564
+ )
1565
+ start, end = draw(pairs(strategy, sorted=True))
1566
+ return ZonedDateTimePeriod(start, end)
1567
+
1568
+
1569
+ ##
1570
+
1571
+
1572
+ @composite
1573
+ def zoned_date_times(
1308
1574
  draw: DrawFn,
1309
1575
  /,
1310
1576
  *,
@@ -1312,16 +1578,16 @@ def zoned_datetimes(
1312
1578
  max_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
1313
1579
  time_zone: MaybeSearchStrategy[TimeZoneLike] = UTC,
1314
1580
  ) -> ZonedDateTime:
1315
- """Strategy for generating zoned datetimes."""
1581
+ """Strategy for generating zoned date-times."""
1316
1582
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1317
- time_zone_ = ensure_time_zone(draw2(draw, time_zone))
1583
+ time_zone_ = to_zone_info(draw2(draw, time_zone))
1318
1584
  match min_value_:
1319
1585
  case None | PlainDateTime():
1320
1586
  ...
1321
1587
  case ZonedDateTime():
1322
1588
  with assume_does_not_raise(ValueError):
1323
1589
  min_value_ = min_value_.to_tz(time_zone_.key).to_plain()
1324
- case _ as never:
1590
+ case never:
1325
1591
  assert_never(never)
1326
1592
  match max_value_:
1327
1593
  case None | PlainDateTime():
@@ -1329,22 +1595,26 @@ def zoned_datetimes(
1329
1595
  case ZonedDateTime():
1330
1596
  with assume_does_not_raise(ValueError):
1331
1597
  max_value_ = max_value_.to_tz(time_zone_.key).to_plain()
1332
- case _ as never:
1598
+ case never:
1333
1599
  assert_never(never)
1334
- plain = draw(plain_datetimes(min_value=min_value_, max_value=max_value_))
1600
+ plain = draw(plain_date_times(min_value=min_value_, max_value=max_value_))
1335
1601
  with (
1336
1602
  assume_does_not_raise(RepeatedTime),
1337
1603
  assume_does_not_raise(SkippedTime),
1338
- assume_does_not_raise(TimeZoneNotFoundError),
1339
- assume_does_not_raise(ValueError, match="Resulting datetime is out of range"),
1604
+ assume_does_not_raise(ValueError, match=r"Resulting time is out of range"),
1340
1605
  ):
1341
1606
  zoned = plain.assume_tz(time_zone_.key, disambiguate="raise")
1342
- with assume_does_not_raise(OverflowError, match="date value out of range"):
1607
+ with assume_does_not_raise(OverflowError, match=r"date value out of range"):
1343
1608
  if not ((Date.MIN + DAY) <= zoned.date() <= (Date.MAX - DAY)):
1344
1609
  _ = zoned.py_datetime()
1345
1610
  return zoned
1346
1611
 
1347
1612
 
1613
+ zoned_date_times_2000 = zoned_date_times(
1614
+ min_value=ZonedDateTime(2000, 1, 1, tz=UTC.key),
1615
+ max_value=ZonedDateTime(2000, 12, 31, tz=UTC.key),
1616
+ )
1617
+
1348
1618
  __all__ = [
1349
1619
  "Draw2Error",
1350
1620
  "MaybeSearchStrategy",
@@ -1352,6 +1622,7 @@ __all__ = [
1352
1622
  "assume_does_not_raise",
1353
1623
  "bool_arrays",
1354
1624
  "date_deltas",
1625
+ "date_periods",
1355
1626
  "date_time_deltas",
1356
1627
  "dates",
1357
1628
  "draw2",
@@ -1363,6 +1634,8 @@ __all__ = [
1363
1634
  "hashables",
1364
1635
  "import_froms",
1365
1636
  "imports",
1637
+ "int8s",
1638
+ "int16s",
1366
1639
  "int32s",
1367
1640
  "int64s",
1368
1641
  "int_arrays",
@@ -1371,8 +1644,10 @@ __all__ = [
1371
1644
  "numbers",
1372
1645
  "pairs",
1373
1646
  "paths",
1374
- "plain_datetimes",
1647
+ "permissions",
1648
+ "plain_date_times",
1375
1649
  "py_datetimes",
1650
+ "quadruples",
1376
1651
  "random_states",
1377
1652
  "sentinels",
1378
1653
  "sets_fixed_length",
@@ -1388,11 +1663,18 @@ __all__ = [
1388
1663
  "text_digits",
1389
1664
  "text_printable",
1390
1665
  "time_deltas",
1666
+ "time_periods",
1391
1667
  "times",
1392
1668
  "triples",
1669
+ "uint8s",
1670
+ "uint16s",
1393
1671
  "uint32s",
1394
1672
  "uint64s",
1673
+ "urls",
1395
1674
  "versions",
1396
1675
  "year_months",
1397
- "zoned_datetimes",
1676
+ "zone_infos",
1677
+ "zoned_date_time_periods",
1678
+ "zoned_date_times",
1679
+ "zoned_date_times_2000",
1398
1680
  ]