plexus-python-common 1.0.70__tar.gz → 1.0.72__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 (91) hide show
  1. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/PKG-INFO +1 -1
  2. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/tagutils.py +268 -125
  3. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
  4. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/tagutils_test.py +72 -67
  5. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/.editorconfig +0 -0
  6. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/.github/workflows/pr.yml +0 -0
  7. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/.github/workflows/push.yml +0 -0
  8. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/.gitignore +0 -0
  9. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/MANIFEST.in +0 -0
  10. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/README.md +0 -0
  11. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/VERSION +0 -0
  12. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/pyproject.toml +0 -0
  13. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
  14. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
  15. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
  16. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/0-dummy +0 -0
  17. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/1-dummy +0 -0
  18. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/2-dummy +0 -0
  19. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.0.0.jsonl +0 -0
  20. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.0.0.vol-0.jsonl +0 -0
  21. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.0.jsonl +0 -0
  22. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.1.1.jsonl +0 -0
  23. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.1.1.vol-1.jsonl +0 -0
  24. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.1.jsonl +0 -0
  25. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.2.2.jsonl +0 -0
  26. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.2.2.vol-2.jsonl +0 -0
  27. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.2.jsonl +0 -0
  28. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.csv.part0 +0 -0
  29. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.csv.part1 +0 -0
  30. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.csv.part2 +0 -0
  31. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/pathutils/dummy.txt +0 -0
  32. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
  33. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
  34. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
  35. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  36. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  37. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  38. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  39. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
  40. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
  41. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
  42. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
  43. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
  44. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/setup.cfg +0 -0
  45. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/setup.py +0 -0
  46. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/__init__.py +0 -0
  47. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/carto/OSMFile.py +0 -0
  48. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/carto/OSMNode.py +0 -0
  49. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/carto/OSMTags.py +0 -0
  50. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/carto/OSMWay.py +0 -0
  51. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/carto/__init__.py +0 -0
  52. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/resources/__init__.py +0 -0
  53. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/resources/tags/__init__.py +0 -0
  54. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/resources/tags/unittest-1.0.0.tagset.yaml +0 -0
  55. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/resources/tags/universal-1.0.0.tagset.yaml +0 -0
  56. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/__init__.py +0 -0
  57. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/apiutils.py +0 -0
  58. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/bagutils.py +0 -0
  59. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/config.py +0 -0
  60. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/datautils.py +0 -0
  61. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/dockerutils.py +0 -0
  62. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/gisutils.py +0 -0
  63. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/jsonutils.py +0 -0
  64. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/ormutils.py +0 -0
  65. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/pathutils.py +0 -0
  66. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/s3utils.py +0 -0
  67. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/sqlutils.py +0 -0
  68. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/strutils.py +0 -0
  69. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus/common/utils/testutils.py +0 -0
  70. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
  71. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
  72. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
  73. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus_python_common.egg-info/requires.txt +0 -0
  74. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/src/plexus_python_common.egg-info/top_level.txt +0 -0
  75. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/__init__.py +0 -0
  76. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/__init__.py +0 -0
  77. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/carto/__init__.py +0 -0
  78. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
  79. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
  80. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/__init__.py +0 -0
  81. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
  82. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/datautils_test.py +0 -0
  83. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/dockerutils_test.py +0 -0
  84. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/gisutils_test.py +0 -0
  85. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
  86. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
  87. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/pathutils_test.py +0 -0
  88. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/s3utils_test.py +0 -0
  89. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/strutils_test.py +0 -0
  90. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/plexus_tests/common/utils/testutils_test.py +0 -0
  91. {plexus_python_common-1.0.70 → plexus_python_common-1.0.72}/test/testenv.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.70
3
+ Version: 1.0.72
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -33,14 +33,14 @@ from plexus.common.resources.tags import predefined_tagset_specs
33
33
  from plexus.common.utils.datautils import validate_colon_tag, validate_snake_case, validate_vehicle_name
34
34
  from plexus.common.utils.datautils import validate_dt_timezone, validate_semver, validate_slash_tag
35
35
  from plexus.common.utils.jsonutils import json_datetime_encoder
36
- from plexus.common.utils.ormutils import SQLiteDateTime
37
- from plexus.common.utils.ormutils import SequenceModelMixinProtocol
36
+ from plexus.common.utils.ormutils import SQLiteDateTime, SequenceModelMixinProtocol
38
37
  from plexus.common.utils.ormutils import clone_sequence_model_instance, make_base_model, make_sequence_model_mixin
39
38
  from plexus.common.utils.sqlutils import escape_sql_like
40
39
 
41
40
  __all__ = [
42
41
  "RichDesc",
43
42
  "Tag",
43
+ "BoundTag",
44
44
  "Tagset",
45
45
  "MutableTagset",
46
46
  "populate_tagset",
@@ -894,7 +894,7 @@ class TagCache(object):
894
894
  *,
895
895
  tagsets: Sequence[Tagset] | None = None,
896
896
  tagset_inverted: bool = False,
897
- ):
897
+ ) -> Self:
898
898
  """
899
899
  Remove tag records from the cache that match the specified filters.
900
900
 
@@ -906,6 +906,7 @@ class TagCache(object):
906
906
  :param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
907
907
  :param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
908
908
  tagsets)
909
+ :return: Self for chaining
909
910
  """
910
911
  with self.make_session() as session:
