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.
- plexus/pulse/common/__init__.py +6 -0
- plexus/pulse/common/carto/OSMFile.py +259 -0
- plexus/pulse/common/carto/OSMNode.py +25 -0
- plexus/pulse/common/carto/OSMTags.py +101 -0
- plexus/pulse/common/carto/OSMWay.py +24 -0
- plexus/pulse/common/carto/__init__.py +11 -0
- plexus/pulse/common/resources/__init__.py +0 -0
- plexus/pulse/common/resources/tags/__init__.py +35 -0
- plexus/pulse/common/resources/tags/unittest-1.0.0.tagset.yaml +98 -0
- plexus/pulse/common/resources/tags/universal-1.0.0.tagset.yaml +1390 -0
- plexus/pulse/common/utils/__init__.py +0 -0
- plexus/pulse/common/utils/apiutils.py +31 -0
- plexus/pulse/common/utils/bagutils.py +331 -0
- plexus/pulse/common/utils/config.py +63 -0
- plexus/pulse/common/utils/datautils.py +233 -0
- plexus/pulse/common/utils/dockerutils.py +181 -0
- plexus/pulse/common/utils/gisutils.py +406 -0
- plexus/pulse/common/utils/jsonutils.py +96 -0
- plexus/pulse/common/utils/ormutils.py +1638 -0
- plexus/pulse/common/utils/pathutils.py +234 -0
- plexus/pulse/common/utils/s3utils.py +939 -0
- plexus/pulse/common/utils/sqlutils.py +9 -0
- plexus/pulse/common/utils/strutils.py +401 -0
- plexus/pulse/common/utils/tagutils.py +1476 -0
- plexus/pulse/common/utils/testutils.py +172 -0
- plexus_python_pulse_common-1.1.75.dist-info/METADATA +42 -0
- plexus_python_pulse_common-1.1.75.dist-info/RECORD +29 -0
- plexus_python_pulse_common-1.1.75.dist-info/WHEEL +5 -0
- plexus_python_pulse_common-1.1.75.dist-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
1
|
+
plexus
|