csu 2.6.6__tar.gz → 2.6.7__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.
Files changed (108) hide show
  1. {csu-2.6.6 → csu-2.6.7}/.bumpversion.cfg +1 -1
  2. {csu-2.6.6 → csu-2.6.7}/.cookiecutterrc +1 -1
  3. {csu-2.6.6 → csu-2.6.7}/PKG-INFO +3 -3
  4. {csu-2.6.6 → csu-2.6.7}/README.rst +2 -2
  5. {csu-2.6.6 → csu-2.6.7}/pyproject.toml +1 -1
  6. {csu-2.6.6 → csu-2.6.7}/src/csu/__init__.py +1 -1
  7. {csu-2.6.6 → csu-2.6.7}/src/csu/exceptions.py +37 -3
  8. {csu-2.6.6 → csu-2.6.7}/src/csu/service.py +3 -6
  9. {csu-2.6.6 → csu-2.6.7}/src/csu.egg-info/PKG-INFO +3 -3
  10. {csu-2.6.6 → csu-2.6.7}/tests/test_exceptions.py +14 -3
  11. {csu-2.6.6 → csu-2.6.7}/tests/test_service.py +64 -52
  12. {csu-2.6.6 → csu-2.6.7}/tests/test_service_auth.py +12 -12
  13. {csu-2.6.6 → csu-2.6.7}/tests/test_views.py +8 -8
  14. {csu-2.6.6 → csu-2.6.7}/.coveragerc +0 -0
  15. {csu-2.6.6 → csu-2.6.7}/.editorconfig +0 -0
  16. {csu-2.6.6 → csu-2.6.7}/.github/workflows/github-actions.yml +0 -0
  17. {csu-2.6.6 → csu-2.6.7}/.pre-commit-config.yaml +0 -0
  18. {csu-2.6.6 → csu-2.6.7}/.tombi.toml +0 -0
  19. {csu-2.6.6 → csu-2.6.7}/AUTHORS.rst +0 -0
  20. {csu-2.6.6 → csu-2.6.7}/CHANGELOG.rst +0 -0
  21. {csu-2.6.6 → csu-2.6.7}/CONTRIBUTING.rst +0 -0
  22. {csu-2.6.6 → csu-2.6.7}/LICENSE +0 -0
  23. {csu-2.6.6 → csu-2.6.7}/MANIFEST.in +0 -0
  24. {csu-2.6.6 → csu-2.6.7}/ci/bootstrap.py +0 -0
  25. {csu-2.6.6 → csu-2.6.7}/ci/requirements.txt +0 -0
  26. {csu-2.6.6 → csu-2.6.7}/ci/templates/.github/workflows/github-actions.yml +0 -0
  27. {csu-2.6.6 → csu-2.6.7}/pytest.ini +0 -0
  28. {csu-2.6.6 → csu-2.6.7}/setup.cfg +0 -0
  29. {csu-2.6.6 → csu-2.6.7}/src/csu/admin.py +0 -0
  30. {csu-2.6.6 → csu-2.6.7}/src/csu/conf.py +0 -0
  31. {csu-2.6.6 → csu-2.6.7}/src/csu/consts.py +0 -0
  32. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/__init__.py +0 -0
  33. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/auth.py +0 -0
  34. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/fields.py +0 -0
  35. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/forms.py +0 -0
  36. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/perms.py +0 -0
  37. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/phonenumber.py +0 -0
  38. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/serializers.py +0 -0
  39. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/test_auth.py +0 -0
  40. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/test_fields.py +0 -0
  41. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/test_forms.py +0 -0
  42. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/test_phonenumber.py +0 -0
  43. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/test_serializers.py +0 -0
  44. {csu-2.6.6 → csu-2.6.7}/src/csu/drf/views.py +0 -0
  45. {csu-2.6.6 → csu-2.6.7}/src/csu/enums.py +0 -0
  46. {csu-2.6.6 → csu-2.6.7}/src/csu/env.py +0 -0
  47. {csu-2.6.6 → csu-2.6.7}/src/csu/export.py +0 -0
  48. {csu-2.6.6 → csu-2.6.7}/src/csu/fixups.py +0 -0
  49. {csu-2.6.6 → csu-2.6.7}/src/csu/forms/__init__.py +0 -0
  50. {csu-2.6.6 → csu-2.6.7}/src/csu/forms/crispy.py +0 -0
  51. {csu-2.6.6 → csu-2.6.7}/src/csu/forms/fields.py +0 -0
  52. {csu-2.6.6 → csu-2.6.7}/src/csu/gettext.py +0 -0
  53. {csu-2.6.6 → csu-2.6.7}/src/csu/gettext_lazy.py +0 -0
  54. {csu-2.6.6 → csu-2.6.7}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
  55. {csu-2.6.6 → csu-2.6.7}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
  56. {csu-2.6.6 → csu-2.6.7}/src/csu/logging.py +0 -0
  57. {csu-2.6.6 → csu-2.6.7}/src/csu/management.py +0 -0
  58. {csu-2.6.6 → csu-2.6.7}/src/csu/models.py +0 -0
  59. {csu-2.6.6 → csu-2.6.7}/src/csu/query.py +0 -0
  60. {csu-2.6.6 → csu-2.6.7}/src/csu/routers.py +0 -0
  61. {csu-2.6.6 → csu-2.6.7}/src/csu/templates/api_exception.html +0 -0
  62. {csu-2.6.6 → csu-2.6.7}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
  63. {csu-2.6.6 → csu-2.6.7}/src/csu/test_consts.py +0 -0
  64. {csu-2.6.6 → csu-2.6.7}/src/csu/test_env.py +0 -0
  65. {csu-2.6.6 → csu-2.6.7}/src/csu/test_management.py +0 -0
  66. {csu-2.6.6 → csu-2.6.7}/src/csu/test_service.py +0 -0
  67. {csu-2.6.6 → csu-2.6.7}/src/csu/test_timezones.py +0 -0
  68. {csu-2.6.6 → csu-2.6.7}/src/csu/test_utils.py +0 -0
  69. {csu-2.6.6 → csu-2.6.7}/src/csu/test_xml.py +0 -0
  70. {csu-2.6.6 → csu-2.6.7}/src/csu/timezones.py +0 -0
  71. {csu-2.6.6 → csu-2.6.7}/src/csu/utils.py +0 -0
  72. {csu-2.6.6 → csu-2.6.7}/src/csu/views.py +0 -0
  73. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/__init__.py +0 -0
  74. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/admin.py +0 -0
  75. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/asgi.py +0 -0
  76. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/engine.py +0 -0
  77. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/enums.py +0 -0
  78. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/job.py +0 -0
  79. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/models.py +0 -0
  80. {csu-2.6.6 → csu-2.6.7}/src/csu/worker/registry.py +0 -0
  81. {csu-2.6.6 → csu-2.6.7}/src/csu/wsgi.py +0 -0
  82. {csu-2.6.6 → csu-2.6.7}/src/csu/xml.py +0 -0
  83. {csu-2.6.6 → csu-2.6.7}/src/csu.egg-info/SOURCES.txt +0 -0
  84. {csu-2.6.6 → csu-2.6.7}/src/csu.egg-info/dependency_links.txt +0 -0
  85. {csu-2.6.6 → csu-2.6.7}/src/csu.egg-info/requires.txt +0 -0
  86. {csu-2.6.6 → csu-2.6.7}/src/csu.egg-info/top_level.txt +0 -0
  87. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
  88. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
  89. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service/test_decoding.yaml +0 -0
  90. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service/test_error.yaml +0 -0
  91. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service/test_logging.yaml +0 -0
  92. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service/test_redirects.yaml +0 -0
  93. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
  94. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service_auth/test_async_auth.yaml +0 -0
  95. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
  96. {csu-2.6.6 → csu-2.6.7}/tests/cassettes/test_service_auth/test_sync_auth.yaml +0 -0
  97. {csu-2.6.6 → csu-2.6.7}/tests/conftest.py +0 -0
  98. {csu-2.6.6 → csu-2.6.7}/tests/exampleworker.py +0 -0
  99. {csu-2.6.6 → csu-2.6.7}/tests/test_exampleworker.py +0 -0
  100. {csu-2.6.6 → csu-2.6.7}/tests/test_logging.py +0 -0
  101. {csu-2.6.6 → csu-2.6.7}/tests/test_models.py +0 -0
  102. {csu-2.6.6 → csu-2.6.7}/tests/test_worker.py +0 -0
  103. {csu-2.6.6 → csu-2.6.7}/tests/testproject/__init__.py +0 -0
  104. {csu-2.6.6 → csu-2.6.7}/tests/testproject/fixtures/testuser.json +0 -0
  105. {csu-2.6.6 → csu-2.6.7}/tests/testproject/models.py +0 -0
  106. {csu-2.6.6 → csu-2.6.7}/tests/testproject/settings.py +0 -0
  107. {csu-2.6.6 → csu-2.6.7}/tests/testproject/urls.py +0 -0
  108. {csu-2.6.6 → csu-2.6.7}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.6.6
