python3-commons 0.16.0__tar.gz → 0.16.1__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 (74) hide show
  1. {python3_commons-0.16.0 → python3_commons-0.16.1}/PKG-INFO +2 -1
  2. {python3_commons-0.16.0 → python3_commons-0.16.1}/pyproject.toml +1 -0
  3. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/__init__.py +13 -13
  4. python3_commons-0.16.1/src/python3_commons/log/formatters.py +118 -0
  5. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/object_storage.py +4 -4
  6. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons.egg-info/PKG-INFO +2 -1
  7. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons.egg-info/requires.txt +1 -0
  8. {python3_commons-0.16.0 → python3_commons-0.16.1}/uv.lock +28 -3
  9. python3_commons-0.16.0/src/python3_commons/log/formatters.py +0 -35
  10. {python3_commons-0.16.0 → python3_commons-0.16.1}/.coveragerc +0 -0
  11. {python3_commons-0.16.0 → python3_commons-0.16.1}/.devcontainer/Dockerfile +0 -0
  12. {python3_commons-0.16.0 → python3_commons-0.16.1}/.devcontainer/devcontainer.json +0 -0
  13. {python3_commons-0.16.0 → python3_commons-0.16.1}/.devcontainer/docker-compose.yml +0 -0
  14. {python3_commons-0.16.0 → python3_commons-0.16.1}/.env_template +0 -0
  15. {python3_commons-0.16.0 → python3_commons-0.16.1}/.github/workflows/checks.yml +0 -0
  16. {python3_commons-0.16.0 → python3_commons-0.16.1}/.github/workflows/python-publish.yaml +0 -0
  17. {python3_commons-0.16.0 → python3_commons-0.16.1}/.github/workflows/release-on-tag-push.yml +0 -0
  18. {python3_commons-0.16.0 → python3_commons-0.16.1}/.gitignore +0 -0
  19. {python3_commons-0.16.0 → python3_commons-0.16.1}/.pre-commit-config.yaml +0 -0
  20. {python3_commons-0.16.0 → python3_commons-0.16.1}/.python-version +0 -0
  21. {python3_commons-0.16.0 → python3_commons-0.16.1}/AUTHORS.rst +0 -0
  22. {python3_commons-0.16.0 → python3_commons-0.16.1}/CHANGELOG.rst +0 -0
  23. {python3_commons-0.16.0 → python3_commons-0.16.1}/LICENSE +0 -0
  24. {python3_commons-0.16.0 → python3_commons-0.16.1}/README.md +0 -0
  25. {python3_commons-0.16.0 → python3_commons-0.16.1}/README.rst +0 -0
  26. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/Makefile +0 -0
  27. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/_static/.gitignore +0 -0
  28. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/authors.rst +0 -0
  29. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/changelog.rst +0 -0
  30. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/conf.py +0 -0
  31. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/index.rst +0 -0
  32. {python3_commons-0.16.0 → python3_commons-0.16.1}/docs/license.rst +0 -0
  33. {python3_commons-0.16.0 → python3_commons-0.16.1}/setup.cfg +0 -0
  34. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/__init__.py +0 -0
  35. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/api_client.py +0 -0
  36. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/async_functools.py +0 -0
  37. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/audit.py +0 -0
  38. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/auth.py +0 -0
  39. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/cache.py +0 -0
  40. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/conf.py +0 -0
  41. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/helpers.py +0 -0
  42. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/models/__init__.py +0 -0
  43. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/models/auth.py +0 -0
  44. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/models/common.py +0 -0
  45. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/models/rbac.py +0 -0
  46. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/db/models/users.py +0 -0
  47. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/exceptions.py +0 -0
  48. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/fs.py +0 -0
  49. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/generators.py +0 -0
  50. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/helpers.py +0 -0
  51. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/log/__init__.py +0 -0
  52. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/log/filters.py +0 -0
  53. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/permissions.py +0 -0
  54. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/serializers/__init__.py +0 -0
  55. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/serializers/common.py +0 -0
  56. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/serializers/json.py +0 -0
  57. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/serializers/msgpack.py +0 -0
  58. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons/serializers/msgspec.py +0 -0
  59. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons.egg-info/SOURCES.txt +0 -0
  60. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons.egg-info/dependency_links.txt +0 -0
  61. {python3_commons-0.16.0 → python3_commons-0.16.1}/src/python3_commons.egg-info/top_level.txt +0 -0
  62. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/__init__.py +0 -0
  63. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/integration/__init__.py +0 -0
  64. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/integration/test_cache.py +0 -0
  65. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/integration/test_osc.py +0 -0
  66. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/__init__.py +0 -0
  67. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/conftest.py +0 -0
  68. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/log/__init__.py +0 -0
  69. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/log/test_formatters.py +0 -0
  70. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/test_async_functools.py +0 -0
  71. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/test_audit.py +0 -0
  72. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/test_helpers.py +0 -0
  73. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/test_msgpack.py +0 -0
  74. {python3_commons-0.16.0 → python3_commons-0.16.1}/tests/unit/test_msgspec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.16.0