911
912
  query_stmt = session.query(TagRecordTable)
@@ -930,86 +931,20 @@ class TagCache(object):
930
931
  query_stmt.delete()
931
932
  session.commit()
932
933
 
933
- def clear(self):
934
- with self.make_session() as session:
935
- session.execute(sa.delete(TagRecordTable))
936
- session.execute(sa.delete(TagTargetTable))
937
- session.commit()
938
-
939
- def append_to(self, target_file_path: str, *, overwrite: bool = False):
940
- target_tag_cache = TagCache(file_path=target_file_path)
941
- if overwrite:
942
- target_tag_cache.clear()
943
- TagCache.copy_to(self, target_tag_cache)
944
-
945
- def merge_from(self, source_file_path: str, *, overwrite: bool = False):
946
- source_tag_cache = TagCache(file_path=source_file_path)
947
- if overwrite:
948
- self.clear()
949
- TagCache.copy_to(source_tag_cache, self)
950
-
951
- @staticmethod
952
- def copy_to(src: "TagCache", dst: "TagCache"):
953
- # If src and dst are the same instance or point to the same file path,
954
- # do nothing to avoid accidentally clearing the cache
955
- if src == dst or src.file_path == dst.file_path:
956
- return
957
-
958
- with src.make_session() as src_session, dst.make_session() as dst_session:
959
- src_db_tag_targets = src_session.query(TagTargetTable).all()
960
- dst_db_tag_targets = [
961
- clone_sequence_model_instance(TagTargetTable, db_tag_target, clear_meta_fields=True)
962
- for db_tag_target in src_db_tag_targets
963
- ]
964
- dst_session.add_all(dst_db_tag_targets)
965
- dst_session.flush() # ensure new sqn values are assigned
966
-
967
- sqn_map = {src_db_tag_target.sqn: dst_db_tag_target.sqn
968
- for src_db_tag_target, dst_db_tag_target in zip(src_db_tag_targets, dst_db_tag_targets)}
969
-
970
- for db_tag_records in batched(src_session.query(TagRecordTable).yield_per(1000), 1000):
971
- cloned_db_tag_records = []
972
- for db_tag_record in db_tag_records:
973
- cloned_db_tag_record = clone_sequence_model_instance(TagRecordTable,
974
- db_tag_record,
975
- clear_meta_fields=True)
976
- try:
977
- cloned_db_tag_record.target_sqn = sqn_map[db_tag_record.target_sqn]
978
- except KeyError as e:
979
- raise ValueError(f"no cloned tag target for target_sqn '{db_tag_record.target_sqn}'") from e
980
- cloned_db_tag_records.append(cloned_db_tag_record)
981
- dst_session.add_all(cloned_db_tag_records)
982
- dst_session.commit()
983
-
984
- count_tags = count_records
985
- iter_tags = iter_records
986
- iter_tag_and_targets = iter_record_and_targets
987
- remove_tags = remove_records
988
-
989
-
990
- class TargetedTagCache(object):
991
- def __init__(self, cache: TagCache, tag_target: TagTargetTable):
992
- self.cache = cache
993
- self.tag_target = tag_target
994
-
995
- @contextlib.contextmanager
996
- def make_session(self) -> Generator[sa_orm.Session, None, None]:
997
- with self.cache.make_session() as session:
998
- if not session.query(TagTargetTable).filter(TagTargetTable.sqn == self.tag_target.sqn).first():
999
- raise ValueError(f"tag target with sqn '{self.tag_target.sqn}' is no longer present in cache")
1000
- yield session
934
+ return self
1001
935
 
1002
- def count_records(self) -> int:
936
+ def target_count_records(self, target_sqn: int) -> int:
1003
937
  with self.make_session() as session:
1004
938
  return (
1005
939
  session
1006
940
  .query(sa.func.count(TagRecordTable.sqn))
1007
- .filter(TagRecordTable.target_sqn == self.tag_target.sqn)
941
+ .filter(TagRecordTable.target_sqn == target_sqn)
1008
942
  .scalar()
1009
943
  )
1010
944
 
1011
- def iter_records(
945
+ def target_iter_records(
1012
946
  self,
947
+ target_sqn: int,
1013
948
  begin_dt: datetime.datetime | None = None,
1014
949
  end_dt: datetime.datetime | None = None,
1015
950
  tagset_namespace: str | None = None,
@@ -1025,6 +960,7 @@ class TargetedTagCache(object):
1025
960
  """
1026
961
  Query tag records in the cache with optional filters.
1027
962
 
963
+ :param target_sqn: Filter by target sequence number (exact match)
1028
964
  :param begin_dt: Filter by begin time (inclusive)
1029
965
  :param end_dt: Filter by end time (inclusive)
1030
966
  :param tagset_namespace: Filter by tagset namespace (exact match)
@@ -1040,7 +976,7 @@ class TargetedTagCache(object):
1040
976
  """
1041
977
 
1042
978
  with self.make_session() as session:
1043
- query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn == self.tag_target.sqn)
979
+ query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn == target_sqn)
1044
980
  if begin_dt:
1045
981
  query_stmt = query_stmt.filter(TagRecordTable.end_dt >= begin_dt)
1046
982
  if end_dt:
@@ -1068,19 +1004,20 @@ class TargetedTagCache(object):
1068
1004
  for result in query_stmt.yield_per(batch_size):
1069
1005
  yield result