2
+ current_version = 2.6.7
3
3
  commit = True
4
4
  tag = True
5
5
 
@@ -41,7 +41,7 @@ default_context:
41
41
  sphinx_theme: furo
42
42
  test_matrix_separate_coverage: false
43
43
  tests_inside_package: true
44
- version: 2.6.6
44
+ version: 2.6.7
45
45
  version_manager: bump2version
46
46
  website: https://blog.ionelmc.ro
47
47
  year_from: '2024'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csu
3
- Version: 2.6.6
3
+ Version: 2.6.7
4
4
  Summary: Clean Slate Utils - bunch of utility code, mostly Django/DRF specific.
5
5
  Author-email: Ionel Cristian Mărieș <contact@ionelmc.ro>
6
6
  License-Expression: BSD-2-Clause
@@ -69,9 +69,9 @@ Overview
69
69
  .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
70
70
  :alt: Supported implementations
71
71
  :target: https://pypi.org/project/csu
72
- .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.6.6.svg
72
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.6.7.svg
73
73
  :alt: Commits since latest release
74
- :target: https://github.com/ionelmc/python-csu/compare/v2.6.6...main
74
+ :target: https://github.com/ionelmc/python-csu/compare/v2.6.7...main
75
75
  .. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
76
76
  :alt: Scrutinizer Status
