plexus-python-common 1.0.57__tar.gz → 1.0.59__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.
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/PKG-INFO +1 -1
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/bagutils.py +14 -12
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/ormutils.py +9 -9
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/tagutils.py +26 -20
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
- plexus_python_common-1.0.59/test/plexus_tests/common/utils/jsonutils_test.py +37 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/tagutils_test.py +164 -92
- plexus_python_common-1.0.57/test/plexus_tests/common/utils/jsonutils_test.py +0 -15
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/.editorconfig +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/.github/workflows/pr.yml +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/.github/workflows/push.yml +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/.gitignore +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/MANIFEST.in +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/README.md +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/VERSION +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/pyproject.toml +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/0-dummy +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/1-dummy +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/2-dummy +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.0.0.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.0.0.vol-0.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.1.1.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.1.1.vol-1.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.2.2.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.2.2.vol-2.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.csv.part0 +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.csv.part1 +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.csv.part2 +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.txt +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/setup.cfg +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/setup.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMFile.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMNode.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMTags.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMWay.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/pose.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/proj.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/resources/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/resources/tags/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/resources/tags/unittest-1.0.0.tagset.yaml +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/resources/tags/universal-1.0.0.tagset.yaml +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/apiutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/config.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/datautils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/dockerutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/jsonutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/pathutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/s3utils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/sqlutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/strutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/testutils.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus_python_common.egg-info/requires.txt +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus_python_common.egg-info/top_level.txt +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/pose_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/proj_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/datautils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/dockerutils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/pathutils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/s3utils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/strutils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/utils/testutils_test.py +0 -0
- {plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/testenv.py +0 -0
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/bagutils.py
RENAMED
|
@@ -148,18 +148,18 @@ class BagReader(AbstractContextManager, BagIOBase):
|
|
|
148
148
|
@staticmethod
|
|
149
149
|
def open_ros2(db_dir: str | os.PathLike[str], db_filename: str = default_bag_db_file, **kwargs) -> "BagReader":
|
|
150
150
|
"""
|
|
151
|
-
Creates a BagReader instance to read messages from a ROS2 bag file in the specified directory.
|
|
151
|
+
Creates a ``BagReader`` instance to read messages from a ROS2 bag file in the specified directory.
|
|
152
152
|
|
|
153
153
|
:param db_dir: path to the directory where the SQLite DB file is located.
|
|
154
154
|
:param db_filename: name of the SQLite DB file to read. Default is "bag.db".
|
|
155
|
-
:param kwargs: additional keyword arguments to pass to the BagReader constructor.
|
|
156
|
-
:return: a BagReader instance.
|
|
155
|
+
:param kwargs: additional keyword arguments to pass to the ``BagReader`` constructor.
|
|
156
|
+
:return: a ``BagReader`` instance.
|
|
157
157
|
"""
|
|
158
158
|
return BagReader(make_path(db_dir) / db_filename)
|
|
159
159
|
|
|
160
160
|
def __init__(self, bag_file_path: str | os.PathLike[str]):
|
|
161
161
|
"""
|
|
162
|
-
Creates a BagReader instance to read messages from a ROS2 bag file.
|
|
162
|
+
Creates a ``BagReader`` instance to read messages from a ROS2 bag file.
|
|
163
163
|
|
|
164
164
|
:param bag_file_path: path to the SQLite DB file to read.
|
|
165
165
|
"""
|
|
@@ -202,24 +202,26 @@ class BagWriter(AbstractContextManager, BagIOBase):
|
|
|
202
202
|
@staticmethod
|
|
203
203
|
def open_ros2(db_dir: str | os.PathLike[str], db_filename: str = default_bag_db_file, **kwargs) -> "BagWriter":
|
|
204
204
|
"""
|
|
205
|
-
Creates a BagWriter instance to write messages to a ROS2 bag file in the specified directory.
|
|
205
|
+
Creates a ``BagWriter`` instance to write messages to a ROS2 bag file in the specified directory.
|
|
206
206
|
|
|
207
207
|
:param db_dir: path to the directory where the SQLite DB file will be created.
|
|
208
208
|
:param db_filename: name of the SQLite DB file to create. Default is "bag.db".
|
|
209
|
-
:param kwargs: additional keyword arguments to pass to the BagWriter constructor.
|
|
210
|
-
:return: a BagWriter instance.
|
|
209
|
+
:param kwargs: additional keyword arguments to pass to the ``BagWriter`` constructor.
|
|
210
|
+
:return: a ``BagWriter`` instance.
|
|
211
211
|
"""
|
|
212
212
|
return BagWriter(make_path(db_dir) / db_filename, **kwargs)
|
|
213
213
|
|
|
214
214
|
def __init__(self, bag_file_path: str | os.PathLike[str], *, overwrite: bool = True, exist_ok: bool = False):
|
|
215
215
|
"""
|
|
216
|
-
Creates a BagWriter instance to write messages to a ROS2 bag file.
|
|
216
|
+
Creates a ``BagWriter`` instance to write messages to a ROS2 bag file.
|
|
217
217
|
|
|
218
218
|
:param bag_file_path: path to the SQLite DB file to create.
|
|
219
|
-
:param overwrite: whether to overwrite the SQLite DB file if it already exists. If False and the file
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
:param overwrite: whether to overwrite the SQLite DB file if it already exists. If ``False`` and the file
|
|
220
|
+
already exists, a ``FileExistsError`` is raised. If ``True``, the existing file is deleted
|
|
221
|
+
and a new one is created.
|
|
222
|
+
:param exist_ok: whether to ignore if the SQLite DB file already exists. If ``False`` and the file already
|
|
223
|
+
exists, a ``FileExistsError`` is raised. If ``True``, the existing file is used and no new
|
|
224
|
+
file is created.
|
|
223
225
|
"""
|
|
224
226
|
super().__init__(bag_file_path)
|
|
225
227
|
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/ormutils.py
RENAMED
|
@@ -451,11 +451,11 @@ def model_revision_type(dialect: str | None = None) -> sa.types.TypeEngine[int]:
|
|
|
451
451
|
|
|
452
452
|
def make_sequence_model_mixin(dialect: str | None = None) -> type[SequenceModelMixin]:
|
|
453
453
|
"""
|
|
454
|
-
Creates a mixin class for SQLModel models that adds a unique identifier field
|
|
454
|
+
Creates a mixin class for SQLModel models that adds a unique identifier field ``sqn``.
|
|
455
455
|
Use this mixin to add an auto-incremented primary key to your models.
|
|
456
456
|
|
|
457
|
-
:param dialect: The database dialect to determine the column type for the
|
|
458
|
-
:return: A mixin class that can be used with SQLModel models to add the
|
|
457
|
+
:param dialect: The database dialect to determine the column type for the ``sqn`` field.
|
|
458
|
+
:return: A mixin class that can be used with SQLModel models to add the ``sqn`` field.
|
|
459
459
|
"""
|
|
460
460
|
|
|
461
461
|
class ModelMixin(SQLModel):
|
|
@@ -765,11 +765,11 @@ class RevisionModel(make_base_model(), make_revision_model_mixin(), table=True):
|
|
|
765
765
|
def make_snapshot_model_trigger[SnapshotModelT: SnapshotModelMixin](engine: sa.Engine, model: type[SnapshotModelT]):
|
|
766
766
|
"""
|
|
767
767
|
Creates the necessary database objects (sequence, function, trigger) to support automatic snapshot management
|
|
768
|
-
for the given snapshot model. This includes a sequence for
|
|
769
|
-
and a trigger to invoke the function before inserts. The model must extend
|
|
768
|
+
for the given snapshot model. This includes a sequence for ``record_sqn``, a function to handle snapshot updates,
|
|
769
|
+
and a trigger to invoke the function before inserts. The model must extend ``SnapshotModel``.
|
|
770
770
|
|
|
771
771
|
:param engine: SQLAlchemy engine connected to the target database.
|
|
772
|
-
:param model: The snapshot model class extending
|
|
772
|
+
:param model: The snapshot model class extending ``SnapshotModel``.
|
|
773
773
|
"""
|
|
774
774
|
table_name = model_name_of(model, fallback_classname=False)
|
|
775
775
|
if not table_name:
|
|
@@ -834,11 +834,11 @@ def make_snapshot_model_trigger[SnapshotModelT: SnapshotModelMixin](engine: sa.E
|
|
|
834
834
|
def make_revision_model_trigger[RevisionModelT: RevisionModelMixin](engine: sa.Engine, model: type[RevisionModelT]):
|
|
835
835
|
"""
|
|
836
836
|
Creates the necessary database objects (sequence, function, trigger) to support automatic revision management
|
|
837
|
-
for the given revision model. This includes a sequence for
|
|
838
|
-
and a trigger to invoke the function before inserts. The model must extend
|
|
837
|
+
for the given revision model. This includes a sequence for ``record_sqn``, a function to handle revision updates,
|
|
838
|
+
and a trigger to invoke the function before inserts. The model must extend ``RevisionModel``.
|
|
839
839
|
|
|
840
840
|
:param engine: SQLAlchemy engine connected to the target database.
|
|
841
|
-
:param model: The revision model class extending
|
|
841
|
+
:param model: The revision model class extending ``RevisionModel``.
|
|
842
842
|
"""
|
|
843
843
|
table_name = model_name_of(model, fallback_classname=False)
|
|
844
844
|
if not table_name:
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/tagutils.py
RENAMED
|
@@ -69,7 +69,8 @@ class Tag(object):
|
|
|
69
69
|
|
|
70
70
|
@property
|
|
71
71
|
def parent_tag_name(self) -> str | None:
|
|
72
|
-
|
|
72
|
+
name = head_or_none(self.name.rsplit(":", 1))
|
|
73
|
+
return None if is_blank(name) or name == self.name else name
|
|
73
74
|
|
|
74
75
|
def unbind(self) -> Self:
|
|
75
76
|
return self
|
|
@@ -155,18 +156,18 @@ class Tagset(Sequence[Tag], Mapping[str, Tag]):
|
|
|
155
156
|
def tag_names(self) -> list[str]:
|
|
156
157
|
return list(self.tags_dict.keys())
|
|
157
158
|
|
|
158
|
-
def child_tags(self, parent: str | Tag | BoundTag) -> list[
|
|
159
|
+
def child_tags(self, parent: str | Tag | BoundTag) -> list[Tag]:
|
|
159
160
|
parent = self.get(parent)
|
|
160
161
|
if parent is None:
|
|
161
162
|
return []
|
|
162
163
|
subtree = dicttree_subtree(self.tags_tree, parent.tag_parts)
|
|
163
|
-
return list(
|
|
164
|
+
return list(dicttree_children(subtree)) if subtree else []
|
|
164
165
|
|
|
165
|
-
def parent_tags(self, child: str | Tag | BoundTag) -> list[
|
|
166
|
+
def parent_tags(self, child: str | Tag | BoundTag) -> list[Tag]:
|
|
166
167
|
child = self.get(child)
|
|
167
168
|
if child is None:
|
|
168
169
|
return []
|
|
169
|
-
return list(
|
|
170
|
+
return list(dicttree_lineage(self.tags_tree, child.tag_parts[:-1]))
|
|
170
171
|
|
|
171
172
|
def validate(self, tag_name: str, props: JsonType | None, *, raise_on_error: bool = False) -> bool:
|
|
172
173
|
tag = self.get(tag_name)
|
|
@@ -687,7 +688,7 @@ class TagCache(object):
|
|
|
687
688
|
:param tag_prefix: Filter by tag name prefix, e.g. "dummy_tag:" to match all tags starting with "dummy_tag:"
|
|
688
689
|
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
689
690
|
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
690
|
-
|
|
691
|
+
tagsets)
|
|
691
692
|
:param batch_size: Number of records to fetch per batch from the database (for memory efficiency)
|
|
692
693
|
:return: Generator of ``TagRecordTable`` instances that match the filters
|
|
693
694
|
"""
|
|
@@ -748,7 +749,7 @@ class TagCache(object):
|
|
|
748
749
|
:param target_end_dt: Filter by target end time (inclusive)
|
|
749
750
|
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
750
751
|
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
751
|
-
|
|
752
|
+
tagsets)
|
|
752
753
|
:param batch_size: Number of records to fetch per batch from the database (for memory efficiency)
|
|
753
754
|
:return: Generator of ``TagRecordTable`` instances that match the filters
|
|
754
755
|
"""
|
|
@@ -812,7 +813,7 @@ class TagCache(object):
|
|
|
812
813
|
:param tag_prefix: Filter by tag name prefix, e.g. "dummy_tag:" to match all tags starting with "dummy_tag:"
|
|
813
814
|
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
814
815
|
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
815
|
-
|
|
816
|
+
tagsets)
|
|
816
817
|
"""
|
|
817
818
|
with self.make_session() as session:
|
|
818
819
|
query = session.query(TagRecordTable)
|
|
@@ -920,7 +921,7 @@ class TargetedTagCache(object):
|
|
|
920
921
|
:param tag_prefix: Filter by tag name prefix, e.g. "dummy_tag:" to match all tags starting with "dummy_tag:"
|
|
921
922
|
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
922
923
|
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
923
|
-
|
|
924
|
+
tagsets)
|
|
924
925
|
:param batch_size: Number of records to fetch per batch from the database (for memory efficiency)
|
|
925
926
|
:return: Generator of ``TagRecordTable`` instances that match the filters
|
|
926
927
|
"""
|
|
@@ -964,12 +965,13 @@ class TargetedTagCache(object):
|
|
|
964
965
|
:param begin_dt: Begin datetime of the tag record
|
|
965
966
|
:param end_dt: End datetime of the tag record
|
|
966
967
|
:param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
|
|
967
|
-
|
|
968
|
+
its name will be used.
|
|
968
969
|
:param props: Additional properties of the tag record in JSON format (optional)
|
|
969
970
|
:param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
971
|
+
``BoundTag`` instance, this parameter will be ignored and the namespace from the
|
|
972
|
+
instance will be used.
|
|
973
|
+
:param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a ``BoundTag``
|
|
974
|
+
instance, this parameter will be ignored and the version from the instance will be used.
|
|
973
975
|
:return: Self instance for chaining
|
|
974
976
|
"""
|
|
975
977
|
with self.make_session() as session:
|
|
@@ -998,12 +1000,14 @@ class TargetedTagCache(object):
|
|
|
998
1000
|
Add a tag record to the cache for the entire target range.
|
|
999
1001
|
|
|
1000
1002
|
:param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
|
|
1001
|
-
|
|
1003
|
+
its name will be used.
|
|
1002
1004
|
:param props: Additional properties of the tag record in JSON format (optional)
|
|
1003
1005
|
:param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1004
|
-
|
|
1006
|
+
``BoundTag`` instance, this parameter will be ignored and the namespace from the
|
|
1007
|
+
instance will be used.
|
|
1005
1008
|
:param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a
|
|
1006
|
-
|
|
1009
|
+
``BoundTag`` instance, this parameter will be ignored and the version from the instance
|
|
1010
|
+
will be used.
|
|
1007
1011
|
:return: Self instance for chaining
|
|
1008
1012
|
"""
|
|
1009
1013
|
return self.add_ranged_tag(
|
|
@@ -1036,7 +1040,7 @@ class TargetedTagCache(object):
|
|
|
1036
1040
|
:param tag_prefix: Filter by tag name prefix, e.g. "dummy_tag:" to match all tags starting with "dummy_tag:"
|
|
1037
1041
|
:param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
|
|
1038
1042
|
:param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
|
|
1039
|
-
|
|
1043
|
+
tagsets)
|
|
1040
1044
|
:return: Self instance for chaining
|
|
1041
1045
|
"""
|
|
1042
1046
|
with self.make_session() as session:
|
|
@@ -1073,10 +1077,12 @@ def tag_cache(*, identifier: str | None = None, file_path: str | None = None) ->
|
|
|
1073
1077
|
string and return a ``TagCache`` instance associated with a file path derived from the identifier.
|
|
1074
1078
|
|
|
1075
1079
|
:param identifier: An optional string identifier for the tag cache. If provided, it must be in snake case format
|
|
1076
|
-
|
|
1080
|
+
and will be used to derive the file path for the tag cache. If not provided, a default file path
|
|
1081
|
+
will be used.
|
|
1077
1082
|
:param file_path: An optional file path for the tag cache. If provided, it will be used directly. If not provided,
|
|
1078
|
-
|
|
1079
|
-
|
|
1083
|
+
the file path will be derived from the identifier if it is provided, or a default file path will
|
|
1084
|
+
be used if the identifier is not provided. Note that both ``identifier`` and ``file_path`` cannot
|
|
1085
|
+
be specified at the same time.
|
|
1080
1086
|
:return: A ``TagCache`` instance associated with the specified or default file path.
|
|
1081
1087
|
"""
|
|
1082
1088
|
if identifier is not None and file_path is not None:
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
import ddt
|
|
5
|
+
from iker.common.utils.dtutils import dt_parse_iso
|
|
6
|
+
|
|
7
|
+
from plexus.common.utils.jsonutils import json_dumps, json_loads
|
|
8
|
+
from plexus.common.utils.jsonutils import read_chunked_jsonl
|
|
9
|
+
from testenv import resources_directory
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@ddt.ddt
|
|
13
|
+
class JsonUtilsTest(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
def test_json_loads_dumps(self):
|
|
16
|
+
data = {
|
|
17
|
+
"int": 123,
|
|
18
|
+
"float": 123.456,
|
|
19
|
+
"string": "dummy_string",
|
|
20
|
+
"boolean": True,
|
|
21
|
+
"null": None,
|
|
22
|
+
"datetime": dt_parse_iso("2024-01-01T00:00:00.000000+00:00"),
|
|
23
|
+
"list": [123, 123.456, "dummy_string", True, None, dt_parse_iso("2024-01-01T00:00:00.000000+00:00")],
|
|
24
|
+
"dict": {
|
|
25
|
+
"int": 123,
|
|
26
|
+
"float": 123.456,
|
|
27
|
+
"string": "dummy_string",
|
|
28
|
+
"boolean": True,
|
|
29
|
+
"null": None,
|
|
30
|
+
"datetime": dt_parse_iso("2024-01-01T00:00:00.000000+00:00"),
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
self.assertEqual(data, json_loads(json_dumps(data)))
|
|
34
|
+
|
|
35
|
+
def test_read_chunked_jsonl(self):
|
|
36
|
+
for data, path in read_chunked_jsonl(str(resources_directory / "unittest" / "jsonutils" / "dummy.{{}}.jsonl")):
|
|
37
|
+
self.assertEqual(data["file"], os.path.basename(path))
|
|
@@ -6,6 +6,7 @@ import unittest
|
|
|
6
6
|
|
|
7
7
|
import ddt
|
|
8
8
|
from iker.common.utils.dtutils import dt_parse_iso
|
|
9
|
+
from iker.common.utils.iterutils import last
|
|
9
10
|
from iker.common.utils.jsonutils import JsonObject
|
|
10
11
|
|
|
11
12
|
from plexus.common.utils.tagutils import MutableTagset, Tag, TagCache, Tagset
|
|
@@ -25,8 +26,19 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
25
26
|
for tag in tagset:
|
|
26
27
|
self.assertIn(tag, tagset)
|
|
27
28
|
self.assertIn(tag.name, tagset)
|
|
28
|
-
self.assertEqual(tag
|
|
29
|
-
self.assertEqual(tag.name, tagset.
|
|
29
|
+
self.assertEqual(tag, tagset.get(tag))
|
|
30
|
+
self.assertEqual(tag.name, tagset.get_bound(tag).name)
|
|
31
|
+
self.assertEqual(tag, tagset.get_bound(tag).unbind())
|
|
32
|
+
|
|
33
|
+
if tag.parent_tag_name is not None:
|
|
34
|
+
self.assertIn(tag.parent_tag_name, tagset)
|
|
35
|
+
self.assertEqual(tag.parent_tag_name, tagset.get(tag.parent_tag_name).name)
|
|
36
|
+
self.assertEqual(last(tagset.parent_tags(tag)), tagset.get(tag.parent_tag_name))
|
|
37
|
+
|
|
38
|
+
for child_tag in tagset.child_tags(tag):
|
|
39
|
+
self.assertIn(child_tag, tagset)
|
|
40
|
+
self.assertIn(child_tag.name, tagset)
|
|
41
|
+
self.assertIn(tag, tagset.parent_tags(child_tag))
|
|
30
42
|
|
|
31
43
|
markdown = render_tagset_markdown_readme(tagset)
|
|
32
44
|
self.assertIsInstance(markdown, str)
|
|
@@ -84,17 +96,20 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
84
96
|
tagset.validate(tag, props, raise_on_error=True)
|
|
85
97
|
|
|
86
98
|
def test_tag_cache(self):
|
|
87
|
-
|
|
88
|
-
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
89
|
-
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
90
|
-
|
|
91
|
-
tags = [
|
|
99
|
+
tag_names = [
|
|
92
100
|
"dummy:foo",
|
|
93
101
|
"dummy:bar",
|
|
94
102
|
"dummy:baz",
|
|
95
103
|
"dummy:qux",
|
|
96
104
|
]
|
|
97
105
|
|
|
106
|
+
tagset = MutableTagset(namespace="tagset", version="1.0.0", desc="A dummy tagset for testing")
|
|
107
|
+
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
108
|
+
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
109
|
+
|
|
110
|
+
tags_count = len(tag_names)
|
|
111
|
+
tagset_tags_count = sum(1 for tag_name in tag_names if tag_name in tagset)
|
|
112
|
+
|
|
98
113
|
cache = TagCache()
|
|
99
114
|
|
|
100
115
|
self.assertEqual(cache.file_path, tag_cache_file_path())
|
|
@@ -109,72 +124,85 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
109
124
|
|
|
110
125
|
target_cache = cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
111
126
|
|
|
112
|
-
|
|
127
|
+
tag_records_count = 1000
|
|
113
128
|
|
|
114
|
-
for i in range(
|
|
129
|
+
for i in range(tag_records_count):
|
|
130
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
131
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
115
132
|
target_cache.add_ranged_tag(
|
|
116
133
|
dt_parse_iso("2020-01-01T00:00:00+00:00") + datetime.timedelta(seconds=i),
|
|
117
134
|
dt_parse_iso("2020-01-02T00:00:00+00:00") + datetime.timedelta(seconds=i + 1),
|
|
118
|
-
|
|
135
|
+
tag,
|
|
119
136
|
)
|
|
120
137
|
|
|
121
|
-
self.assertEqual(len(list(target_cache.iter_tags())),
|
|
122
|
-
self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset]))),
|
|
123
|
-
|
|
124
|
-
self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset], tagset_inverted=
|
|
138
|
+
self.assertEqual(len(list(target_cache.iter_tags())), tag_records_count)
|
|
139
|
+
self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset]))),
|
|
140
|
+
tag_records_count * tagset_tags_count // tags_count)
|
|
141
|
+
self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
142
|
+
tag_records_count * (tags_count - tagset_tags_count) // tags_count)
|
|
143
|
+
self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
144
|
+
tag_records_count * tagset_tags_count // tags_count)
|
|
125
145
|
self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
126
146
|
dt_parse_iso("2020-01-01T00:01:00+00:00")))),
|
|
127
147
|
60 + 1)
|
|
128
148
|
self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
129
149
|
dt_parse_iso("2020-01-01T00:01:00+00:00"),
|
|
130
150
|
tag_prefix="dummy:foo"))),
|
|
131
|
-
60 //
|
|
151
|
+
60 // tags_count + 1)
|
|
132
152
|
self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
133
153
|
dt_parse_iso("2020-01-01T00:01:00+00:00"),
|
|
134
154
|
tag_prefix="dummy:bar"))),
|
|
135
|
-
60 //
|
|
155
|
+
60 // tags_count)
|
|
136
156
|
self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
137
157
|
dt_parse_iso("2020-01-01T00:01:00+00:00"),
|
|
138
158
|
tag_prefix="dummy"))),
|
|
139
159
|
60 + 1)
|
|
140
160
|
self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy:foo"))),
|
|
141
|
-
|
|
161
|
+
tag_records_count // tags_count)
|
|
142
162
|
self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy:bar"))),
|
|
143
|
-
|
|
144
|
-
self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy"))),
|
|
145
|
-
|
|
146
|
-
self.assertEqual(len(list(cache.iter_tags())),
|
|
147
|
-
self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset]))),
|
|
148
|
-
self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
149
|
-
|
|
163
|
+
tag_records_count // tags_count)
|
|
164
|
+
self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy"))), tag_records_count)
|
|
165
|
+
|
|
166
|
+
self.assertEqual(len(list(cache.iter_tags())), tag_records_count)
|
|
167
|
+
self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset]))), tag_records_count // 2)
|
|
168
|
+
self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
169
|
+
tag_records_count * (tags_count - tagset_tags_count) // tags_count)
|
|
170
|
+
self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
171
|
+
tag_records_count * tagset_tags_count // tags_count)
|
|
150
172
|
self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
151
173
|
dt_parse_iso("2020-01-01T00:01:00+00:00")))),
|
|
152
174
|
60 + 1)
|
|
153
175
|
self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
154
176
|
dt_parse_iso("2020-01-01T00:01:00+00:00"),
|
|
155
177
|
tag_prefix="dummy:foo"))),
|
|
156
|
-
60 //
|
|
178
|
+
60 // tags_count + 1)
|
|
157
179
|
self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
158
180
|
dt_parse_iso("2020-01-01T00:01:00+00:00"),
|
|
159
181
|
tag_prefix="dummy:bar"))),
|
|
160
|
-
60 //
|
|
182
|
+
60 // tags_count)
|
|
161
183
|
self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
|
|
162
184
|
dt_parse_iso("2020-01-01T00:01:00+00:00"),
|
|
163
185
|
tag_prefix="dummy"))),
|
|
164
186
|
60 + 1)
|
|
165
|
-
self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy:foo"))),
|
|
166
|
-
self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy:bar"))),
|
|
167
|
-
self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy"))),
|
|
187
|
+
self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy:foo"))), tag_records_count // tags_count)
|
|
188
|
+
self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy:bar"))), tag_records_count // tags_count)
|
|
189
|
+
self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy"))), tag_records_count)
|
|
168
190
|
|
|
169
191
|
target_cache.remove_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"), dt_parse_iso("2020-01-01T00:01:00+00:00"))
|
|
170
192
|
|
|
171
|
-
self.assertEqual(len(list(target_cache.iter_tags())),
|
|
172
|
-
self.assertEqual(len(list(cache.iter_tags())),
|
|
193
|
+
self.assertEqual(len(list(target_cache.iter_tags())), tag_records_count - (60 + 1))
|
|
194
|
+
self.assertEqual(len(list(cache.iter_tags())), tag_records_count - (60 + 1))
|
|
173
195
|
|
|
174
196
|
target_cache.remove_tags(tagsets=[tagset], tagset_inverted=True)
|
|
175
197
|
|
|
176
|
-
self.assertEqual(
|
|
177
|
-
|
|
198
|
+
self.assertEqual(
|
|
199
|
+
len(list(target_cache.iter_tags())),
|
|
200
|
+
tag_records_count * tagset_tags_count // tags_count - (60 * tagset_tags_count // tags_count + 1),
|
|
201
|
+
)
|
|
202
|
+
self.assertEqual(
|
|
203
|
+
len(list(cache.iter_tags())),
|
|
204
|
+
tag_records_count * tagset_tags_count // tags_count - (60 * tagset_tags_count // tags_count + 1),
|
|
205
|
+
)
|
|
178
206
|
|
|
179
207
|
target_cache.remove_tags()
|
|
180
208
|
|
|
@@ -182,17 +210,27 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
182
210
|
self.assertEqual(len(list(cache.iter_tags())), 0)
|
|
183
211
|
|
|
184
212
|
def test_tag_cache__multithread(self):
|
|
185
|
-
|
|
186
|
-
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
187
|
-
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
188
|
-
|
|
189
|
-
tags = [
|
|
213
|
+
tag_names = [
|
|
190
214
|
"dummy:foo",
|
|
191
215
|
"dummy:bar",
|
|
192
216
|
"dummy:baz",
|
|
193
217
|
"dummy:qux",
|
|
194
218
|
]
|
|
195
219
|
|
|
220
|
+
old_tagset = MutableTagset(namespace="tagset", version="1.0.0", desc="A dummy tagset for testing")
|
|
221
|
+
old_tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
222
|
+
old_tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
223
|
+
|
|
224
|
+
new_tagset = MutableTagset(namespace="tagset", version="1.1.0", desc="Updated dummy tagset for testing")
|
|
225
|
+
new_tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
226
|
+
new_tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
227
|
+
new_tagset.add(Tag(name="dummy:baz", desc="Updated dummy tag for testing"))
|
|
228
|
+
new_tagset.remove("dummy:bar")
|
|
229
|
+
|
|
230
|
+
tags_count = len(tag_names)
|
|
231
|
+
tagset_tags_count = sum(1 for tag_name in tag_names if tag_name in old_tagset or tag_name in new_tagset)
|
|
232
|
+
old_tagset_tags_count = sum(1 for tag_name in tag_names if tag_name in old_tagset)
|
|
233
|
+
|
|
196
234
|
with tempfile.TemporaryDirectory() as temp_directory:
|
|
197
235
|
temp_directory = pathlib.Path(temp_directory)
|
|
198
236
|
|
|
@@ -223,14 +261,16 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
223
261
|
start_barrier = threading.Barrier(total_threads_count)
|
|
224
262
|
|
|
225
263
|
def worker(tid):
|
|
226
|
-
|
|
264
|
+
target_cache = target_caches[tid % target_caches_count]
|
|
227
265
|
|
|
228
266
|
start_barrier.wait()
|
|
229
267
|
for i in range(tasks_count_per_thread):
|
|
230
|
-
|
|
268
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
269
|
+
tag = old_tagset.get_bound(tag_name) or new_tagset.get_bound(tag_name) or tag_name
|
|
270
|
+
target_cache.add_ranged_tag(
|
|
231
271
|
dt_parse_iso("2020-01-01T00:00:00+00:00") + datetime.timedelta(seconds=i),
|
|
232
272
|
dt_parse_iso("2020-01-02T00:00:00+00:00") + datetime.timedelta(seconds=i + 1),
|
|
233
|
-
|
|
273
|
+
tag,
|
|
234
274
|
)
|
|
235
275
|
|
|
236
276
|
threads = [threading.Thread(target=worker, args=(tid,)) for tid in range(total_threads_count)]
|
|
@@ -242,24 +282,35 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
242
282
|
for target_cache in target_caches:
|
|
243
283
|
self.assertEqual(len(list(target_cache.iter_tags())), tasks_count_per_target_cache)
|
|
244
284
|
self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy:bar"))),
|
|
245
|
-
tasks_count_per_target_cache //
|
|
285
|
+
tasks_count_per_target_cache // tags_count)
|
|
286
|
+
self.assertEqual(len(list(target_cache.iter_tags(tagset_namespace="tagset"))),
|
|
287
|
+
tasks_count_per_target_cache * tagset_tags_count // tags_count)
|
|
288
|
+
self.assertEqual(len(list(target_cache.iter_tags(tagset_namespace="tagset", tagset_version="1.0.0"))),
|
|
289
|
+
tasks_count_per_target_cache * old_tagset_tags_count // tags_count)
|
|
246
290
|
|
|
247
291
|
self.assertEqual(len(list(cache.iter_tag_and_targets())), total_tasks_count)
|
|
248
292
|
self.assertEqual(len(list(cache.iter_tag_and_targets(tag_prefix="dummy:bar"))),
|
|
249
|
-
total_tasks_count //
|
|
293
|
+
total_tasks_count // tags_count)
|
|
294
|
+
self.assertEqual(len(list(cache.iter_tag_and_targets(tagset_namespace="tagset"))),
|
|
295
|
+
total_tasks_count * tagset_tags_count // tags_count)
|
|
296
|
+
self.assertEqual(len(list(cache.iter_tag_and_targets(tagset_namespace="tagset", tagset_version="1.0.0"))),
|
|
297
|
+
total_tasks_count * old_tagset_tags_count // tags_count)
|
|
250
298
|
|
|
251
299
|
def test_tag_cache__clone(self):
|
|
252
|
-
|
|
253
|
-
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
254
|
-
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
255
|
-
|
|
256
|
-
tags = [
|
|
300
|
+
tag_names = [
|
|
257
301
|
"dummy:foo",
|
|
258
302
|
"dummy:bar",
|
|
259
303
|
"dummy:baz",
|
|
260
304
|
"dummy:qux",
|
|
261
305
|
]
|
|
262
306
|
|
|
307
|
+
tagset = MutableTagset(namespace="tagset", version="1.0.0", desc="A dummy tagset for testing")
|
|
308
|
+
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
309
|
+
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
310
|
+
|
|
311
|
+
tags_count = len(tag_names)
|
|
312
|
+
tagset_tags_count = sum(1 for tag_name in tag_names if tag_name in tagset)
|
|
313
|
+
|
|
263
314
|
with tempfile.TemporaryDirectory() as temp_directory:
|
|
264
315
|
temp_directory = pathlib.Path(temp_directory)
|
|
265
316
|
|
|
@@ -274,46 +325,55 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
274
325
|
|
|
275
326
|
src_target_cache = src_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
276
327
|
|
|
277
|
-
|
|
328
|
+
tag_records_count = 1000
|
|
278
329
|
|
|
279
|
-
for i in range(
|
|
280
|
-
|
|
330
|
+
for i in range(tag_records_count):
|
|
331
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
332
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
333
|
+
src_target_cache.add_tag(tag)
|
|
281
334
|
|
|
282
335
|
dst_cache = TagCache(file_path=temp_directory / "dst_tag_cache.db")
|
|
283
336
|
|
|
284
337
|
TagCache.copy_to(src_cache, dst_cache)
|
|
285
338
|
|
|
286
|
-
for i in range(
|
|
287
|
-
|
|
339
|
+
for i in range(tag_records_count):
|
|
340
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
341
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
342
|
+
src_target_cache.add_tag(tag)
|
|
288
343
|
|
|
289
344
|
dst_target_cache = dst_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
290
345
|
|
|
291
|
-
self.assertEqual(len(list(src_target_cache.iter_tags())),
|
|
292
|
-
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))),
|
|
346
|
+
self.assertEqual(len(list(src_target_cache.iter_tags())), tag_records_count * 2)
|
|
347
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))),
|
|
348
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
293
349
|
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
294
|
-
|
|
350
|
+
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
295
351
|
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
296
|
-
|
|
352
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
297
353
|
|
|
298
|
-
self.assertEqual(len(list(dst_target_cache.iter_tags())),
|
|
299
|
-
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))),
|
|
354
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags())), tag_records_count)
|
|
355
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))),
|
|
356
|
+
tag_records_count * tagset_tags_count // tags_count)
|
|
300
357
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
301
|
-
tags_count //
|
|
358
|
+
tag_records_count * (tags_count - tagset_tags_count) // tags_count)
|
|
302
359
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
303
|
-
|
|
360
|
+
tag_records_count * tagset_tags_count // tags_count)
|
|
304
361
|
|
|
305
362
|
def test_tag_cache__clone_same_file(self):
|
|
306
|
-
|
|
307
|
-
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
308
|
-
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
309
|
-
|
|
310
|
-
tags = [
|
|
363
|
+
tag_names = [
|
|
311
364
|
"dummy:foo",
|
|
312
365
|
"dummy:bar",
|
|
313
366
|
"dummy:baz",
|
|
314
367
|
"dummy:qux",
|
|
315
368
|
]
|
|
316
369
|
|
|
370
|
+
tagset = MutableTagset(namespace="tagset", version="1.0.0", desc="A dummy tagset for testing")
|
|
371
|
+
tagset.add(Tag(name="dummy:foo", desc="A dummy tag for testing"))
|
|
372
|
+
tagset.add(Tag(name="dummy:bar", desc="Another dummy tag for testing"))
|
|
373
|
+
|
|
374
|
+
tags_count = len(tag_names)
|
|
375
|
+
tagset_tags_count = sum(1 for tag_name in tag_names if tag_name in tagset)
|
|
376
|
+
|
|
317
377
|
# Clone to the same file path should not cause any issue, and the cloned cache should be able to read
|
|
318
378
|
# the tags added to the source cache after cloning.
|
|
319
379
|
with tempfile.TemporaryDirectory() as temp_directory:
|
|
@@ -330,33 +390,39 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
330
390
|
|
|
331
391
|
src_target_cache = src_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
332
392
|
|
|
333
|
-
|
|
393
|
+
tag_records_count = 1000
|
|
334
394
|
|
|
335
|
-
for i in range(
|
|
336
|
-
|
|
395
|
+
for i in range(tag_records_count):
|
|
396
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
397
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
398
|
+
src_target_cache.add_tag(tag)
|
|
337
399
|
|
|
338
400
|
dst_cache = TagCache(file_path=temp_directory / "tag_cache.db")
|
|
339
401
|
|
|
340
402
|
TagCache.copy_to(src_cache, dst_cache)
|
|
341
403
|
|
|
342
|
-
for i in range(
|
|
343
|
-
|
|
404
|
+
for i in range(tag_records_count):
|
|
405
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
406
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
407
|
+
src_target_cache.add_tag(tag)
|
|
344
408
|
|
|
345
409
|
dst_target_cache = dst_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
346
410
|
|
|
347
|
-
self.assertEqual(len(list(src_target_cache.iter_tags())),
|
|
348
|
-
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))),
|
|
411
|
+
self.assertEqual(len(list(src_target_cache.iter_tags())), tag_records_count * 2)
|
|
412
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))),
|
|
413
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
349
414
|
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
350
|
-
|
|
415
|
+
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
351
416
|
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
352
|
-
|
|
417
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
353
418
|
|
|
354
|
-
self.assertEqual(len(list(dst_target_cache.iter_tags())),
|
|
355
|
-
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))),
|
|
419
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags())), tag_records_count * 2)
|
|
420
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))),
|
|
421
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
356
422
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
357
|
-
|
|
423
|
+
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
358
424
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
359
|
-
|
|
425
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
360
426
|
|
|
361
427
|
with tempfile.TemporaryDirectory() as temp_directory:
|
|
362
428
|
temp_directory = pathlib.Path(temp_directory)
|
|
@@ -372,30 +438,36 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
372
438
|
|
|
373
439
|
src_target_cache = src_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
374
440
|
|
|
375
|
-
|
|
441
|
+
tag_records_count = 1000
|
|
376
442
|
|
|
377
|
-
for i in range(
|
|
378
|
-
|
|
443
|
+
for i in range(tag_records_count):
|
|
444
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
445
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
446
|
+
src_target_cache.add_tag(tag)
|
|
379
447
|
|
|
380
448
|
dst_cache = src_cache
|
|
381
449
|
|
|
382
450
|
TagCache.copy_to(src_cache, dst_cache)
|
|
383
451
|
|
|
384
|
-
for i in range(
|
|
385
|
-
|
|
452
|
+
for i in range(tag_records_count):
|
|
453
|
+
tag_name = tag_names[i % len(tag_names)]
|
|
454
|
+
tag = tagset.get_bound(tag_name) or tag_name
|
|
455
|
+
src_target_cache.add_tag(tag)
|
|
386
456
|
|
|
387
457
|
dst_target_cache = dst_cache.with_target("awesome_tagger/20200101_000000/dummy_vehicle/0")
|
|
388
458
|
|
|
389
|
-
self.assertEqual(len(list(src_target_cache.iter_tags())),
|
|
390
|
-
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))),
|
|
459
|
+
self.assertEqual(len(list(src_target_cache.iter_tags())), tag_records_count * 2)
|
|
460
|
+
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset]))),
|
|
461
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
391
462
|
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
392
|
-
|
|
463
|
+
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
393
464
|
self.assertEqual(len(list(src_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
394
|
-
|
|
465
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
395
466
|
|
|
396
|
-
self.assertEqual(len(list(dst_target_cache.iter_tags())),
|
|
397
|
-
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))),
|
|
467
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags())), tag_records_count * 2)
|
|
468
|
+
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset]))),
|
|
469
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
398
470
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
|
|
399
|
-
|
|
471
|
+
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
400
472
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
401
|
-
|
|
473
|
+
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import os.path
|
|
2
|
-
import unittest
|
|
3
|
-
|
|
4
|
-
import ddt
|
|
5
|
-
|
|
6
|
-
from plexus.common.utils.jsonutils import read_chunked_jsonl
|
|
7
|
-
from testenv import resources_directory
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@ddt.ddt
|
|
11
|
-
class JsonUtilsTest(unittest.TestCase):
|
|
12
|
-
|
|
13
|
-
def test_read_chunked_jsonl(self):
|
|
14
|
-
for data, path in read_chunked_jsonl(str(resources_directory / "unittest" / "jsonutils" / "dummy.{{}}.jsonl")):
|
|
15
|
-
self.assertEqual(data["file"], os.path.basename(path))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/0-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/1-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/2-dummy
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/resources/unittest/pathutils/dummy.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMFile.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMNode.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMTags.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/OSMWay.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/carto/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/resources/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/apiutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/config.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/datautils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/dockerutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/jsonutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/pathutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/s3utils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/sqlutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/strutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/src/plexus/common/utils/testutils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/pose_test.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.57 → plexus_python_common-1.0.59}/test/plexus_tests/common/proj_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|