1070
1006
 
1071
- def add_ranged_record(
1007
+ def target_add_record(
1072
1008
  self,
1073
- begin_dt: datetime.datetime | None,
1074
- end_dt: datetime.datetime | None,
1009
+ target_sqn: int,
1010
+ begin_dt: datetime.datetime,
1011
+ end_dt: datetime.datetime,
1075
1012
  tag: str | Tag | BoundTag,
1076
1013
  props: JsonType | None = None,
1077
1014
  tagset_namespace: str | None = None,
1078
1015
  tagset_version: str | None = None,
1079
1016
  ) -> Chainable[Self, TagRecordTable]:
1080
1017
  """
1081
- Add a tag record to the cache for the specified time range. If ``begin_dt`` or ``end_dt`` is None, it will
1082
- default to the target's ``begin_dt`` or ``end_dt`` respectively.
1018
+ Add a tag record to the cache for the specified time range.
1083
1019
 
1020
+ :param target_sqn: Sequence number of the tag record's target
1084
1021
  :param begin_dt: Begin datetime of the tag record
1085
1022
  :param end_dt: End datetime of the tag record
1086
1023
  :param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
@@ -1091,13 +1028,13 @@ class TargetedTagCache(object):
1091
1028
  instance will be used.
1092
1029
  :param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a ``BoundTag``
1093
1030
  instance, this parameter will be ignored and the version from the instance will be used.
1094
- :return: Self instance for chaining
1031
+ :return: Self instance for chaining along with the added ``TagRecordTable`` instance
1095
1032
  """
1096
1033
  with self.make_session() as session:
1097
1034
  tag_record = TagRecord(
1098
- target_sqn=self.tag_target.sqn,
1099
- begin_dt=begin_dt or self.tag_target.begin_dt,
1100
- end_dt=end_dt or self.tag_target.end_dt,
1035
+ target_sqn=target_sqn,
1036
+ begin_dt=begin_dt,
1037
+ end_dt=end_dt,
1101
1038
  tagset_namespace=tag.namespace if isinstance(tag, BoundTag) else tagset_namespace,
1102
1039
  tagset_version=tag.version if isinstance(tag, BoundTag) else tagset_version,
1103
1040
  tag=tag.name if isinstance(tag, Tag) else tag,
@@ -1109,38 +1046,9 @@ class TargetedTagCache(object):
1109
1046
 
1110
1047
  return chainable(self, db_tag_record)
1111
1048
 
1112
- def add_record(
1113
- self,
1114
- tag: str | Tag | BoundTag,
1115
- props: JsonType | None = None,
1116
- tagset_namespace: str | None = None,
1117
- tagset_version: str | None = None,
1118
- ) -> Chainable[Self, TagRecordTable]:
1119
- """
1120
- Add a tag record to the cache for the entire target range.
1121
-
1122
- :param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
1123
- its name will be used.
1124
- :param props: Additional properties of the tag record in JSON format (optional)
1125
- :param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
1126
- ``BoundTag`` instance, this parameter will be ignored and the namespace from the
1127
- instance will be used.
1128
- :param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a
1129
- ``BoundTag`` instance, this parameter will be ignored and the version from the instance
1130
- will be used.
1131
- :return: Self instance for chaining
1132
- """
1133
- return self.add_ranged_record(
1134
- begin_dt=self.tag_target.begin_dt,
1135
- end_dt=self.tag_target.end_dt,
1136
- tag=tag,
1137
- tagset_namespace=tagset_namespace,
1138
- tagset_version=tagset_version,
1139
- props=props,
1140
- )
1141
-
1142
- def update_record(
1049
+ def target_update_record(
1143
1050
  self,
1051
+ target_sqn: int,
1144
1052
  sqn: int,
1145
1053
  *,
1146
1054
  begin_dt: datetime.datetime | None = None,
@@ -1154,6 +1062,7 @@ class TargetedTagCache(object):
1154
1062
  """
1155
1063
  Update a tag record in the cache by its sequence number.
1156
1064
 
1065
+ :param target_sqn: Sequence number of the tag record's target
1157
1066
  :param sqn: Sequence number of the tag record to be updated
1158
1067
  :param begin_dt: New begin datetime of the tag record (optional)
1159
1068
  :param end_dt: New end datetime of the tag record (optional)
@@ -1167,10 +1076,15 @@ class TargetedTagCache(object):
1167
1076
  is a ``BoundTag`` instance, this parameter will be ignored and the version from the
1168
1077
  instance will be used.
1169
1078
  :param flags: New integer bitmask storing status or metadata flags for this tag record (optional)
1170
- :return: Self instance for chaining
1079
+ :return: Self instance for chaining along with the updated ``TagRecordTable`` instance
1171
1080
  """
1172
1081
  with self.make_session() as session:
1173
- db_tag_record = session.query(TagRecordTable).filter(TagRecordTable.sqn == sqn).one_or_none()
1082
+ db_tag_record = (
1083
+ session
1084
+ .query(TagRecordTable)
1085
+ .filter(TagRecordTable.target_sqn == target_sqn, TagRecordTable.sqn == sqn)
1086
+ .one_or_none()
1087
+ )
1174
1088
  if not db_tag_record:
1175
1089
  raise ValueError(f"tag record with sqn '{sqn}' not found in cache")
1176
1090
 
@@ -1193,8 +1107,9 @@ class TargetedTagCache(object):
1193
1107
 
1194
1108
  return chainable(self, db_tag_record)
1195
1109
 
1196
- def remove_records(
1110
+ def target_remove_records(
1197
1111
  self,
1112
+ target_sqn: int,
1198
1113
  begin_dt: datetime.datetime | None = None,
1199
1114
  end_dt: datetime.datetime | None = None,
1200
1115
  tagset_namespace: str | None = None,
@@ -1207,6 +1122,7 @@ class TargetedTagCache(object):
1207
1122
  """
1208
1123
  Remove tag records from the cache that match the specified filters.
1209
1124
 
1125
+ :param target_sqn: Filter by target sequence number (exact match)
1210
1126
  :param begin_dt: Filter by begin time (inclusive)
1211
1127
  :param end_dt: Filter by end time (inclusive)
1212
1128
  :param tagset_namespace: Filter by tagset namespace (exact match)
@@ -1218,7 +1134,7 @@ class TargetedTagCache(object):
1218
1134
  :return: Self instance for chaining
1219
1135
  """
1220
1136
  with self.make_session() as session:
1221
- query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn == self.tag_target.sqn)
1137
+ query_stmt = session.query(TagRecordTable).filter(TagRecordTable.target_sqn == target_sqn)
1222
1138
  if begin_dt:
1223
1139
  query_stmt = query_stmt.filter(TagRecordTable.end_dt >= begin_dt)
1224
1140
  if end_dt:
@@ -1242,12 +1158,239 @@ class TargetedTagCache(object):
1242
1158
 
1243
1159
  return self
1244
1160
 
1245
- count_tags = count_records
1246
- iter_tags = iter_records
1247
- add_ranged_tag = add_ranged_record
1248
- add_tag = add_record
1249
- update_tag = update_record
1250
- remove_tags = remove_records
1161
+ def clear(self):
1162
+ with self.make_session() as session:
1163
+ session.execute(sa.delete(TagRecordTable))
1164
+ session.execute(sa.delete(TagTargetTable))
1165
+ session.commit()
1166
+
1167
+ def append_to(self, target_file_path: str, *, overwrite: bool = False):
1168
+ target_tag_cache = TagCache(file_path=target_file_path)
1169
+ if overwrite:
1170
+ target_tag_cache.clear()
1171
+ TagCache.copy_to(self, target_tag_cache)
1172
+
1173
+ def merge_from(self, source_file_path: str, *, overwrite: bool = False):
1174
+ source_tag_cache = TagCache(file_path=source_file_path)
1175
+ if overwrite:
1176
+ self.clear()
1177
+ TagCache.copy_to(source_tag_cache, self)
1178
+
1179
+ @staticmethod
1180
+ def copy_to(src: "TagCache", dst: "TagCache"):
1181
+ # If src and dst are the same instance or point to the same file path,
1182
+ # do nothing to avoid accidentally clearing the cache
1183
+ if src == dst or src.file_path == dst.file_path:
1184
+ return
1185
+
1186
+ with src.make_session() as src_session, dst.make_session() as dst_session:
1187
+ src_db_tag_targets = src_session.query(TagTargetTable).all()
1188
+ dst_db_tag_targets = [
1189
+ clone_sequence_model_instance(TagTargetTable, db_tag_target, clear_meta_fields=True)
1190
+ for db_tag_target in src_db_tag_targets
1191
+ ]
1192
+ dst_session.add_all(dst_db_tag_targets)
1193
+ dst_session.flush() # ensure new sqn values are assigned
1194
+
1195
+ sqn_map = {src_db_tag_target.sqn: dst_db_tag_target.sqn
1196
+ for src_db_tag_target, dst_db_tag_target in zip(src_db_tag_targets, dst_db_tag_targets)}
1197
+
1198
+ for db_tag_records in batched(src_session.query(TagRecordTable).yield_per(1000), 1000):
1199
+ cloned_db_tag_records = []
1200
+ for db_tag_record in db_tag_records:
1201
+ cloned_db_tag_record = clone_sequence_model_instance(TagRecordTable,
1202
+ db_tag_record,
1203
+ clear_meta_fields=True)
1204
+ try:
1205
+ cloned_db_tag_record.target_sqn = sqn_map[db_tag_record.target_sqn]
1206
+ except KeyError as e:
1207
+ raise ValueError(f"no cloned tag target for target_sqn '{db_tag_record.target_sqn}'") from e
1208
+ cloned_db_tag_records.append(cloned_db_tag_record)
1209
+ dst_session.add_all(cloned_db_tag_records)
1210
+ dst_session.commit()
1211
+
1212
+
1213
+ class TargetedTagCache(object):
1214
+ def __init__(self, cache: TagCache, tag_target: TagTargetTable):
1215
+ self.cache = cache
1216
+ self.tag_target = tag_target
1217
+
1218
+ def count_records(self) -> int:
1219
+ return self.cache.target_count_records(self.tag_target.sqn)
1220
+
1221
+ def iter_records(
1222
+ self,
1223
+ begin_dt: datetime.datetime | None = None,
1224
+ end_dt: datetime.datetime | None = None,
1225
+ tagset_namespace: str | None = None,
1226
+ tagset_version: str | None = None,
1227
+ tag_prefix: str | None = None,
1228
+ *,
1229
+ tagsets: Sequence[Tagset] | None = None,
1230
+ tagset_inverted: bool = False,
1231
+ skip: int | None = None,
1232
+ limit: int | None = None,
1233
+ batch_size: int = 1000,
1234
+ ) -> Generator[TagRecordTable, None, None]:
1235
+ return self.cache.target_iter_records(
1236
+ target_sqn=self.tag_target.sqn,
1237
+ begin_dt=begin_dt,
1238
+ end_dt=end_dt,
1239
+ tagset_namespace=tagset_namespace,
1240
+ tagset_version=tagset_version,
1241
+ tag_prefix=tag_prefix,
1242
+ tagsets=tagsets,
1243
+ tagset_inverted=tagset_inverted,
1244
+ skip=skip,
1245
+ limit=limit,
1246
+ batch_size=batch_size,
1247
+ )
1248
+
1249
+ def add_ranged_record(
1250
+ self,
1251
+ begin_dt: datetime.datetime | None,
1252
+ end_dt: datetime.datetime | None,
1253
+ tag: str | Tag | BoundTag,
1254
+ props: JsonType | None = None,
1255
+ tagset_namespace: str | None = None,
1256
+ tagset_version: str | None = None,
1257
+ ) -> Chainable[Self, TagRecordTable]:
1258
+ """
1259
+ Add a tag record to the cache for the specified time range. If ``begin_dt`` or ``end_dt`` is None, it will
1260
+ default to the target's ``begin_dt`` or ``end_dt`` respectively.
1261
+
1262
+ :param begin_dt: Begin datetime of the tag record
1263
+ :param end_dt: End datetime of the tag record
1264
+ :param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
1265
+ its name will be used.
1266
+ :param props: Additional properties of the tag record in JSON format (optional)
1267
+ :param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
1268
+ ``BoundTag`` instance, this parameter will be ignored and the namespace from the
1269
+ instance will be used.
1270
+ :param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a ``BoundTag``
1271
+ instance, this parameter will be ignored and the version from the instance will be used.
1272
+ :return: Self instance for chaining along with the added ``TagRecordTable`` instance
1273
+ """
1274
+ result = self.cache.target_add_record(
1275
+ target_sqn=self.tag_target.sqn,
1276
+ begin_dt=begin_dt or self.tag_target.begin_dt,
1277
+ end_dt=end_dt or self.tag_target.end_dt,
1278
+ tag=tag,
1279
+ props=props,
1280
+ tagset_namespace=tagset_namespace,
1281
+ tagset_version=tagset_version,
1282
+ )
1283
+ return result.retarget(self)
1284
+
1285
+ def add_record(
1286
+ self,
1287
+ tag: str | Tag | BoundTag,
1288
+ props: JsonType | None = None,
1289
+ tagset_namespace: str | None = None,
1290
+ tagset_version: str | None = None,
1291
+ ) -> Chainable[Self, TagRecordTable]:
1292
+ """
1293
+ Add a tag record to the cache for the entire target range.
1294
+
1295
+ :param tag: Tag name or ``Tag``/``BoundTag`` instance to be added. If ``Tag``/``BoundTag`` instance is provided,
1296
+ its name will be used.
1297
+ :param props: Additional properties of the tag record in JSON format (optional)
1298
+ :param tagset_namespace: Namespace of the tagset that the tag belongs to. If the ``tag`` parameter is a
1299
+ ``BoundTag`` instance, this parameter will be ignored and the namespace from the
1300
+ instance will be used.
1301
+ :param tagset_version: Version of the tagset that the tag belongs to. If the ``tag`` parameter is a
1302
+ ``BoundTag`` instance, this parameter will be ignored and the version from the instance
1303
+ will be used.
1304
+ :return: Self instance for chaining along with the added ``TagRecordTable`` instance
1305
+ """
1306
+ result = self.add_ranged_record(
1307
+ begin_dt=self.tag_target.begin_dt,
1308
+ end_dt=self.tag_target.end_dt,
1309
+ tag=tag,
1310
+ tagset_namespace=tagset_namespace,
1311
+ tagset_version=tagset_version,
1312
+ props=props,
1313
+ )
1314
+ return result.retarget(self)
1315
+
1316
+ def update_record(
1317
+ self,
1318
+ sqn: int,
1319
+ *,
1320
+ begin_dt: datetime.datetime | None = None,
1321
+ end_dt: datetime.datetime | None = None,
1322
+ tag: str | Tag | BoundTag | None = None,
1323
+ props: JsonType | None = None,
1324
+ tagset_namespace: str | None = None,
1325
+ tagset_version: str | None = None,
1326
+ flags: int | None = None,
1327
+ ) -> Chainable[Self, TagRecordTable]:
1328
+ """
1329
+ Update a tag record in the cache by its sequence number.
1330
+
1331
+ :param sqn: Sequence number of the tag record to be updated
1332
+ :param begin_dt: New begin datetime of the tag record (optional)
1333
+ :param end_dt: New end datetime of the tag record (optional)
1334
+ :param tag: New tag name or ``Tag``/``BoundTag`` instance to be updated (optional). If ``Tag``/``BoundTag``
1335
+ instance is provided, its name will be used.
1336
+ :param props: New additional properties of the tag record in JSON format (optional)
1337
+ :param tagset_namespace: New namespace of the tagset that the tag belongs to (optional). If the ``tag``
1338
+ parameter is a ``BoundTag`` instance, this parameter will be ignored and the namespace
1339
+ from the instance will be used.
1340
+ :param tagset_version: New version of the tagset that the tag belongs to (optional). If the ``tag`` parameter
1341
+ is a ``BoundTag`` instance, this parameter will be ignored and the version from the
1342
+ instance will be used.
1343
+ :param flags: New integer bitmask storing status or metadata flags for this tag record (optional)
1344
+ :return: Self instance for chaining along with the updated ``TagRecordTable`` instance
1345
+ """
1346
+ result = self.cache.target_update_record(
1347
+ self.tag_target.sqn,
1348
+ sqn,
1349
+ begin_dt=begin_dt,
1350
+ end_dt=end_dt,
1351
+ tag=tag,
1352
+ props=props,
1353
+ tagset_namespace=tagset_namespace,
1354
+ tagset_version=tagset_version,
1355
+ flags=flags,
1356
+ )
1357
+ return result.retarget(self)
1358
+
1359
+ def remove_records(
1360
+ self,
1361
+ begin_dt: datetime.datetime | None = None,
1362
+ end_dt: datetime.datetime | None = None,
1363
+ tagset_namespace: str | None = None,
1364
+ tagset_version: str | None = None,
1365
+ tag_prefix: str | None = None,
1366
+ *,
1367
+ tagsets: Sequence[Tagset] | None = None,
1368
+ tagset_inverted: bool = False,
1369
+ ) -> Self:
1370
+ """
1371
+ Remove tag records from the cache that match the specified filters.
1372
+
1373
+ :param begin_dt: Filter by begin time (inclusive)
1374
+ :param end_dt: Filter by end time (inclusive)
1375
+ :param tagset_namespace: Filter by tagset namespace (exact match)
1376
+ :param tagset_version: Filter by tagset version (exact match)
1377
+ :param tag_prefix: Filter by tag name prefix, e.g. "dummy_tag:" to match all tags starting with "dummy_tag:"
1378
+ :param tagsets: Filter by tagsets (match tags that are in any of the specified tagsets)
1379
+ :param tagset_inverted: Whether to invert the tagset filter (match tags that are NOT in any of the specified
1380
+ tagsets)
1381
+ :return: Self instance for chaining
1382
+ """
1383
+ self.cache.target_remove_records(
1384
+ self.tag_target.sqn,
1385
+ begin_dt=begin_dt,
1386
+ end_dt=end_dt,
1387
+ tagset_namespace=tagset_namespace,
1388
+ tagset_version=tagset_version,
1389
+ tag_prefix=tag_prefix,
1390
+ tagsets=tagsets,
1391
+ tagset_inverted=tagset_inverted,
1392
+ )
1393
+ return self
1251
1394
 
1252
1395
 
1253
1396
  @memorized
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.70
3
+ Version: 1.0.72
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -131,7 +131,7 @@ class TagUtilsTest(unittest.TestCase):
131
131
  for i in range(tag_records_count):
132
132
  tag_name = tag_names[i % len(tag_names)]
133
133
  tag = tagset.get_bound(tag_name) or tag_name
134
- target_cache.add_ranged_tag(
134
+ target_cache.add_ranged_record(
135
135
  dt_parse_iso("2020-01-01T00:00:00+00:00") + datetime.timedelta(seconds=i),
136
136
  dt_parse_iso("2020-01-02T00:00:00+00:00") + datetime.timedelta(seconds=i + 1),
137
137
  tag,
@@ -150,92 +150,93 @@ class TagUtilsTest(unittest.TestCase):
150
150
  },
151
151
  )
152
152
 
153
- self.assertEqual(target_cache.count_tags(), tag_records_count)
154
- self.assertEqual(len(list(target_cache.iter_tags())), tag_records_count)
155
- self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset]))),
153
+ self.assertEqual(target_cache.count_records(), tag_records_count)
154
+ self.assertEqual(len(list(target_cache.iter_records())), tag_records_count)
155
+ self.assertEqual(len(list(target_cache.iter_records(tagsets=[tagset]))),
156
156
  tag_records_count * tagset_tags_count // tags_count)
157
- self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
157
+ self.assertEqual(len(list(target_cache.iter_records(tagsets=[tagset], tagset_inverted=True))),
158
158
  tag_records_count * (tags_count - tagset_tags_count) // tags_count)
159
- self.assertEqual(len(list(target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
159
+ self.assertEqual(len(list(target_cache.iter_records(tagsets=[tagset], tagset_inverted=False))),
160
160
  tag_records_count * tagset_tags_count // tags_count)
161
- self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
162
- dt_parse_iso("2020-01-01T00:01:00+00:00")))),
161
+ self.assertEqual(len(list(target_cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
162
+ dt_parse_iso("2020-01-01T00:01:00+00:00")))),
163
163
  60 + 1)
164
- self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
165
- dt_parse_iso("2020-01-01T00:01:00+00:00"),
166
- tag_prefix="dummy:foo"))),
164
+ self.assertEqual(len(list(target_cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
165
+ dt_parse_iso("2020-01-01T00:01:00+00:00"),
166
+ tag_prefix="dummy:foo"))),
167
167
  60 // tags_count + 1)