3
+ Version: 0.16.1
4
4
  Summary: Re-usable Python3 code
5
5
  Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
6
6
  License-Expression: GPL-3.0
@@ -19,6 +19,7 @@ Requires-Dist: lxml~=6.1.0
19
19
  Requires-Dist: msgpack~=1.1.2
20
20
  Requires-Dist: msgspec==0.21.1
21
21
  Requires-Dist: object-storage-client==0.0.22
22
+ Requires-Dist: orjson==3.11.8
22
23
  Requires-Dist: pydantic-settings~=2.14.0
23
24
  Requires-Dist: SQLAlchemy[asyncio]~=2.0.49
24
25
  Requires-Dist: valkey[libvalkey]~=6.1.1
@@ -25,6 +25,7 @@ dependencies = [
25
25
  "msgpack~=1.1.2",
26
26
  "msgspec==0.21.1",
27
27
  "object-storage-client==0.0.22",
28
+ "orjson==3.11.8",
28
29
  "pydantic-settings~=2.14.0",
29
30
  "SQLAlchemy[asyncio]~=2.0.49",
30
31
  "valkey[libvalkey]~=6.1.1",
@@ -17,28 +17,28 @@ Base = declarative_base(metadata=metadata)
17
17
 
18
18
 
19
19
  class AsyncSessionManager:
20
- def __init__(self, db_settings: Mapping[str, DBSettings]) -> None:
21
- self.db_settings: Mapping[str, DBSettings] = db_settings
20
+ def __init__(self, db_configs: Mapping[str, DBSettings]) -> None:
21
+ self.db_configs: Mapping[str, DBSettings] = db_configs
22
22
  self.engines: dict[str, AsyncEngine] = {}
23
23
  self.session_makers: dict = {}
24
24
 
25
- def get_db_settings(self, name: str) -> DBSettings:
25
+ def get_db_config(self, name: str) -> DBSettings:
26
26
  try:
27
- return self.db_settings[name]
27
+ return self.db_configs[name]
28
28
  except KeyError:
29
29
  logger.exception('Missing database settings: %s', name)
30
30
 
31
31
  raise
32
32
 
33
- def async_engine_from_db_settings(self, name):
34
- db_settings = self.get_db_settings(name)
33
+ def async_engine_from_db_config(self, name):
34
+ db_config = self.get_db_config(name)
35
35
  configuration = {
36
- 'url': str(db_settings.dsn),
37
- 'echo': db_settings.echo,
38
- 'pool_size': db_settings.pool_size,
39
- 'max_overflow': db_settings.max_overflow,
40
- 'pool_timeout': db_settings.pool_timeout,
41
- 'pool_recycle': db_settings.pool_recycle,
36
+ 'url': str(db_config.dsn),
37
+ 'echo': db_config.echo,
38
+ 'pool_size': db_config.pool_size,
39
+ 'max_overflow': db_config.max_overflow,
40
+ 'pool_timeout': db_config.pool_timeout,
41
+ 'pool_recycle': db_config.pool_recycle,
42
42
  }
43
43
 
44
44
  return async_engine_from_config(configuration, prefix='')
@@ -48,7 +48,7 @@ class AsyncSessionManager:
48
48
  engine = self.engines[name]
49
49
  except KeyError:
50
50
  logger.debug('Creating engine: %s', name)
51
- engine = self.async_engine_from_db_settings(name)
51
+ engine = self.async_engine_from_db_config(name)
52
52
  self.engines[name] = engine
53
53
 
54
54
  return engine
@@ -0,0 +1,118 @@
1
+ import logging
2
+ import traceback
3
+ from contextvars import ContextVar
4
+ from datetime import UTC, datetime, date
5
+ from decimal import Decimal
6
+ from typing import Any, Final, Callable
7
+
8
+ import orjson
9
+
10
+ correlation_id: ContextVar[str | None] = ContextVar('correlation_id', default=None)
11
+
12
+ _DEFAULT_MAX_TB_CHARS: Final[int] = 8_000
13
+ _STD_LOG_FIELDS: Final[frozenset[str]] = frozenset(
14
+ {
15
+ 'msg',
16
+ 'args',
17
+ 'levelname',
18
+ 'levelno',
19
+ 'pathname',
20
+ 'filename',
21
+ 'module',
22
+ 'exc_info',
23
+ 'exc_text',
24
+ 'stack_info',
25
+ 'lineno',
26
+ 'funcName',
27
+ 'created',
28
+ 'msecs',
29
+ 'relativeCreated',
30
+ 'thread',
31
+ 'threadName',
32
+ 'process',
33
+ 'processName',
34
+ 'name',
35
+ }
36
+ )
37
+
38
+
39
+ # Keep normalization minimal and branch-based (faster than dict dispatch)
40
+ def _normalize(v: Any) -> Any:
41
+ if isinstance(v, datetime | date):
42
+ return v.isoformat()
43
+
44
+ if isinstance(v, bytes):
45
+ return v.decode('utf-8', errors='replace')
46
+
47
+ if isinstance(v, Decimal):
48
+ return str(v)
49
+
50
+ if isinstance(v, Exception):
51
+ return str(v)
52
+
53
+ return v
54
+
55
+
56
+ class JSONFormatter(logging.Formatter):
57
+ __slots__ = ('_get_correlation_id', '_max_tb_chars')
58
+
59
+ def __init__(
60
+ self,
61
+ *,
62
+ get_correlation_id: Callable[[], str | None] = lambda: correlation_id.get(),
63
+ max_exc_tb_chars: int = _DEFAULT_MAX_TB_CHARS,
64
+ **kwargs: Any,
65
+ ) -> None:
66
+ super().__init__(**kwargs)
67
+ self._get_correlation_id = get_correlation_id
68
+ self._max_tb_chars = max_exc_tb_chars
69
+
70
+ def format(self, record: logging.LogRecord) -> str:
71
+ # --- message (fast path, avoid Formatter.formatMessage) ---
72
+ try:
73
+ message = record.getMessage()
74
+ except Exception:
75
+ message = str(record.msg)
76
+
77
+ # --- timestamp (no Formatter.formatTime overhead) ---
78
+ timestamp = datetime.fromtimestamp(record.created, UTC).isoformat().replace('+00:00', 'Z')
79
+
80
+ log: dict[str, Any] = {
81
+ 'message': message,
82
+ 'level': record.levelname,
83
+ 'logger': record.name,
84
+ 'timestamp': timestamp,
85
+ }
86
+
87
+ if (corr_id := self._get_correlation_id()) is not None:
88
+ log['correlation_id'] = corr_id
89
+
90
+ if (exc_info := record.exc_info) and exc_info[0] is not None:
91
+ exc_type, exc_value, exc_tb = exc_info
92
+
93
+ log['exc_type'] = f'{exc_type.__module__}.{exc_type.__qualname__}'
94
+ log['exc_value'] = str(exc_value)
95
+
96
+ tb = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb)).rstrip()
97
+
98
+ cap = self._max_tb_chars
99
+
100
+ if cap and len(tb) > cap:
101
+ tb = tb[:cap] + '\n... <truncated>'
102
+
103
+ log['exc_traceback'] = tb
104
+
105
+ record_dict = record.__dict__
106
+ std_log_fields = _STD_LOG_FIELDS
107
+
108
+ if len(record_dict) > len(std_log_fields):
109
+ normalize = _normalize
110
+ out_set = log.__setitem__
111
+
112
+ for k, v in record_dict.items():
113
+ if k[0] == '_' or k in std_log_fields:
114
+ continue
115
+
116
+ out_set(k, normalize(v))
117
+
118
+ return orjson.dumps(log).decode('utf-8')
@@ -135,7 +135,7 @@ async def list_objects(bucket_name: str, prefix: str, *, recursive: bool = True)
135
135
 
