plexus-python-common 1.0.65__py3-none-any.whl → 1.0.66__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,9 @@ __all__ = [
21
21
  ]
22
22
 
23
23
 
24
- def json_datetime_decoder(v: Any) -> datetime.datetime:
24
+ def json_datetime_decoder(v: Any) -> datetime.datetime | None:
25
+ if v is None:
26
+ return None
25
27
  if isinstance(v, str):
26
28
  return json_datetime_decoder(dt_parse(v, extended_format(with_us=True, with_tz=True)))
27
29
  if isinstance(v, datetime.datetime):
@@ -29,7 +31,9 @@ def json_datetime_decoder(v: Any) -> datetime.datetime:
29
31
  raise ValueError("unexpected type of value for datetime decoder")
30
32
 
31
33
 
32
- def json_datetime_encoder(v: Any) -> str:
34
+ def json_datetime_encoder(v: Any) -> str | None:
35
+ if v is None:
36
+ return None
33
37
  if isinstance(v, str):
34
38
  return json_datetime_encoder(dt_parse(v, extended_format(with_us=True, with_tz=True)))
35
39
  if isinstance(v, datetime.datetime):
@@ -28,6 +28,10 @@ __all__ = [
28
28
  "ChangingModelMixinProtocol",
29
29
  "SnapshotModelMixinProtocol",
30
30
  "RevisionModelMixinProtocol",
31
+ "SQLiteDateTime",
32
+ "model_sqn_type",
33
+ "model_datetime_tz_type",
34
+ "model_revision_type",
31
35
  "SequenceModelMixin",
32
36
  "ChangingModelMixin",
33
37
  "SnapshotModelMixin",
@@ -391,12 +395,26 @@ class RevisionModelMixinProtocol(SequenceModelMixinProtocol):
391
395
  ...
392
396
 
393
397
 
394
- # At the present time, we cannot express intersection of Protocol and SQLModel directly.
395
- # Thus, we define union types here for the mixins.
396
- SequenceModelMixin = SequenceModelMixinProtocol | SQLModel
397
- ChangingModelMixin = ChangingModelMixinProtocol | SQLModel
398
- SnapshotModelMixin = SnapshotModelMixinProtocol | SQLModel
399
- RevisionModelMixin = RevisionModelMixinProtocol | SQLModel
398
+ class SQLiteDateTime(sa.TypeDecorator):
399
+ """
400
+ Custom SQLAlchemy type decorator for handling timezone-aware datetimes in SQLite.
401
+ """
402
+ impl = sa.DateTime
403
+ cache_ok = True
404
+
405
+ def process_bind_param(self, value: datetime.datetime | None, dialect) -> datetime.datetime | None:
406
+ if value is None:
407
+ return None
408
+ if value.tzinfo is None:
409
+ return value.replace(tzinfo=datetime.timezone.utc)
410
+ if value.tzinfo == datetime.timezone.utc:
411
+ return value
412
+ raise ValueError("Only UTC timezone-aware datetimes are supported")
413
+
414
+ def process_result_value(self, value: datetime.datetime | None, dialect) -> datetime.datetime | None:
415
+ if value is None:
416
+ return None
417
+ return value.replace(tzinfo=datetime.timezone.utc)
400
418
 
401
419
 
402
420
  def model_sqn_type(dialect: str | None = None) -> sa.types.TypeEngine[int]:
@@ -428,7 +446,7 @@ def model_datetime_tz_type(dialect: str | None = None) -> sa.types.TypeEngine[da
428
446
  if dialect == dialects.postgresql:
429
447
  return sa_pg.TIMESTAMP(timezone=True)
430
448
  if dialect == dialects.sqlite:
431
- return sa_sqlite.TIMESTAMP(timezone=True)
449
+ return SQLiteDateTime
432
450
  raise ValueError(f"unsupported database dialect '{dialect}'")
433
451
 
434
452
 
@@ -448,6 +466,14 @@ def model_revision_type(dialect: str | None = None) -> sa.types.TypeEngine[int]:
448
466
  raise ValueError(f"unsupported database dialect '{dialect}'")
449
467
 
450
468
 
469
+ # At the present time, we cannot express intersection of Protocol and SQLModel directly.
470
+ # Thus, we define union types here for the mixins.
471
+ SequenceModelMixin = SequenceModelMixinProtocol | SQLModel
472
+ ChangingModelMixin = ChangingModelMixinProtocol | SQLModel
473
+ SnapshotModelMixin = SnapshotModelMixinProtocol | SQLModel
474
+ RevisionModelMixin = RevisionModelMixinProtocol | SQLModel
475
+
476
+
451
477
  def make_sequence_model_mixin(dialect: str | None = None) -> type[SequenceModelMixin]:
452
478
  """
453
479
  Creates a mixin class for SQLModel models that adds a unique identifier field ``sqn``.
@@ -496,14 +522,14 @@ def make_changing_model_mixin(dialect: str | None = None) -> type[ChangingModelM
496
522
 
497
523
  @pdt.field_validator("created_at", mode="after")
498
524
  @classmethod
499
- def validate_created_at(cls, v: datetime.datetime) -> datetime.datetime:
525
+ def validate_created_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
500
526
  if v is not None:
501
527
  validate_dt_timezone(v)
502
528
  return v
503
529
 
504
530
  @pdt.field_validator("updated_at", mode="after")
505
531
  @classmethod
506
- def validate_updated_at(cls, v: datetime.datetime) -> datetime.datetime:
532
+ def validate_updated_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
507
533
  if v is not None:
508
534
  validate_dt_timezone(v)
509
535
  return v
@@ -571,14 +597,14 @@ def make_snapshot_model_mixin(dialect: str | None = None) -> type[SnapshotModelM
571
597
 
572
598
  @pdt.field_validator("created_at", mode="after")
573
599
  @classmethod
574
- def validate_created_at(cls, v: datetime.datetime) -> datetime.datetime:
600
+ def validate_created_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
575
601
  if v is not None:
576
602
  validate_dt_timezone(v)
577
603
  return v
578
604
 
579
605
  @pdt.field_validator("expired_at", mode="after")
580
606
  @classmethod
581
- def validate_expired_at(cls, v: datetime.datetime) -> datetime.datetime:
607
+ def validate_expired_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
582
608
  if v is not None:
583
609
  validate_dt_timezone(v)
584
610
  return v
@@ -684,28 +710,28 @@ def make_revision_model_mixin(dialect: str | None = None) -> type[RevisionModelM
684
710
 
685
711
  @pdt.field_validator("created_at", mode="after")
686
712
  @classmethod
687
- def validate_created_at(cls, v: datetime.datetime) -> datetime.datetime:
713
+ def validate_created_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
688
714
  if v is not None:
689
715
  validate_dt_timezone(v)
690
716
  return v
691
717
 
692
718
  @pdt.field_validator("updated_at", mode="after")
693
719
  @classmethod
694
- def validate_updated_at(cls, v: datetime.datetime) -> datetime.datetime:
720
+ def validate_updated_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
695
721
  if v is not None:
696
722
  validate_dt_timezone(v)
697
723
  return v
698
724
 
699
725
  @pdt.field_validator("expired_at", mode="after")
700
726
  @classmethod
701
- def validate_expired_at(cls, v: datetime.datetime) -> datetime.datetime:
727
+ def validate_expired_at(cls, v: datetime.datetime | None) -> datetime.datetime | None:
702
728
  if v is not None:
703
729
  validate_dt_timezone(v)
704
730
  return v
705
731
 
706
732
  @pdt.field_validator("revision", mode="after")
707
733
  @classmethod
708
- def validate_revision(cls, v: int) -> int:
734
+ def validate_revision(cls, v: int | None) -> int | None:
709
735
  if v is not None and not v > 0:
710
736
  raise ValueError("revision number must be positive integer")
711
737
  return v
@@ -941,11 +967,12 @@ def clone_sequence_model_instance[SequenceModelT: SequenceModelMixin](
941
967
  model: type[SequenceModelT],
942
968
  instance: SequenceModelMixin,
943
969
  *,
970
+ validate_only: bool = False,
944
971
  clear_meta_fields: bool = True,
945
- inplace: bool = False,
946
972
  ) -> SequenceModelT:
947
973
  result = model.model_validate(instance)
948
- result = instance if inplace else result
974
+ if validate_only:
975
+ return instance
949
976
  if clear_meta_fields:
950
977
  result.sqn = None
951
978
  return result
@@ -955,11 +982,12 @@ def clone_changing_model_instance[ChangingModelT: ChangingModelMixin](
955
982
  model: type[ChangingModelT],
956
983
  instance: ChangingModelMixin,
957
984
  *,
985
+ validate_only: bool = False,
958
986
  clear_meta_fields: bool = True,
959
- inplace: bool = False,
960
987
  ) -> ChangingModelT:
961
988
  result = model.model_validate(instance)
962
- result = instance if inplace else result
989
+ if validate_only:
990
+ return instance
963
991
  if clear_meta_fields:
964
992
  result.sqn = None
965
993
  result.created_at = None
@@ -971,11 +999,12 @@ def clone_snapshot_model_instance[SnapshotModelT: SnapshotModelMixin](
971
999
  model: type[SnapshotModelT],
972
1000
  instance: SnapshotModelMixin,
973
1001
  *,
1002
+ validate_only: bool = False,
974
1003
  clear_meta_fields: bool = True,
975
- inplace: bool = False,
976
1004
  ) -> SnapshotModelT:
977
1005
  result = model.model_validate(instance)
978
- result = instance if inplace else result
1006
+ if validate_only:
1007
+ return instance
979
1008
  if clear_meta_fields:
980
1009
  result.sqn = None
981
1010
  result.created_at = None
@@ -988,11 +1017,12 @@ def clone_revision_model_instance[RevisionModelT: RevisionModelMixin](
988
1017
  model: type[RevisionModelT],
989
1018
  instance: RevisionModelMixin,
990
1019
  *,
1020
+ validate_only: bool = False,
991
1021
  clear_meta_fields: bool = True,
992
- inplace: bool = False,
993
1022
  ) -> RevisionModelT:
994
1023
  result = model.model_validate(instance)
995
- result = instance if inplace else result
1024
+ if validate_only:
1025
+ return instance
996
1026
  if clear_meta_fields:
997
1027
  result.sqn = None
998
1028
  result.created_at = None
@@ -1081,7 +1111,7 @@ def db_update_sequence_model[SequenceModelT: SequenceModelMixin](
1081
1111
  raise sa_exc.NoResultFound(f"'{model_name_of(model)}' of specified sqn '{sqn}' not found")
1082
1112
 
1083
1113
  db_instance = model_copy_from(db_instance, clone_sequence_model_instance(model, instance), exclude_none=True)
1084
- db_instance = clone_sequence_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
1114
+ clone_sequence_model_instance(model, db_instance, validate_only=True)
1085
1115
  db.flush()
1086
1116
 
1087
1117
  return db_instance
@@ -1146,7 +1176,7 @@ def db_update_changing_model[ChangingModelT: ChangingModelMixin](
1146
1176
 
1147
1177
  db_instance = model_copy_from(db_instance, clone_changing_model_instance(model, instance), exclude_none=True)
1148
1178
  db_instance.updated_at = updated_at
1149
- db_instance = clone_changing_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
1179
+ clone_changing_model_instance(model, db_instance, validate_only=True)
1150
1180
  db.flush()
1151
1181
 
1152
1182
  return db_instance
@@ -1304,13 +1334,14 @@ def db_update_snapshot_model[SnapshotModelT: SnapshotModelMixin](
1304
1334
  raise sa_exc.NoResultFound(f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
1305
1335
 
1306
1336
  db_instance.expired_at = updated_at
1307
- db_instance = clone_snapshot_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
1337
+ clone_snapshot_model_instance(model, db_instance, validate_only=True)
1308
1338
  db.flush()
1309
1339
 
1310
1340
  db_new_instance = clone_snapshot_model_instance(model, instance)
1311
1341
  db_new_instance.record_sqn = record_sqn
1312
1342
  db_new_instance.created_at = updated_at
1313
1343
  db_new_instance.expired_at = None
1344
+ clone_snapshot_model_instance(model, db_new_instance, validate_only=True)
1314
1345
  db.add(db_new_instance)
1315
1346
  db.flush()
1316
1347
 
@@ -1333,7 +1364,7 @@ def db_expire_snapshot_model[SnapshotModelT: SnapshotModelMixin](
1333
1364
  raise sa_exc.NoResultFound(f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
1334
1365
 
1335
1366
  db_instance.expired_at = updated_at
1336
- db_instance = clone_snapshot_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
1367
+ clone_snapshot_model_instance(model, db_instance, validate_only=True)
1337
1368
  db.flush()
1338
1369
 
1339
1370
  return db_instance
@@ -1364,9 +1395,10 @@ def db_activate_snapshot_model[SnapshotModelT: SnapshotModelMixin](
1364
1395
  db_new_instance.record_sqn = record_sqn
1365
1396
  db_new_instance.created_at = db_instance.expired_at
1366
1397
  db_new_instance.expired_at = updated_at
1367
- db_new_instance = clone_snapshot_model_instance(model, db_new_instance, clear_meta_fields=False, inplace=True)
1398
+ clone_snapshot_model_instance(model, db_new_instance, validate_only=True)
1368
1399
  db_new_instance.created_at = updated_at
1369
1400
  db_new_instance.expired_at = None
1401
+ clone_snapshot_model_instance(model, db_new_instance, validate_only=True)
1370
1402
  db.add(db_new_instance)
1371
1403
  db.flush()
1372
1404
 
@@ -1529,7 +1561,7 @@ def db_update_revision_model[RevisionModelT: RevisionModelMixin](
1529
1561
  raise sa_exc.NoResultFound(f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
1530
1562
 
1531
1563
  db_instance.expired_at = updated_at
1532
- db_instance = clone_revision_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
1564
+ clone_revision_model_instance(model, db_instance, validate_only=True)
1533
1565
  db.flush()
1534
1566
 
1535
1567
  db_new_instance = clone_revision_model_instance(model, instance)
@@ -1538,6 +1570,7 @@ def db_update_revision_model[RevisionModelT: RevisionModelMixin](
1538
1570
  db_new_instance.updated_at = updated_at
1539
1571
  db_new_instance.expired_at = None
1540
1572
  db_new_instance.revision = db_instance.revision + 1
1573
+ clone_revision_model_instance(model, db_new_instance, validate_only=True)
1541
1574
  db.add(db_new_instance)
1542
1575
  db.flush()
1543
1576
 
@@ -1560,7 +1593,7 @@ def db_expire_revision_model[RevisionModelT: RevisionModelMixin](
1560
1593
  raise sa_exc.NoResultFound(f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
1561
1594
 
1562
1595
  db_instance.expired_at = updated_at
1563
- db_instance = clone_revision_model_instance(model, db_instance, clear_meta_fields=False, inplace=True)
1596
+ clone_revision_model_instance(model, db_instance, validate_only=True)
1564
1597
  db.flush()
1565
1598
 
1566
1599
  return db_instance
@@ -1593,9 +1626,10 @@ def db_activate_revision_model[RevisionModelT: RevisionModelMixin](
1593
1626
  db_new_instance.updated_at = db_instance.expired_at
1594
1627
  db_new_instance.expired_at = updated_at
1595
1628
  db_new_instance.revision = db_instance.revision + 1
1596
- db_new_instance = clone_revision_model_instance(model, db_new_instance, clear_meta_fields=False, inplace=True)
1629
+ clone_revision_model_instance(model, db_new_instance, validate_only=True)
1597
1630
  db_new_instance.updated_at = updated_at
1598
1631
  db_new_instance.expired_at = None
1632
+ clone_revision_model_instance(model, db_new_instance, validate_only=True)
1599
1633
  db.add(db_new_instance)
1600
1634
  db.flush()
1601
1635
 
@@ -33,6 +33,7 @@ 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
36
37
  from plexus.common.utils.ormutils import SequenceModelMixinProtocol
37
38
  from plexus.common.utils.ormutils import clone_sequence_model_instance, make_base_model, make_sequence_model_mixin
38
39
  from plexus.common.utils.sqlutils import escape_sql_like
@@ -396,11 +397,11 @@ class TagTarget(BaseModel):
396
397
  description="Vehicle name associated with the tag record",
397
398
  )
398
399
  begin_dt: datetime.datetime = Field(
399
- sa_column=sa.Column(sa_sqlite.TIMESTAMP, nullable=False),
400
+ sa_column=sa.Column(SQLiteDateTime, nullable=False),
400
401
  description="Begin datetime of the target range associated with the tag record",
401
402
  )
402
403
  end_dt: datetime.datetime = Field(
403
- sa_column=sa.Column(sa_sqlite.TIMESTAMP, nullable=False),
404
+ sa_column=sa.Column(SQLiteDateTime, nullable=False),
404
405
  description="End datetime of the target range associated with the tag record",
405
406
  )
406
407
 
@@ -431,13 +432,13 @@ class TagTarget(BaseModel):
431
432
  @pdt.field_validator("begin_dt", mode="after")
432
433
  @classmethod
433
434
  def validate_begin_dt(cls, v: datetime.datetime) -> datetime.datetime:
434
- validate_dt_timezone(v, allow_naive=True)
435
+ validate_dt_timezone(v)
435
436
  return v
436
437
 
437
438
  @pdt.field_validator("end_dt", mode="after")
438
439
  @classmethod
439
440
  def validate_end_dt(cls, v: datetime.datetime) -> datetime.datetime:
440
- validate_dt_timezone(v, allow_naive=True)
441
+ validate_dt_timezone(v)
441
442
  return v
442
443
 
443
444
  @pdt.model_validator(mode="after")
@@ -461,11 +462,11 @@ class TagRecord(BaseModel):
461
462
  description="Sequence number of the tag record's target",
462
463
  )
463
464
  begin_dt: datetime.datetime = Field(
464
- sa_column=sa.Column(sa_sqlite.TIMESTAMP, nullable=False),
465
+ sa_column=sa.Column(SQLiteDateTime, nullable=False),
465
466
  description="Begin datetime of the tag record",
466
467
  )
467
468
  end_dt: datetime.datetime = Field(
468
- sa_column=sa.Column(sa_sqlite.TIMESTAMP, nullable=False),
469
+ sa_column=sa.Column(SQLiteDateTime, nullable=False),
469
470
  description="End datetime of the tag record",
470
471
  )
471
472
  tagset_namespace: str | None = Field(
@@ -496,13 +497,13 @@ class TagRecord(BaseModel):
496
497
  @pdt.field_validator("begin_dt", mode="after")
497
498
  @classmethod
498
499
  def validate_begin_dt(cls, v: datetime.datetime) -> datetime.datetime:
499
- validate_dt_timezone(v, allow_naive=True)
500
+ validate_dt_timezone(v)
500
501
  return v
501
502
 
502
503
  @pdt.field_validator("end_dt", mode="after")
503
504
  @classmethod
504
505
  def validate_end_dt(cls, v: datetime.datetime) -> datetime.datetime:
505
- validate_dt_timezone(v, allow_naive=True)
506
+ validate_dt_timezone(v)
506
507
  return v
507
508
 
508
509
  @pdt.field_validator("tagset_namespace", mode="after")
@@ -571,7 +572,7 @@ if typing.TYPE_CHECKING:
571
572
 
572
573
  @singleton
573
574
  def tag_cache_file_path() -> pathlib.Path:
574
- return pathlib.Path.home() / ".local" / "plus" / "datahub" / "tag_cache" / f"{randomizer().random_alphanumeric(7)}.db"
575
+ return pathlib.Path.home() / ".local" / "plexus" / "tag_cache" / f"{randomizer().random_alphanumeric(7)}.db"
575
576
 
576
577
 
577
578
  class TagCache(object):
@@ -595,9 +596,13 @@ class TagCache(object):
595
596
 
596
597
  @contextlib.contextmanager
597
598
  def make_session(self) -> Generator[sa_orm.Session, None, None]:
598
- with self.thread_lock:
599
- with self.conn_maker.make_session() as session:
599
+ with self.thread_lock, self.conn_maker.make_session(expire_on_commit=False) as session:
600
+ try:
600
601
  yield session
602
+ session.commit()
603
+ except Exception:
604
+ session.rollback()
605
+ raise
601
606
 
602
607
  def get_target(self, target: int | str) -> TagTargetTable | None:
603
608
  with self.make_session() as session:
@@ -1019,7 +1024,6 @@ class TargetedTagCache(object):
1019
1024
  session.add(db_tag_record)
1020
1025
  session.commit()
1021
1026
 
1022
- session.refresh(db_tag_record)
1023
1027
  return chainable(self, db_tag_record)
1024
1028
 
1025
1029
  def add_tag(
@@ -1104,7 +1108,6 @@ class TargetedTagCache(object):
1104
1108
 
1105
1109
  session.commit()
1106
1110
 
1107
- session.refresh(db_tag_record)
1108
1111
  return chainable(self, db_tag_record)
1109
1112
 
1110
1113
  def remove_tags(
@@ -1158,7 +1161,7 @@ class TargetedTagCache(object):
1158
1161
 
1159
1162
 
1160
1163
  @memorized
1161
- def tag_cache(*, identifier: str | None = None, file_path: str | None = None) -> TagCache:
1164
+ def tag_cache(*, identifier: str | None = None, file_path: str | os.PathLike[str] | None = None) -> TagCache:
1162
1165
  """
1163
1166
  Get a ``TagCache`` instance associated with the given identifier. If the identifier is ``None``, return a
1164
1167
  ``TagCache`` instance associated with a default file path. Otherwise, validate the identifier as a snake case
@@ -116,6 +116,7 @@ def patched_postgresql_session_maker(
116
116
  base_model: type[SQLModel],
117
117
  app: FastAPI,
118
118
  session_maker: Callable[[], Generator[sa_orm.Session]],
119
+ **kwargs,
119
120
  ) -> Callable[[pytest.FixtureRequest], Generator[Callable[[], Iterator[sa_orm.Session]]]]:
120
121
  """
121
122
  Create a pytest fixture that provides a patched session maker for PostgreSQL tests.
@@ -141,7 +142,7 @@ def patched_postgresql_session_maker(
141
142
  )
142
143
 
143
144
  def make_session_maker() -> Iterator[sa_orm.Session]:
144
- with cm.make_session(autocommit=False, autoflush=False) as db:
145
+ with cm.make_session(**kwargs) as db:
145
146
  try:
146
147
  # language=postgresql
147
148
  db.execute(sa.sql.text("SET TIMEZONE TO 'UTC'"))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.65
3
+ Version: 1.0.66
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -15,15 +15,15 @@ plexus/common/utils/config.py,sha256=uCzSYR9W-vNNZiRJ3FdiExuUazlDXY7xJtJaY11T_bA
15
15
  plexus/common/utils/datautils.py,sha256=mgnr-dcHpw-Pk3qBud0lC3JX_pv-iKzI8llsPW9Q12g,9275
16
16
  plexus/common/utils/dockerutils.py,sha256=WPxQuabRWyyM8wpSSYhb_HZaOw5yZ2TbU2dEQ2xRIlQ,5787
17
17
  plexus/common/utils/gisutils.py,sha256=UR3uVoD1nAy0SWJ1AYWCUy94Lo8zNb4nv_JdpcANBDE,11462
18
- plexus/common/utils/jsonutils.py,sha256=hD3cFkBll0AIH3u5FniJSVAsyZpUosgARX4ayIJmY6s,3238
19
- plexus/common/utils/ormutils.py,sha256=IenV_DdgGPS7Xb3QuV0GuIYmYw0GbU4dAN2a2XDaoxs,59327
18
+ plexus/common/utils/jsonutils.py,sha256=-_uKlQMLMgmVO9pB99S45Y_Vufx5dFSq43DIwGz1a54,3328
19
+ plexus/common/utils/ormutils.py,sha256=_kkQLTScYeoAneEdbGvkcsXh1TDja__AUznVkOpRNn0,60393
20
20
  plexus/common/utils/pathutils.py,sha256=hGJqSLj08tuOeZ7WeC5d4BtjnPI732BuntVQBQsqOaI,9581
21
21
  plexus/common/utils/s3utils.py,sha256=zlO4kGs-c2gUeOfPfiKIE5liQZsbYxqAZYCwA8kL0Lo,36017
22
22
  plexus/common/utils/sqlutils.py,sha256=D6kTBjhO5YlNRt3uFlPt6z3uH61m9ajEzPYmsI6NoFc,231
23
23
  plexus/common/utils/strutils.py,sha256=O9Inv4ffUTf6Xjc5ftoZwbIua1NeG7itCT9S3zjZxBc,16436
24
- plexus/common/utils/tagutils.py,sha256=dpj_bnpWQVcMF5ISBJHVVrw4BzMwLtQysARr_zJrpDA,51048
25
- plexus/common/utils/testutils.py,sha256=GyrKOKfrl1Go8Q7tCZLybxYvVqyox1AtEFWzWoecNwg,6163
26
- plexus_python_common-1.0.65.dist-info/METADATA,sha256=0Wtmhl6ClvNMcQdzz9mASKiGKufWSnTXALnL8Vyrwc8,1481
27
- plexus_python_common-1.0.65.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
28
- plexus_python_common-1.0.65.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
29
- plexus_python_common-1.0.65.dist-info/RECORD,,
24
+ plexus/common/utils/tagutils.py,sha256=MzsuxBH62lAPuvQL1wx7kKvQHsA5NXYMEPRhSYwb4gA,51077
25
+ plexus/common/utils/testutils.py,sha256=N8ijLu7X-hlQlHzvv0TtSsQpIF4T1hbr-AjkILoV2Ac,6152
26
+ plexus_python_common-1.0.66.dist-info/METADATA,sha256=FrVKPSwnSIbWkpwtHO-PABwKcvEK6Sey8UtP8sRKleg,1481
27
+ plexus_python_common-1.0.66.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
28
+ plexus_python_common-1.0.66.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
29
+ plexus_python_common-1.0.66.dist-info/RECORD,,