168
- self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
169
- dt_parse_iso("2020-01-01T00:01:00+00:00"),
170
- tag_prefix="dummy:bar"))),
168
+ self.assertEqual(len(list(target_cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
169
+ dt_parse_iso("2020-01-01T00:01:00+00:00"),
170
+ tag_prefix="dummy:bar"))),
171
171
  60 // tags_count)
172
- self.assertEqual(len(list(target_cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
173
- dt_parse_iso("2020-01-01T00:01:00+00:00"),
174
- tag_prefix="dummy"))),
172
+ self.assertEqual(len(list(target_cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
173
+ dt_parse_iso("2020-01-01T00:01:00+00:00"),
174
+ tag_prefix="dummy"))),
175
175
  60 + 1)
176
- self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy:foo"))),
176
+ self.assertEqual(len(list(target_cache.iter_records(tag_prefix="dummy:foo"))),
177
177
  tag_records_count // tags_count)
178
- self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy:bar"))),
178
+ self.assertEqual(len(list(target_cache.iter_records(tag_prefix="dummy:bar"))),
179
179
  tag_records_count // tags_count)
180
- self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy"))), tag_records_count)
180
+ self.assertEqual(len(list(target_cache.iter_records(tag_prefix="dummy"))), tag_records_count)
181
181
 
182
- self.assertEqual(len(list(cache.iter_tags())), tag_records_count)
183
- self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset]))), tag_records_count // 2)
184
- self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset], tagset_inverted=True))),
182
+ self.assertEqual(len(list(cache.iter_records())), tag_records_count)
183
+ self.assertEqual(len(list(cache.iter_records(tagsets=[tagset]))), tag_records_count // 2)
184
+ self.assertEqual(len(list(cache.iter_records(tagsets=[tagset], tagset_inverted=True))),
185
185
  tag_records_count * (tags_count - tagset_tags_count) // tags_count)
186
- self.assertEqual(len(list(cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
186
+ self.assertEqual(len(list(cache.iter_records(tagsets=[tagset], tagset_inverted=False))),
187
187
  tag_records_count * tagset_tags_count // tags_count)
188
- self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
189
- dt_parse_iso("2020-01-01T00:01:00+00:00")))),
188
+ self.assertEqual(len(list(cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
189
+ dt_parse_iso("2020-01-01T00:01:00+00:00")))),
190
190
  60 + 1)
191
- self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
192
- dt_parse_iso("2020-01-01T00:01:00+00:00"),
193
- tag_prefix="dummy:foo"))),
191
+ self.assertEqual(len(list(cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
192
+ dt_parse_iso("2020-01-01T00:01:00+00:00"),
193
+ tag_prefix="dummy:foo"))),
194
194
  60 // tags_count + 1)
