plexus-python-pulse-common 1.1.75__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.
@@ -0,0 +1,172 @@
1
+ import contextlib
2
+ import uuid as py_uuid
3
+ from collections.abc import Callable, Generator, Iterator
4
+ from typing import Protocol
5
+
6
+ import pytest
7
+ import sqlalchemy as sa
8
+ import sqlalchemy.orm as sa_orm
9
+ import textcase
10
+ from fastapi import FastAPI
11
+ from plexus.common.utils.dbutils import ConnectionMaker, Dialects, Drivers
12
+ from plexus.common.utils.dbutils import make_scheme
13
+ from plexus.common.utils.jsonutils import JsonType
14
+ from plexus.common.utils.jsonutils import json_difference, json_reformat
15
+ from sqlmodel import SQLModel
16
+
17
+ from plexus.pulse.common.utils.datautils import compute_vin_code_check_digit
18
+
19
+ __all__ = [
20
+ "generate_dummy_uuid_str",
21
+ "generate_dummy_uuid",
22
+ "generate_dummy_vin_code",
23
+ "case_insensitive_json_compare",
24
+ "patched_value_factory",
25
+ "patched_postgresql_session_maker",
26
+ ]
27
+
28
+
29
+ def generate_dummy_uuid_str(*nums: int) -> str:
30
+ if len(nums) > 8:
31
+ raise ValueError("a maximum of 8 integers can be provided")
32
+ if not all(0 <= num <= 0xFFFF for num in nums):
33
+ raise ValueError("all integers must be in the range 0 to 65535 (0xFFFF)")
34
+ i0, i1, i2, i3, i4, i5, i6, i7 = list(nums) + [0] * (8 - len(nums))
35
+ return f"{i0:04x}{i1:04x}-{i2:04x}-{i3:04x}-{i4:04x}-{i5:04x}{i6:04x}{i7:04x}"
36
+
37
+
38
+ def generate_dummy_uuid(*nums: int) -> py_uuid.UUID:
39
+ return py_uuid.UUID(generate_dummy_uuid_str(*nums))
40
+
41
+
42
+ def generate_dummy_vin_code(*nums: int) -> str:
43
+ if len(nums) > 4:
44
+ raise ValueError("a maximum of 4 integers can be provided")
45
+ if not all(0 <= num <= 9999 for num in nums):
46
+ raise ValueError("all integers must be in the range 0 to 9999")
47
+ i0, i1, i2, i3 = list(nums) + [0] * (4 - len(nums))
48
+ unchecked_vin_code = f"{i0:04d}{i1:04d}0{i2:04d}{i3:04d}"
49
+ check_digit = compute_vin_code_check_digit(unchecked_vin_code)
50
+ return unchecked_vin_code[:8] + check_digit + unchecked_vin_code[-8:]
51
+
52
+
53
+ def case_insensitive_json_compare(a: JsonType, b: JsonType, *, print_diff_messages: bool = True) -> bool:
54
+ identical = True
55
+ for node_path, diff_message in json_difference(json_reformat(a, key_formatter=textcase.camel),
56
+ json_reformat(b, key_formatter=textcase.camel),
57
+ []):
58
+ if print_diff_messages:
59
+ print(node_path, diff_message)
60
+ identical = False
61
+ return identical
62
+
63
+
64
+ class PatcherProtocol[T](Protocol):
65
+
66
+ def value_factory(self) -> Generator[T, None, None]:
67
+ ...
68
+
69
+ @contextlib.contextmanager
70
+ def use(self, value: T) -> Generator[T, None, None]:
71
+ ...
72
+
73
+
74
+ def patched_value_factory[T](
75
+ app: FastAPI,
76
+ value_factory: Callable[[], Generator[T, None, None]],
77
+ ) -> Callable[[], Generator[PatcherProtocol[T], None, None]]:
78
+ """
79
+ Create a pytest fixture that allows patching a FastAPI dependency value factory.
80
+
81
+ :param app: FastAPI application instance.
82
+ :param value_factory: Original value factory to be overridden.
83
+ """
84
+
85
+ class Patcher(object):
86
+ value: T | None = None
87
+
88
+ def value_factory(self) -> Generator[T, None, None]:
89
+ if self.value is not None:
90
+ yield self.value
91
+ else:
92
+ yield from value_factory()
93
+
94
+ @contextlib.contextmanager
95
+ def use(self, value: T) -> Generator[T, None, None]:
96
+ self.value = value
97
+ try:
98
+ yield self.value
99
+ finally:
100
+ self.value = None
101
+
102
+ @pytest.fixture(scope="function")
103
+ def fixture() -> Generator[PatcherProtocol[T], None, None]:
104
+ patcher = Patcher()
105
+ app.dependency_overrides[value_factory] = patcher.value_factory
106
+ try:
107
+ yield patcher
108
+ finally:
109
+ app.dependency_overrides[value_factory] = value_factory
110
+
111
+ return fixture
112
+
113
+
114
+ def patched_postgresql_session_maker(
115
+ postgresql_test_fixture_name: str,
116
+ base_model: type[SQLModel],
117
+ app: FastAPI,
118
+ session_maker: Callable[[], Generator[sa_orm.Session]],
119
+ **kwargs,
120
+ ) -> Callable[[pytest.FixtureRequest], Generator[Callable[[], Iterator[sa_orm.Session]]]]:
121
+ """
122
+ Create a pytest fixture that provides a patched session maker for PostgreSQL tests.
123
+
124
+ :param postgresql_test_fixture_name: Name of the PostgreSQL test fixture to use.
125
+ :param base_model: The base SQLModel model containing metadata.
126
+ :param app: FastAPI application instance.
127
+ :param session_maker: Original session maker to be overridden.
128
+ :return: A pytest fixture function.
129
+ """
130
+
131
+ @pytest.fixture(scope="function")
132
+ def fixture(request: pytest.FixtureRequest):
133
+ postgresql_test_fixture = request.getfixturevalue(postgresql_test_fixture_name)
134
+
135
+ cm = ConnectionMaker.create(
136
+ scheme=make_scheme(Dialects.postgresql, Drivers.psycopg),
137
+ host=postgresql_test_fixture.info.host,
138
+ port=postgresql_test_fixture.info.port,
139
+ username=postgresql_test_fixture.info.user,
140
+ password="",
141
+ database=postgresql_test_fixture.info.dbname,
142
+ )
143
+
144
+ def make_session_maker() -> Iterator[sa_orm.Session]:
145
+ with cm.make_session(**kwargs) as db:
146
+ try:
147
+ # language=postgresql
148
+ db.execute(sa.sql.text("SET TIMEZONE TO 'UTC'"))
149
+ yield db
150
+ finally:
151
+ db.close()
152
+
153
+ app.dependency_overrides[session_maker] = make_session_maker
154
+
155
+ with cm.make_session() as session:
156
+ for table in base_model.metadata.tables.values():
157
+ session.execute(sa.schema.CreateSchema(table.schema, if_not_exists=True))
158
+ session.commit()
159
+ base_model.metadata.create_all(cm.engine)
160
+
161
+ try:
162
+ yield make_session_maker
163
+ finally:
164
+ base_model.metadata.drop_all(cm.engine)
165
+ with cm.make_session() as session:
166
+ for table in base_model.metadata.tables.values():
167
+ session.execute(sa.schema.DropSchema(table.schema, if_exists=True))
168
+ session.commit()
169
+
170
+ app.dependency_overrides[session_maker] = session_maker
171
+
172
+ return fixture
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: plexus-python-pulse-common
3
+ Version: 1.1.75
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Programming Language :: Python :: 3.12
6
+ Classifier: Programming Language :: Python :: 3.13
7
+ Classifier: Programming Language :: Python :: 3.14
8
+ Requires-Python: <3.15,>=3.12
9
+ Requires-Dist: aiobotocore<2.27,>=2.26
10
+ Requires-Dist: boto3<1.42,>=1.41
11
+ Requires-Dist: boto3-stubs[s3]<1.42,>=1.41
12
+ Requires-Dist: docker>=7.1
13
+ Requires-Dist: fsspec>=2025.10
14
+ Requires-Dist: jinja2>=3.0
15
+ Requires-Dist: jsonschema>=4.0
16
+ Requires-Dist: lxml>=6.0
17
+ Requires-Dist: markdown-it-py>=4.0
18
+ Requires-Dist: numpy>=2.3
19
+ Requires-Dist: pydantic>=2.8
20
+ Requires-Dist: pygeodesy>=24.10
21
+ Requires-Dist: pyparsing>=3.2
22
+ Requires-Dist: pyproj>=3.6
23
+ Requires-Dist: pyquaternion>=0.9
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: sqlalchemy>=2.0
26
+ Requires-Dist: s3fs>=2025.10
27
+ Requires-Dist: rich>=13.9
28
+ Requires-Dist: textcase>=0.4
29
+ Requires-Dist: ujson>=5.9
30
+ Requires-Dist: plexus-python-common[all]>=1.1
31
+ Provides-Extra: all
32
+ Requires-Dist: plexus-python-pulse-common[api]; extra == "all"
33
+ Provides-Extra: api
34
+ Requires-Dist: fastapi>=0.119.0; extra == "api"
35
+ Requires-Dist: sqlmodel>=0.0.25; extra == "api"
36
+ Provides-Extra: test
37
+ Requires-Dist: ddt>=1.7; extra == "test"
38
+ Requires-Dist: moto[s3,server]>=5.1; extra == "test"
39
+ Requires-Dist: pytest-cov>=5.0; extra == "test"
40
+ Requires-Dist: pytest-order>=1.3; extra == "test"
41
+ Requires-Dist: pytest-postgresql>=6.1; extra == "test"
42
+ Requires-Dist: pytest>=8.3; extra == "test"
@@ -0,0 +1,29 @@
1
+ plexus/pulse/common/__init__.py,sha256=Kxyx_3ekA19__tyo3XvEeagUU3tzQMQtLIah07a4y3M,183
2
+ plexus/pulse/common/carto/OSMFile.py,sha256=JQ9sq9B5hk82k16z1jDLshTV1FemJJYoKTcu-S3WAWg,8170
3
+ plexus/pulse/common/carto/OSMNode.py,sha256=-G8ij4Rqq-uQxDH_F_s1F2INn2egd4wErLxg0OiOxmk,617
4
+ plexus/pulse/common/carto/OSMTags.py,sha256=9sbs_nD2RIvGnkyZS1PAdgQ8KBCCMaciPUzgFc9-718,3452
5
+ plexus/pulse/common/carto/OSMWay.py,sha256=y36uLV1MpjpPaF6qAfq8Hljdh-mtWv6n4aAr6fLS_7M,570
6
+ plexus/pulse/common/carto/__init__.py,sha256=rL3wircaMKsMA8gYpeMlVH9q_EdBvHBdfMMT045ZxjI,288
7
+ plexus/pulse/common/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ plexus/pulse/common/resources/tags/__init__.py,sha256=1BmOq9aIT2Fr4YTzDwTrYySDhiyRpQ8Bih1gqzlt5v8,1135
9
+ plexus/pulse/common/resources/tags/unittest-1.0.0.tagset.yaml,sha256=8zWjsYp3OxGfBJSiS6-r08q61Jhulol65iV5-pvF1-M,5338
10
+ plexus/pulse/common/resources/tags/universal-1.0.0.tagset.yaml,sha256=PRxyS5xwWcNwUjAbYqN-SafLZVviE-QSzU-DXyXNffw,80736
11
+ plexus/pulse/common/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ plexus/pulse/common/utils/apiutils.py,sha256=8H1wuahkPseOjZHQcrT70EUUDTJ-j7c9lw8kx8SoG0Y,1000
13
+ plexus/pulse/common/utils/bagutils.py,sha256=5JGnMgR8Q9OciW5dE7_2U7n1FGVEXMZXv1SrmI5jznw,13175
14
+ plexus/pulse/common/utils/config.py,sha256=YPtFK5QHTVsXwcFBGV0rlZad0GFtsgwysQNyKYjdzvk,2409
15
+ plexus/pulse/common/utils/datautils.py,sha256=OWlnyFSa6UaPbSSG3HzCJHxdiHyfHAE-FUVHaLfNZoM,9335
16
+ plexus/pulse/common/utils/dockerutils.py,sha256=7DF9yFGz8uHwpYk115xBWqOjs5etz0UE7yh66eDvBzs,5793
17
+ plexus/pulse/common/utils/gisutils.py,sha256=UX_IHPeAx9mbA9p_SsbPFwdsdp7bGeaQhWaCTCzvrBM,11466
18
+ plexus/pulse/common/utils/jsonutils.py,sha256=zSbW94klN2jrkMdBECu4RNJX6zzNaSxauQ3FuihuB6E,3342
19
+ plexus/pulse/common/utils/ormutils.py,sha256=MxSUIyxzGFY9wc12Y6QwaWZkbsYivSF5hNTyGmXFQYU,60683
20
+ plexus/pulse/common/utils/pathutils.py,sha256=S5xk2WEmxP8MiuSKIU3cAEtk4Nk5Lz7IqC5FCd8BEFI,9585
21
+ plexus/pulse/common/utils/s3utils.py,sha256=RArl95rWoyBEFyWU9X0c8WgoQBjP_jOK3OkPoKxIMnA,36025
22
+ plexus/pulse/common/utils/sqlutils.py,sha256=D6kTBjhO5YlNRt3uFlPt6z3uH61m9ajEzPYmsI6NoFc,231
23
+ plexus/pulse/common/utils/strutils.py,sha256=PkmMHCcHN9vALCy_8fJTf00Dg7I2N4QTCXdBmB3t7rk,16440
24
+ plexus/pulse/common/utils/tagutils.py,sha256=smM79dyRCiswKTTcI-At7FLxDH54VN4Id3aFHbDxzYo,61696
25
+ plexus/pulse/common/utils/testutils.py,sha256=hKPRvoN4mg8QGQJH121l59a0uo8jN5rntGQAVYplGBw,6166
26
+ plexus_python_pulse_common-1.1.75.dist-info/METADATA,sha256=-FgPWbRw1LBvHkqaaX0tHVW34Nk8j6y_GulobNLZ9Os,1495
27
+ plexus_python_pulse_common-1.1.75.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
28
+ plexus_python_pulse_common-1.1.75.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
29
+ plexus_python_pulse_common-1.1.75.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+