136
136
 
137
137
  async def get_object_streams(
138
- bucket_name: str, path: str, *, recursive: bool = True
138
+ bucket_name: str, path: str, *, recursive: bool = True
139
139
  ) -> AsyncGenerator[tuple[str, datetime, StreamingBody]]:
140
140
  async for obj in list_objects(bucket_name, path, recursive=recursive):
141
141
  object_name = obj['Key']
@@ -146,7 +146,7 @@ async def get_object_streams(
146
146
 
147
147
 
148
148
  async def get_objects(
149
- bucket_name: str, path: str, *, recursive: bool = True
149
+ bucket_name: str, path: str, *, recursive: bool = True
150
150
  ) -> AsyncGenerator[tuple[str, datetime, bytes]]:
151
151
  async for object_name, last_modified, stream in get_object_streams(bucket_name, path, recursive=recursive):
152
152
  data = await stream.read()
@@ -168,7 +168,7 @@ async def remove_object(bucket_name: str, object_name: str) -> None:
168
168
 
169
169
 
170
170
  async def remove_objects(
171
- bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
171
+ bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
172
172
  ) -> Sequence[Mapping] | None:
173
173
  storage = ObjectStorage(s3_settings)
174
174
 
@@ -191,7 +191,7 @@ async def remove_objects(
191
191
  chunk_size = 1000
192
192
 
193
193
  for i in range(0, len(objects_to_delete), chunk_size):
194
- chunk = objects_to_delete[i: i + chunk_size]
194
+ chunk = objects_to_delete[i : i + chunk_size]
195
195
 
196
196
  response = await s3_client.delete_objects(Bucket=bucket_name, Delete={'Objects': chunk})
197
197
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.16.0
3
+ Version: 0.16.1
4
4
  Summary: Re-usable Python3 code
5
5
  Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
6
6
  License-Expression: GPL-3.0
@@ -19,6 +19,7 @@ Requires-Dist: lxml~=6.1.0
19
19
  Requires-Dist: msgpack~=1.1.2
20
20
  Requires-Dist: msgspec==0.21.1
21
21
  Requires-Dist: object-storage-client==0.0.22
22
+ Requires-Dist: orjson==3.11.8
22
23
  Requires-Dist: pydantic-settings~=2.14.0
23
24
  Requires-Dist: SQLAlchemy[asyncio]~=2.0.49
24
25
  Requires-Dist: valkey[libvalkey]~=6.1.1
@@ -5,6 +5,7 @@ lxml~=6.1.0
5
5
  msgpack~=1.1.2
6
6
  msgspec==0.21.1
7
7
  object-storage-client==0.0.22
8
+ orjson==3.11.8
8
9
  pydantic-settings~=2.14.0
9
10
  SQLAlchemy[asyncio]~=2.0.49
10
11
  valkey[libvalkey]~=6.1.1
@@ -677,6 +677,29 @@ wheels = [
677
677
  { url = "https://files.pythonhosted.org/packages/ee/73/4af6bf9dc086e9c0a69988d2258d2f6f5d02d00ef891d9df3d6d08c3355a/object_storage_client-0.0.22-cp314-cp314-win_amd64.whl", hash = "sha256:df863a2a3f8f1c77d06bdc547d674b321878dca7cb7db94606b763cb9cc7e508", size = 2095414, upload-time = "2026-04-21T12:36:38.966Z" },
678
678
  ]
679
679
 
680
+ [[package]]
681
+ name = "orjson"
682
+ version = "3.11.8"
683
+ source = { registry = "https://pypi.org/simple" }
684
+ sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832, upload-time = "2026-03-31T16:16:27.878Z" }
685
+ wheels = [
686
+ { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171, upload-time = "2026-03-31T16:16:00.651Z" },
687
+ { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746, upload-time = "2026-03-31T16:16:02.673Z" },
688
+ { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867, upload-time = "2026-03-31T16:16:04.342Z" },
689
+ { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664, upload-time = "2026-03-31T16:16:05.837Z" },
690
+ { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701, upload-time = "2026-03-31T16:16:07.407Z" },
691
+ { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202, upload-time = "2026-03-31T16:16:09.48Z" },
692
+ { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194, upload-time = "2026-03-31T16:16:11.02Z" },
693
+ { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639, upload-time = "2026-03-31T16:16:13.434Z" },
694
+ { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914, upload-time = "2026-03-31T16:16:14.955Z" },
695
+ { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800, upload-time = "2026-03-31T16:16:16.594Z" },
696
+ { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837, upload-time = "2026-03-31T16:16:18.585Z" },
697
+ { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441, upload-time = "2026-03-31T16:16:20.151Z" },
698
+ { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983, upload-time = "2026-03-31T16:16:21.823Z" },
699
+ { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396, upload-time = "2026-03-31T16:16:23.685Z" },
700
+ { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330, upload-time = "2026-03-31T16:16:25.496Z" },
701
+ ]
702
+
680
703
  [[package]]
681
704
  name = "packaging"
682
705
  version = "26.1"
@@ -715,7 +738,7 @@ wheels = [
715
738
 
716
739
  [[package]]
717
740
  name = "pre-commit"
718
- version = "4.5.1"
741
+ version = "4.6.0"
719
742
  source = { registry = "https://pypi.org/simple" }
720
743
  dependencies = [
721
744
  { name = "cfgv" },
@@ -724,9 +747,9 @@ dependencies = [
724
747
  { name = "pyyaml" },
725
748
  { name = "virtualenv" },
726
749
  ]
727
- sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
750
+ sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" }
728
751
  wheels = [
729
- { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
752
+ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" },
730
753
  ]
731
754
 
732
755
  [[package]]
@@ -999,6 +1022,7 @@ dependencies = [
999
1022
  { name = "msgpack" },
1000
1023
  { name = "msgspec" },
1001
1024
  { name = "object-storage-client" },
1025
+ { name = "orjson" },
1002
1026
  { name = "pydantic-settings" },
1003
1027
  { name = "sqlalchemy", extra = ["asyncio"] },
1004
1028
  { name = "valkey", extra = ["libvalkey"] },
@@ -1031,6 +1055,7 @@ requires-dist = [
1031
1055
  { name = "msgpack", specifier = "~=1.1.2" },
1032
1056
  { name = "msgspec", specifier = "==0.21.1" },
1033
1057
  { name = "object-storage-client", specifier = "==0.0.22" },
1058
+ { name = "orjson", specifier = "==3.11.8" },
1034
1059
  { name = "pydantic-settings", specifier = "~=2.14.0" },
1035
1060
  { name = "sqlalchemy", extras = ["asyncio"], specifier = "~=2.0.49" },
1036
1061
  { name = "valkey", extras = ["libvalkey"], specifier = "~=6.1.1" },
@@ -1,35 +0,0 @@
1
- import json
2
- import logging
3
- import traceback
4
- from contextvars import ContextVar
5
-
6
- from python3_commons.serializers.json import CustomJSONEncoder
7
-
8
- correlation_id: ContextVar[str | None] = ContextVar('correlation_id', default=None)
9
-
10
-
11
- class JSONFormatter(logging.Formatter):
12
- @staticmethod
13
- def format_exception(exc_info: logging._SysExcInfoType) -> str:
14
- return ''.join(traceback.format_exception(*exc_info))
15
-
16
- def format(self, record: logging.LogRecord) -> str:
17
- if corr_id := correlation_id.get():
18
- record.correlation_id = corr_id
19
-
20
- try:
21
- record.message = record.getMessage()
22
- except TypeError:
23
- record.message = str(record.msg)
24
-
25
- if record.exc_info:
26
- record.exc_text = self.format_exception(record.exc_info)
27
- else:
28
- record.exc_text = None
29
-
30
- record_dict = record.__dict__
31
-
32
- del record_dict['args']
33
- del record_dict['msg']
34
-
35
- return json.dumps(record_dict, cls=CustomJSONEncoder)