195
- self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
196
- dt_parse_iso("2020-01-01T00:01:00+00:00"),
197
- tag_prefix="dummy:bar"))),
195
+ self.assertEqual(len(list(cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
196
+ dt_parse_iso("2020-01-01T00:01:00+00:00"),
197
+ tag_prefix="dummy:bar"))),
198
198
  60 // tags_count)
199
- self.assertEqual(len(list(cache.iter_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"),
200
- dt_parse_iso("2020-01-01T00:01:00+00:00"),
201
- tag_prefix="dummy"))),
199
+ self.assertEqual(len(list(cache.iter_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
200
+ dt_parse_iso("2020-01-01T00:01:00+00:00"),
201
+ tag_prefix="dummy"))),
202
202
  60 + 1)
203
- self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy:foo"))), tag_records_count // tags_count)
204
- self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy:bar"))), tag_records_count // tags_count)
205
- self.assertEqual(len(list(cache.iter_tags(tag_prefix="dummy"))), tag_records_count)
203
+ self.assertEqual(len(list(cache.iter_records(tag_prefix="dummy:foo"))), tag_records_count // tags_count)
204
+ self.assertEqual(len(list(cache.iter_records(tag_prefix="dummy:bar"))), tag_records_count // tags_count)
205
+ self.assertEqual(len(list(cache.iter_records(tag_prefix="dummy"))), tag_records_count)
206
206
 
207
- target_cache.remove_tags(dt_parse_iso("2020-01-01T00:00:00+00:00"), dt_parse_iso("2020-01-01T00:01:00+00:00"))
207
+ target_cache.remove_records(dt_parse_iso("2020-01-01T00:00:00+00:00"),
208
+ dt_parse_iso("2020-01-01T00:01:00+00:00"))
208
209
 
209
- self.assertEqual(target_cache.count_tags(), tag_records_count - (60 + 1))
210
- self.assertEqual(len(list(target_cache.iter_tags())), tag_records_count - (60 + 1))
211
- self.assertEqual(cache.count_tags(), tag_records_count - (60 + 1))
212
- self.assertEqual(len(list(cache.iter_tags())), tag_records_count - (60 + 1))
210
+ self.assertEqual(target_cache.count_records(), tag_records_count - (60 + 1))
211
+ self.assertEqual(len(list(target_cache.iter_records())), tag_records_count - (60 + 1))
212
+ self.assertEqual(cache.count_records(), tag_records_count - (60 + 1))
213
+ self.assertEqual(len(list(cache.iter_records())), tag_records_count - (60 + 1))
213
214
 
214
- target_cache.remove_tags(tagsets=[tagset], tagset_inverted=True)
215
+ target_cache.remove_records(tagsets=[tagset], tagset_inverted=True)
215
216
 
216
217
  self.assertEqual(
217
- target_cache.count_tags(),
218
+ target_cache.count_records(),
218
219
  tag_records_count * tagset_tags_count // tags_count - (60 * tagset_tags_count // tags_count + 1),
219
220
  )
220
221
  self.assertEqual(
221
- len(list(target_cache.iter_tags())),
222
+ len(list(target_cache.iter_records())),
222
223
  tag_records_count * tagset_tags_count // tags_count - (60 * tagset_tags_count // tags_count + 1),
223
224
  )
224
225
  self.assertEqual(
225
- cache.count_tags(),
226
+ cache.count_records(),
226
227
  tag_records_count * tagset_tags_count // tags_count - (60 * tagset_tags_count // tags_count + 1),
227
228
  )
228
229
  self.assertEqual(
229
- len(list(cache.iter_tags())),
230
+ len(list(cache.iter_records())),
230
231
  tag_records_count * tagset_tags_count // tags_count - (60 * tagset_tags_count // tags_count + 1),
231
232
  )
232
233
 
233
- target_cache.remove_tags()
234
+ target_cache.remove_records()
234
235
 
235
- self.assertEqual(target_cache.count_tags(), 0)
236
- self.assertEqual(len(list(target_cache.iter_tags())), 0)
237
- self.assertEqual(cache.count_tags(), 0)
238
- self.assertEqual(len(list(cache.iter_tags())), 0)
236
+ self.assertEqual(target_cache.count_records(), 0)
237
+ self.assertEqual(len(list(target_cache.iter_records())), 0)
238
+ self.assertEqual(cache.count_records(), 0)
239
+ self.assertEqual(len(list(cache.iter_records())), 0)
239
240
 
240
241
  def test_tag_cache__multithread(self):
241
242
  tag_names = [
@@ -296,7 +297,7 @@ class TagUtilsTest(unittest.TestCase):
296
297
  for i in range(tasks_count_per_thread):
297
298
  tag_name = tag_names[i % len(tag_names)]
298
299
  tag = old_tagset.get_bound(tag_name) or new_tagset.get_bound(tag_name) or tag_name
299
- target_cache.add_ranged_tag(
300
+ target_cache.add_ranged_record(
300
301
  dt_parse_iso("2020-01-01T00:00:00+00:00") + datetime.timedelta(seconds=i),
301
302
  dt_parse_iso("2020-01-02T00:00:00+00:00") + datetime.timedelta(seconds=i + 1),
302
303
  tag,
@@ -322,23 +323,27 @@ class TagUtilsTest(unittest.TestCase):
322
323
  thread.join()
323
324
 
324
325
  for target_cache in target_caches:
325
- self.assertEqual(target_cache.count_tags(), tasks_count_per_target_cache)
326
- self.assertEqual(len(list(target_cache.iter_tags())), tasks_count_per_target_cache)
327
- self.assertEqual(len(list(target_cache.iter_tags(tag_prefix="dummy:bar"))),
326
+ self.assertEqual(target_cache.count_records(), tasks_count_per_target_cache)
327
+ self.assertEqual(len(list(target_cache.iter_records())), tasks_count_per_target_cache)
328
+ self.assertEqual(len(list(target_cache.iter_records(tag_prefix="dummy:bar"))),
328
329
  tasks_count_per_target_cache // tags_count)
329
- self.assertEqual(len(list(target_cache.iter_tags(tagset_namespace="tagset"))),
330
+ self.assertEqual(len(list(target_cache.iter_records(tagset_namespace="tagset"))),
330
331
  tasks_count_per_target_cache * tagset_tags_count // tags_count)
331
- self.assertEqual(len(list(target_cache.iter_tags(tagset_namespace="tagset", tagset_version="1.0.0"))),
332
- tasks_count_per_target_cache * old_tagset_tags_count // tags_count)
333
-
334
- self.assertEqual(cache.count_tags(), total_tasks_count)
335
- self.assertEqual(len(list(cache.iter_tag_and_targets())), total_tasks_count)
336
- self.assertEqual(len(list(cache.iter_tag_and_targets(tag_prefix="dummy:bar"))),
332
+ self.assertEqual(
333
+ len(list(target_cache.iter_records(tagset_namespace="tagset", tagset_version="1.0.0"))),
334
+ tasks_count_per_target_cache * old_tagset_tags_count // tags_count,
335
+ )
336
+
337
+ self.assertEqual(cache.count_records(), total_tasks_count)
338
+ self.assertEqual(len(list(cache.iter_record_and_targets())), total_tasks_count)
339
+ self.assertEqual(len(list(cache.iter_record_and_targets(tag_prefix="dummy:bar"))),
337
340
  total_tasks_count // tags_count)
338
- self.assertEqual(len(list(cache.iter_tag_and_targets(tagset_namespace="tagset"))),
341
+ self.assertEqual(len(list(cache.iter_record_and_targets(tagset_namespace="tagset"))),
339
342
  total_tasks_count * tagset_tags_count // tags_count)
340
- self.assertEqual(len(list(cache.iter_tag_and_targets(tagset_namespace="tagset", tagset_version="1.0.0"))),
341
- total_tasks_count * old_tagset_tags_count // tags_count)
343
+ self.assertEqual(
344
+ len(list(cache.iter_record_and_targets(tagset_namespace="tagset", tagset_version="1.0.0"))),
345
+ total_tasks_count * old_tagset_tags_count // tags_count,
346
+ )
342
347
 
343
348
  def test_tag_cache__clone(self):
344
349
  tag_names = [