77
77
  :target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
@@ -34,9 +34,9 @@ Overview
34
34
  .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
35
35
  :alt: Supported implementations
36
36
  :target: https://pypi.org/project/csu
37
- .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.6.6.svg
37
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.6.7.svg
38
38
  :alt: Commits since latest release
39
- :target: https://github.com/ionelmc/python-csu/compare/v2.6.6...main
39
+ :target: https://github.com/ionelmc/python-csu/compare/v2.6.7...main
40
40
  .. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
41
41
  :alt: Scrutinizer Status
42
42
  :target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "csu"
3
- version = "2.6.6"
3
+ version = "2.6.7"
4
4
  description = "Clean Slate Utils - bunch of utility code, mostly Django/DRF specific."
5
5
  requires-python = ">=3.14"
6
6
  license = "BSD-2-Clause"
@@ -2,4 +2,4 @@
2
2
  a.k.a Clean Slate Utils
3
3
  """
4
4
 
5
- __version__ = "2.6.6"
5
+ __version__ = "2.6.7"
@@ -1,7 +1,9 @@
1
- from base64 import b64encode
1
+ import math
2
2
  from collections import Counter
3
3
  from collections.abc import Iterable
4
+ from datetime import datetime
4
5
  from importlib.util import find_spec
6
+ from math import ceil
5
7
  from os import urandom
6
8
  from typing import TYPE_CHECKING
7
9
 
@@ -21,10 +23,42 @@ else:
21
23
 
22
24
  HTTP_503_SERVICE_UNAVAILABLE = 503
23
25
 
26
+ event_id_number_table = dict(enumerate("0123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"))
27
+ event_id_number_table_reversed = {v: k for k, v in event_id_number_table.items()}
28
+ event_id_number_table_len = len(event_id_number_table)
29
+ event_id_number_table_digit_ratio = 8 * math.log(2) / math.log(event_id_number_table_len)
24
30
 
25
- def get_event_id():
31
+
32
+ def get_event_id_datetime(event_id: str) -> datetime:
33
+ """
34
+ Returns naive datetime.
35
+ """
36
+ return datetime( # noqa: DTZ001
37
+ year=event_id_number_table_reversed[event_id[0]],
38
+ month=event_id_number_table_reversed[event_id[1]],
39
+ day=event_id_number_table_reversed[event_id[2]],
40
+ hour=event_id_number_table_reversed[event_id[-3]],
41
+ minute=event_id_number_table_reversed[event_id[-2]],
42
+ second=event_id_number_table_reversed[event_id[-1]],
43
+ )
44
+
45
+
46
+ def get_event_id(bytes=3):
26
47
  dt = now()
27
- return dt.strftime(f"%y%m%d:{b64encode(urandom(3)).strip(b'=').decode()}:%H%M%S")
48
+ year = event_id_number_table[dt.year % 100]
49
+ month = event_id_number_table[dt.month]
50
+ day = event_id_number_table[dt.day]
51
+ hour = event_id_number_table[dt.hour]
52
+ minute = event_id_number_table[dt.minute]
53
+ second = event_id_number_table[dt.second]
54
+ tag_num = int.from_bytes(urandom(bytes))
55
+ tag_parts = []
56
+ while tag_num > 0:
57
+ tag_num, rem = divmod(tag_num, event_id_number_table_len)
58
+ tag_parts.append(event_id_number_table[rem])
59
+ tag = "".join(reversed(tag_parts))
60
+ tag_pad = ceil(bytes * event_id_number_table_digit_ratio)
61
+ return dt.strftime(f"{year}{month}{day}{tag:0>{tag_pad}}{hour}{minute}{second}")
28
62
 
29
63
 
30
64
  class TaggedError(Exception):
@@ -113,13 +113,10 @@ class HTTPServiceContext[RTYPE = HTTPServiceResponse]:
113
113
  self.proxy = proxy
114
114
  self.logger = service.logger
115
115
  self.retries = service.retries
116
- if event_id:
117
- if ":" not in event_id:
118
- raise ValueError("Expected a 'date:random:time' structured event id.")
119
- else:
116
+ if event_id is None:
120
117
  event_id = get_event_id()
121
- date, self.short_id = event_id.split(":", 1)
122
- self.event_id = f"{date}:{self.short_id}"
118
+ self.event_id = event_id
119
+ self.short_id = event_id[3:-3]
123
120
 
124
121
  def __enter__(self) -> Self:
125
122
  self.client.__enter__()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csu
3
- Version: 2.6.6
3
+ Version: 2.6.7
4
4
  Summary: Clean Slate Utils - bunch of utility code, mostly Django/DRF specific.
5
5
  Author-email: Ionel Cristian Mărieș <contact@ionelmc.ro>
6
6
  License-Expression: BSD-2-Clause
@@ -69,9 +69,9 @@ Overview
69
69
  .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
70
70
  :alt: Supported implementations
71
71
  :target: https://pypi.org/project/csu
72
- .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.6.6.svg
72
+ .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.6.7.svg
73
73
  :alt: Commits since latest release
74
- :target: https://github.com/ionelmc/python-csu/compare/v2.6.6...main
74
+ :target: https://github.com/ionelmc/python-csu/compare/v2.6.7...main
75
75
  .. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
76
76
  :alt: Scrutinizer Status
77
77
  :target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
@@ -1,3 +1,4 @@
1
+ # ruff: noqa: DTZ001
1
2
  import traceback
2
3
  from datetime import datetime
3
4
  from zoneinfo import ZoneInfo
@@ -10,6 +11,7 @@ from csu.exceptions import APIServiceError
10
11
  from csu.exceptions import ExhaustedRetriesError
11
12
  from csu.exceptions import OpenServiceError
12
13
  from csu.exceptions import get_event_id
14
+ from csu.exceptions import get_event_id_datetime
13
15
 
14
16
 
15
17
  class OpenFooError(OpenServiceError):
@@ -90,11 +92,20 @@ def test_exhausted_retries_error():
90
92
 
91
93
  def test_get_event_id(monkeypatch):
92
94
  monkeypatch.setattr(exceptions, "urandom", lambda _: b"ZZZ")
93
- with time_machine.travel(datetime(2024, 12, 12, 1, 2, 3, microsecond=456789, tzinfo=ZoneInfo("Europe/Bucharest"))):
94
- assert get_event_id() == snapshot("241212:Wlpa:010203")
95
+ with time_machine.travel(datetime(2024, 12, 31, 12, 59, 59, microsecond=999999, tzinfo=ZoneInfo("Europe/Bucharest"))):
96
+ assert get_event_id() == snapshot("PCW0SPpVCzz")
95
97
 
96
98
 
97
99
  def test_get_accident_id(monkeypatch):
98
100
  monkeypatch.setattr(exceptions, "urandom", lambda len: bytes(range(len)))
99
101
  with time_machine.travel(1674777700.5816755):
100
- assert get_event_id() == snapshot("230127:AAEC:020140")
102
+ assert get_event_id() == snapshot("N1S0004I21f")
103
+
104
+ assert get_event_id_datetime("N1R0007620140") == snapshot(datetime(23, 1, 26, hour=1, minute=4))
105
+
106
+
107
+ def test_get_event_id_datetime(monkeypatch):
108
+
109
+ assert get_event_id_datetime("N1R0007620140") == snapshot(datetime(23, 1, 26, hour=1, minute=4))
110
+ assert get_event_id_datetime("ZAT0007620AZZ") == snapshot(datetime(34, 10, 28, hour=10, minute=34, second=34))
111
+ assert get_event_id_datetime("TAT0007620Czz") == snapshot(datetime(28, 10, 28, hour=12, minute=59, second=59))