plexus-python-pulse-common 1.1.86__py3-none-any.whl → 1.1.88__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plexus/pulse/common/utils/ormutils.py +120 -18
- plexus/pulse/common/utils/strutils.py +16 -3
- {plexus_python_pulse_common-1.1.86.dist-info → plexus_python_pulse_common-1.1.88.dist-info}/METADATA +1 -1
- {plexus_python_pulse_common-1.1.86.dist-info → plexus_python_pulse_common-1.1.88.dist-info}/RECORD +6 -6
- {plexus_python_pulse_common-1.1.86.dist-info → plexus_python_pulse_common-1.1.88.dist-info}/WHEEL +1 -1
- {plexus_python_pulse_common-1.1.86.dist-info → plexus_python_pulse_common-1.1.88.dist-info}/top_level.txt +0 -0
|
@@ -551,7 +551,10 @@ def model_revision_type(dialect: str | None = None) -> sa.types.TypeEngine[int]:
|
|
|
551
551
|
raise ValueError(f"unsupported database dialect '{dialect}'")
|
|
552
552
|
|
|
553
553
|
|
|
554
|
-
def index_condition_kwarg(
|
|
554
|
+
def index_condition_kwarg(
|
|
555
|
+
dialect: str | None = None,
|
|
556
|
+
clause: sa.ClauseElement | None = None
|
|
557
|
+
) -> dict[str, sa.ClauseElement | None]:
|
|
555
558
|
if dialect is None:
|
|
556
559
|
dialect = dialects.postgresql
|
|
557
560
|
if dialect == dialects.postgresql:
|
|
@@ -728,7 +731,7 @@ def make_snapshot_model_mixin(dialect: str | None = None) -> type[SnapshotModelM
|
|
|
728
731
|
index_name,
|
|
729
732
|
"record_sqn",
|
|
730
733
|
unique=True,
|
|
731
|
-
**index_condition_kwarg(dialect, sa.
|
|
734
|
+
**index_condition_kwarg(dialect, sa.column("expired_at").is_(None)),
|
|
732
735
|
)
|
|
733
736
|
|
|
734
737
|
@classmethod
|
|
@@ -736,7 +739,7 @@ def make_snapshot_model_mixin(dialect: str | None = None) -> type[SnapshotModelM
|
|
|
736
739
|
return sa.Index(
|
|
737
740
|
index_name,
|
|
738
741
|
*fields,
|
|
739
|
-
**index_condition_kwarg(dialect, sa.
|
|
742
|
+
**index_condition_kwarg(dialect, sa.column("expired_at").is_(None)),
|
|
740
743
|
)
|
|
741
744
|
|
|
742
745
|
@classmethod
|
|
@@ -745,7 +748,7 @@ def make_snapshot_model_mixin(dialect: str | None = None) -> type[SnapshotModelM
|
|
|
745
748
|
index_name,
|
|
746
749
|
*fields,
|
|
747
750
|
unique=True,
|
|
748
|
-
**index_condition_kwarg(dialect, sa.
|
|
751
|
+
**index_condition_kwarg(dialect, sa.column("expired_at").is_(None)),
|
|
749
752
|
)
|
|
750
753
|
|
|
751
754
|
return ModelMixin
|
|
@@ -865,7 +868,7 @@ def make_revision_model_mixin(dialect: str | None = None) -> type[RevisionModelM
|
|
|
865
868
|
index_name,
|
|
866
869
|
"record_sqn",
|
|
867
870
|
unique=True,
|
|
868
|
-
**index_condition_kwarg(dialect, sa.
|
|
871
|
+
**index_condition_kwarg(dialect, sa.column("expired_at").is_(None)),
|
|
869
872
|
)
|
|
870
873
|
|
|
871
874
|
@classmethod
|
|
@@ -873,7 +876,7 @@ def make_revision_model_mixin(dialect: str | None = None) -> type[RevisionModelM
|
|
|
873
876
|
return sa.Index(
|
|
874
877
|
index_name,
|
|
875
878
|
*fields,
|
|
876
|
-
**index_condition_kwarg(dialect, sa.
|
|
879
|
+
**index_condition_kwarg(dialect, sa.column("expired_at").is_(None)),
|
|
877
880
|
)
|
|
878
881
|
|
|
879
882
|
@classmethod
|
|
@@ -882,7 +885,7 @@ def make_revision_model_mixin(dialect: str | None = None) -> type[RevisionModelM
|
|
|
882
885
|
index_name,
|
|
883
886
|
*fields,
|
|
884
887
|
unique=True,
|
|
885
|
-
**index_condition_kwarg(dialect, sa.
|
|
888
|
+
**index_condition_kwarg(dialect, sa.column("expired_at").is_(None)),
|
|
886
889
|
)
|
|
887
890
|
|
|
888
891
|
return ModelMixin
|
|
@@ -948,6 +951,14 @@ def make_snapshot_model_trigger[SnapshotModelT: SnapshotModelMixin](engine: sa.E
|
|
|
948
951
|
NEW."expired_at" := NULL;
|
|
949
952
|
NEW."record_sqn" := nextval('{record_sqn_seq_name}');
|
|
950
953
|
ELSE
|
|
954
|
+
-- Serialize transitions even when there is no active version.
|
|
955
|
+
PERFORM "sqn" FROM "{table_name}"
|
|
956
|
+
WHERE "record_sqn" = NEW."record_sqn"
|
|
957
|
+
ORDER BY "sqn" LIMIT 1 FOR UPDATE;
|
|
958
|
+
IF NOT FOUND THEN
|
|
959
|
+
RAISE EXCEPTION 'record_sqn % does not exist', NEW."record_sqn";
|
|
960
|
+
END IF;
|
|
961
|
+
|
|
951
962
|
IF NEW."created_at" IS NULL THEN
|
|
952
963
|
NEW."created_at" := CURRENT_TIMESTAMP;
|
|
953
964
|
END IF;
|
|
@@ -1019,6 +1030,14 @@ def make_revision_model_trigger[RevisionModelT: RevisionModelMixin](engine: sa.E
|
|
|
1019
1030
|
NEW."record_sqn" := nextval('{record_sqn_seq_name}');
|
|
1020
1031
|
NEW."revision" := 1;
|
|
1021
1032
|
ELSE
|
|
1033
|
+
-- Serialize transitions even when there is no active version.
|
|
1034
|
+
PERFORM "sqn" FROM "{table_name}"
|
|
1035
|
+
WHERE "record_sqn" = NEW."record_sqn"
|
|
1036
|
+
ORDER BY "sqn" LIMIT 1 FOR UPDATE;
|
|
1037
|
+
IF NOT FOUND THEN
|
|
1038
|
+
RAISE EXCEPTION 'record_sqn % does not exist', NEW."record_sqn";
|
|
1039
|
+
END IF;
|
|
1040
|
+
|
|
1022
1041
|
SELECT MAX("created_at") INTO NEW."created_at"
|
|
1023
1042
|
FROM "{table_name}"
|
|
1024
1043
|
WHERE "record_sqn" = NEW."record_sqn";
|
|
@@ -1070,6 +1089,7 @@ def clone_sequence_model_instance[SequenceModelT: SequenceModelMixin](
|
|
|
1070
1089
|
return instance
|
|
1071
1090
|
if clear_meta_fields:
|
|
1072
1091
|
result.sqn = None
|
|
1092
|
+
result.model_fields_set.discard("sqn")
|
|
1073
1093
|
return result
|
|
1074
1094
|
|
|
1075
1095
|
|
|
@@ -1087,6 +1107,9 @@ def clone_changing_model_instance[ChangingModelT: ChangingModelMixin](
|
|
|
1087
1107
|
result.sqn = None
|
|
1088
1108
|
result.created_at = None
|
|
1089
1109
|
result.updated_at = None
|
|
1110
|
+
result.model_fields_set.discard("sqn")
|
|
1111
|
+
result.model_fields_set.discard("created_at")
|
|
1112
|
+
result.model_fields_set.discard("updated_at")
|
|
1090
1113
|
return result
|
|
1091
1114
|
|
|
1092
1115
|
|
|
@@ -1105,6 +1128,10 @@ def clone_snapshot_model_instance[SnapshotModelT: SnapshotModelMixin](
|
|
|
1105
1128
|
result.created_at = None
|
|
1106
1129
|
result.expired_at = None
|
|
1107
1130
|
result.record_sqn = None
|
|
1131
|
+
result.model_fields_set.discard("sqn")
|
|
1132
|
+
result.model_fields_set.discard("created_at")
|
|
1133
|
+
result.model_fields_set.discard("expired_at")
|
|
1134
|
+
result.model_fields_set.discard("record_sqn")
|
|
1108
1135
|
return result
|
|
1109
1136
|
|
|
1110
1137
|
|
|
@@ -1125,6 +1152,12 @@ def clone_revision_model_instance[RevisionModelT: RevisionModelMixin](
|
|
|
1125
1152
|
result.expired_at = None
|
|
1126
1153
|
result.record_sqn = None
|
|
1127
1154
|
result.revision = None
|
|
1155
|
+
result.model_fields_set.discard("sqn")
|
|
1156
|
+
result.model_fields_set.discard("created_at")
|
|
1157
|
+
result.model_fields_set.discard("updated_at")
|
|
1158
|
+
result.model_fields_set.discard("expired_at")
|
|
1159
|
+
result.model_fields_set.discard("record_sqn")
|
|
1160
|
+
result.model_fields_set.discard("revision")
|
|
1128
1161
|
return result
|
|
1129
1162
|
|
|
1130
1163
|
|
|
@@ -1144,6 +1177,41 @@ def db_make_order_by_clause[SequenceModelT: SequenceModelMixin](
|
|
|
1144
1177
|
return order_criteria
|
|
1145
1178
|
|
|
1146
1179
|
|
|
1180
|
+
def db_lock_model_record[ModelT: RevisionModelMixin | SnapshotModelMixin](
|
|
1181
|
+
db: sa_orm.Session,
|
|
1182
|
+
model: type[ModelT],
|
|
1183
|
+
record_sqn: int,
|
|
1184
|
+
) -> None:
|
|
1185
|
+
"""
|
|
1186
|
+
Lock the first history row, including when the record is inactive.
|
|
1187
|
+
|
|
1188
|
+
Select by ``record_sqn`` rather than assuming it equals the first ``sqn``: trigger-managed
|
|
1189
|
+
records use a separate sequence. Every lifecycle writer must acquire this same lock before
|
|
1190
|
+
reading current state.
|
|
1191
|
+
Locks last until caller commit or rollback. Do not delete the first history row.
|
|
1192
|
+
"""
|
|
1193
|
+
db_anchor = (
|
|
1194
|
+
db
|
|
1195
|
+
.query(model)
|
|
1196
|
+
.where(model.record_sqn == record_sqn)
|
|
1197
|
+
.order_by(model.sqn)
|
|
1198
|
+
.limit(1)
|
|
1199
|
+
.with_for_update()
|
|
1200
|
+
.populate_existing()
|
|
1201
|
+
.one_or_none()
|
|
1202
|
+
)
|
|
1203
|
+
if db_anchor is None:
|
|
1204
|
+
raise sa_exc.NoResultFound(f"'{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
# Mutating helpers flush but never begin, commit, or roll back transactions. Callers must roll
|
|
1208
|
+
# back on failure. Row locks serialize cooperating writers on databases supporting SELECT FOR UPDATE;
|
|
1209
|
+
# SQLite callers must serialize writes themselves (e.g. begin an IMMEDIATE transaction before
|
|
1210
|
+
# reading).
|
|
1211
|
+
# At stronger isolation levels, callers must also handle serialization failures by retrying
|
|
1212
|
+
# transactions. History rows used as lock anchors must not be deleted while a record exists.
|
|
1213
|
+
|
|
1214
|
+
|
|
1147
1215
|
def db_create_sequence_model[SequenceModelT: SequenceModelMixin](
|
|
1148
1216
|
db: sa_orm.Session,
|
|
1149
1217
|
model: type[SequenceModelT],
|
|
@@ -1201,11 +1269,11 @@ def db_update_sequence_model[SequenceModelT: SequenceModelMixin](
|
|
|
1201
1269
|
instance: SequenceModelMixin,
|
|
1202
1270
|
sqn: int,
|
|
1203
1271
|
) -> SequenceModelT:
|
|
1204
|
-
db_instance = db.query(model).where(model.sqn == sqn).one_or_none()
|
|
1272
|
+
db_instance = db.query(model).where(model.sqn == sqn).with_for_update().populate_existing().one_or_none()
|
|
1205
1273
|
if db_instance is None:
|
|
1206
1274
|
raise sa_exc.NoResultFound(f"'{model_name_of(model)}' of specified sqn '{sqn}' not found")
|
|
1207
1275
|
|
|
1208
|
-
db_instance = model_copy_from(db_instance, clone_sequence_model_instance(model, instance),
|
|
1276
|
+
db_instance = model_copy_from(db_instance, clone_sequence_model_instance(model, instance), exclude_unset=True)
|
|
1209
1277
|
clone_sequence_model_instance(model, db_instance, validate_only=True)
|
|
1210
1278
|
db.flush()
|
|
1211
1279
|
|
|
@@ -1217,7 +1285,7 @@ def db_delete_sequence_model[SequenceModelT: SequenceModelMixin](
|
|
|
1217
1285
|
model: type[SequenceModelT],
|
|
1218
1286
|
sqn: int,
|
|
1219
1287
|
) -> SequenceModelT:
|
|
1220
|
-
db_instance = db.query(model).where(model.sqn == sqn).one_or_none()
|
|
1288
|
+
db_instance = db.query(model).where(model.sqn == sqn).with_for_update().populate_existing().one_or_none()
|
|
1221
1289
|
if db_instance is None:
|
|
1222
1290
|
raise sa_exc.NoResultFound(f"'{model_name_of(model)}' of specified sqn '{sqn}' not found")
|
|
1223
1291
|
|
|
@@ -1265,11 +1333,11 @@ def db_update_changing_model[ChangingModelT: ChangingModelMixin](
|
|
|
1265
1333
|
sqn: int,
|
|
1266
1334
|
updated_at: datetime.datetime,
|
|
1267
1335
|
) -> ChangingModelT:
|
|
1268
|
-
db_instance = db.query(model).where(model.sqn == sqn).one_or_none()
|
|
1336
|
+
db_instance = db.query(model).where(model.sqn == sqn).with_for_update().populate_existing().one_or_none()
|
|
1269
1337
|
if db_instance is None:
|
|
1270
1338
|
raise sa_exc.NoResultFound(f"'{model_name_of(model)}' of specified sqn '{sqn}' not found")
|
|
1271
1339
|
|
|
1272
|
-
db_instance = model_copy_from(db_instance, clone_changing_model_instance(model, instance),
|
|
1340
|
+
db_instance = model_copy_from(db_instance, clone_changing_model_instance(model, instance), exclude_unset=True)
|
|
1273
1341
|
db_instance.updated_at = updated_at
|
|
1274
1342
|
clone_changing_model_instance(model, db_instance, validate_only=True)
|
|
1275
1343
|
db.flush()
|
|
@@ -1425,7 +1493,14 @@ def db_update_snapshot_model[SnapshotModelT: SnapshotModelMixin](
|
|
|
1425
1493
|
record_sqn: int,
|
|
1426
1494
|
updated_at: datetime.datetime,
|
|
1427
1495
|
) -> SnapshotModelT:
|
|
1428
|
-
|
|
1496
|
+
db_lock_model_record(db, model, record_sqn)
|
|
1497
|
+
db_instance = (
|
|
1498
|
+
db
|
|
1499
|
+
.query(model)
|
|
1500
|
+
.where(model.record_sqn == record_sqn, model.expired_at.is_(None))
|
|
1501
|
+
.populate_existing()
|
|
1502
|
+
.one_or_none()
|
|
1503
|
+
)
|
|
1429
1504
|
if db_instance is None:
|
|
1430
1505
|
raise sa_exc.NoResultFound(f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
|
|
1431
1506
|
|
|
@@ -1450,10 +1525,12 @@ def db_expire_snapshot_model[SnapshotModelT: SnapshotModelMixin](
|
|
|
1450
1525
|
record_sqn: int,
|
|
1451
1526
|
updated_at: datetime.datetime,
|
|
1452
1527
|
) -> SnapshotModelT:
|
|
1528
|
+
db_lock_model_record(db, model, record_sqn)
|
|
1453
1529
|
db_instance = (
|
|
1454
1530
|
db
|
|
1455
1531
|
.query(model)
|
|
1456
1532
|
.where(model.record_sqn == record_sqn, model.expired_at.is_(None))
|
|
1533
|
+
.populate_existing()
|
|
1457
1534
|
.one_or_none()
|
|
1458
1535
|
)
|
|
1459
1536
|
if db_instance is None:
|
|
@@ -1472,16 +1549,24 @@ def db_activate_snapshot_model[SnapshotModelT: SnapshotModelMixin](
|
|
|
1472
1549
|
record_sqn: int,
|
|
1473
1550
|
updated_at: datetime.datetime,
|
|
1474
1551
|
) -> SnapshotModelT:
|
|
1475
|
-
|
|
1552
|
+
db_lock_model_record(db, model, record_sqn)
|
|
1553
|
+
db_instance = (
|
|
1554
|
+
db
|
|
1555
|
+
.query(model)
|
|
1556
|
+
.where(model.record_sqn == record_sqn, model.expired_at.is_(None))
|
|
1557
|
+
.populate_existing()
|
|
1558
|
+
.one_or_none()
|
|
1559
|
+
)
|
|
1476
1560
|
if db_instance is not None:
|
|
1477
1561
|
raise sa_exc.MultipleResultsFound(
|
|
1478
|
-
f"
|
|
1562
|
+
f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' already exists")
|
|
1479
1563
|
|
|
1480
1564
|
db_instance = (
|
|
1481
1565
|
db
|
|
1482
1566
|
.query(model)
|
|
1483
1567
|
.where(model.record_sqn == record_sqn, model.expired_at.is_not(None))
|
|
1484
|
-
.order_by(model.created_at.desc())
|
|
1568
|
+
.order_by(model.created_at.desc(), model.sqn.desc())
|
|
1569
|
+
.populate_existing()
|
|
1485
1570
|
.first()
|
|
1486
1571
|
)
|
|
1487
1572
|
if db_instance is None:
|
|
@@ -1653,7 +1738,14 @@ def db_update_revision_model[RevisionModelT: RevisionModelMixin](
|
|
|
1653
1738
|
record_sqn: int,
|
|
1654
1739
|
updated_at: datetime.datetime,
|
|
1655
1740
|
) -> RevisionModelT:
|
|
1656
|
-
|
|
1741
|
+
db_lock_model_record(db, model, record_sqn)
|
|
1742
|
+
db_instance = (
|
|
1743
|
+
db
|
|
1744
|
+
.query(model)
|
|
1745
|
+
.where(model.record_sqn == record_sqn, model.expired_at.is_(None))
|
|
1746
|
+
.populate_existing()
|
|
1747
|
+
.one_or_none()
|
|
1748
|
+
)
|
|
1657
1749
|
if db_instance is None:
|
|
1658
1750
|
raise sa_exc.NoResultFound(f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' not found")
|
|
1659
1751
|
|
|
@@ -1680,10 +1772,12 @@ def db_expire_revision_model[RevisionModelT: RevisionModelMixin](
|
|
|
1680
1772
|
record_sqn: int,
|
|
1681
1773
|
updated_at: datetime.datetime,
|
|
1682
1774
|
) -> RevisionModelT:
|
|
1775
|
+
db_lock_model_record(db, model, record_sqn)
|
|
1683
1776
|
db_instance = (
|
|
1684
1777
|
db
|
|
1685
1778
|
.query(model)
|
|
1686
1779
|
.where(model.record_sqn == record_sqn, model.expired_at.is_(None))
|
|
1780
|
+
.populate_existing()
|
|
1687
1781
|
.one_or_none()
|
|
1688
1782
|
)
|
|
1689
1783
|
if db_instance is None:
|
|
@@ -1702,7 +1796,14 @@ def db_activate_revision_model[RevisionModelT: RevisionModelMixin](
|
|
|
1702
1796
|
record_sqn: int,
|
|
1703
1797
|
updated_at: datetime.datetime,
|
|
1704
1798
|
) -> RevisionModelT:
|
|
1705
|
-
|
|
1799
|
+
db_lock_model_record(db, model, record_sqn)
|
|
1800
|
+
db_instance = (
|
|
1801
|
+
db
|
|
1802
|
+
.query(model)
|
|
1803
|
+
.where(model.record_sqn == record_sqn, model.expired_at.is_(None))
|
|
1804
|
+
.populate_existing()
|
|
1805
|
+
.one_or_none()
|
|
1806
|
+
)
|
|
1706
1807
|
if db_instance is not None:
|
|
1707
1808
|
raise sa_exc.MultipleResultsFound(
|
|
1708
1809
|
f"active '{model_name_of(model)}' of specified record_sqn '{record_sqn}' already exists")
|
|
@@ -1712,6 +1813,7 @@ def db_activate_revision_model[RevisionModelT: RevisionModelMixin](
|
|
|
1712
1813
|
.query(model)
|
|
1713
1814
|
.where(model.record_sqn == record_sqn, model.expired_at.is_not(None))
|
|
1714
1815
|
.order_by(model.revision.desc())
|
|
1816
|
+
.populate_existing()
|
|
1715
1817
|
.first()
|
|
1716
1818
|
)
|
|
1717
1819
|
if db_instance is None:
|
|
@@ -28,6 +28,8 @@ __all__ = [
|
|
|
28
28
|
"strict_fragmented_abspath_parser",
|
|
29
29
|
"email_address_pattern",
|
|
30
30
|
"email_address_parser",
|
|
31
|
+
"numeric_version_pattern",
|
|
32
|
+
"numeric_version_parser",
|
|
31
33
|
"semver_pattern",
|
|
32
34
|
"semver_parser",
|
|
33
35
|
"colon_tag_pattern",
|
|
@@ -91,6 +93,7 @@ sharp_token: pp.ParserElement = pp.Char("#")
|
|
|
91
93
|
lower_regexp: re.Pattern[str] = re.compile(r"[a-z]")
|
|
92
94
|
upper_regexp: re.Pattern[str] = re.compile(r"[A-Z]")
|
|
93
95
|
digit_regexp: re.Pattern[str] = re.compile(r"[0-9]")
|
|
96
|
+
alpha_regexp: re.Pattern[str] = re.compile(r"[a-zA-Z]")
|
|
94
97
|
lower_digit_regexp: re.Pattern[str] = re.compile(r"[a-z0-9]")
|
|
95
98
|
upper_digit_regexp: re.Pattern[str] = re.compile(r"[A-Z0-9]")
|
|
96
99
|
alpha_digit_regexp: re.Pattern[str] = re.compile(r"[a-zA-Z0-9]")
|
|
@@ -99,6 +102,7 @@ hex_digit_regexp: re.Pattern[str] = re.compile(r"[a-f0-9]")
|
|
|
99
102
|
lowers_regexp: re.Pattern[str] = re.compile(rf"{lower_regexp.pattern}+")
|
|
100
103
|
uppers_regexp: re.Pattern[str] = re.compile(rf"{upper_regexp.pattern}+")
|
|
101
104
|
digits_regexp: re.Pattern[str] = re.compile(rf"{digit_regexp.pattern}+")
|
|
105
|
+
alphas_regexp: re.Pattern[str] = re.compile(rf"{alpha_regexp.pattern}+")
|
|
102
106
|
lower_digits_regexp: re.Pattern[str] = re.compile(rf"{lower_digit_regexp.pattern}+")
|
|
103
107
|
upper_digits_regexp: re.Pattern[str] = re.compile(rf"{upper_digit_regexp.pattern}+")
|
|
104
108
|
alpha_digits_regexp: re.Pattern[str] = re.compile(rf"{alpha_digit_regexp.pattern}+")
|
|
@@ -110,6 +114,7 @@ strict_chars_regexp: re.Pattern[str] = re.compile(rf"({alpha_digit_regexp.patter
|
|
|
110
114
|
lower_element: pp.ParserElement = pp.Regex(lower_regexp.pattern)
|
|
111
115
|
upper_element: pp.ParserElement = pp.Regex(upper_regexp.pattern)
|
|
112
116
|
digit_element: pp.ParserElement = pp.Regex(digit_regexp.pattern)
|
|
117
|
+
alpha_element: pp.ParserElement = pp.Regex(alpha_regexp.pattern)
|
|
113
118
|
lower_digit_element: pp.ParserElement = pp.Regex(lower_digit_regexp.pattern)
|
|
114
119
|
upper_digit_element: pp.ParserElement = pp.Regex(upper_digit_regexp.pattern)
|
|
115
120
|
alpha_digit_element: pp.ParserElement = pp.Regex(alpha_digit_regexp.pattern)
|
|
@@ -118,6 +123,7 @@ hex_digit_element: pp.ParserElement = pp.Regex(hex_digit_regexp.pattern)
|
|
|
118
123
|
lowers_element: pp.ParserElement = pp.Regex(lowers_regexp.pattern)
|
|
119
124
|
uppers_element: pp.ParserElement = pp.Regex(uppers_regexp.pattern)
|
|
120
125
|
digits_element: pp.ParserElement = pp.Regex(digits_regexp.pattern)
|
|
126
|
+
alphas_element: pp.ParserElement = pp.Regex(alphas_regexp.pattern)
|
|
121
127
|
lower_digits_element: pp.ParserElement = pp.Regex(lower_digits_regexp.pattern)
|
|
122
128
|
upper_digits_element: pp.ParserElement = pp.Regex(upper_digits_regexp.pattern)
|
|
123
129
|
alpha_digits_element: pp.ParserElement = pp.Regex(alpha_digits_regexp.pattern)
|
|
@@ -184,10 +190,14 @@ email_address_regexp: re.Pattern[str] = re.compile(
|
|
|
184
190
|
rf"(({lower_digit_regexp.pattern}|[_-])+)(?:\.({lower_digit_regexp.pattern}|[_-])+)*@(?:{kebab_case_regexp.pattern}\.)+({lower_digit_regexp.pattern}{{2,63}})")
|
|
185
191
|
email_address_element: pp.ParserElement = pp.Regex(email_address_regexp.pattern)
|
|
186
192
|
|
|
193
|
+
numeric_version_regexp: re.Pattern[str] = re.compile(
|
|
194
|
+
rf"(?:{number_regexp.pattern})(?:\.(?:{number_regexp.pattern}))*")
|
|
195
|
+
numeric_version_element: pp.ParserElement = pp.Regex(numeric_version_regexp.pattern)
|
|
196
|
+
|
|
187
197
|
semver_regexp: re.Pattern[str] = re.compile(
|
|
188
|
-
rf"({number_regexp.pattern})\.({number_regexp.pattern})\.({number_regexp.pattern})"
|
|
189
|
-
rf"(?:-{
|
|
190
|
-
rf"(?:\+{
|
|
198
|
+
rf"(?:{number_regexp.pattern})\.(?:{number_regexp.pattern})\.(?:{number_regexp.pattern})"
|
|
199
|
+
rf"(?:-(?:(?:{alpha_digit_regexp.pattern}|-)*(?:{alpha_regexp.pattern}|-)(?:{alpha_digit_regexp.pattern}|-)*|{number_regexp.pattern})(?:\.(?:(?:{alpha_digit_regexp.pattern}|-)*(?:{alpha_regexp.pattern}|-)(?:{alpha_digit_regexp.pattern}|-)*|{number_regexp.pattern}))*)?"
|
|
200
|
+
rf"(?:\+(?:{alpha_digit_regexp.pattern}|-)+(?:\.(?:{alpha_digit_regexp.pattern}|-)+)*)?")
|
|
191
201
|
semver_element: pp.ParserElement = pp.Regex(semver_regexp.pattern)
|
|
192
202
|
|
|
193
203
|
colon_tag_regexp: re.Pattern[str] = re.compile(rf"{snake_case_regexp.pattern}(?::{snake_case_regexp.pattern})*")
|
|
@@ -229,6 +239,9 @@ strict_fragmented_abspath_parser = make_string_parser(strict_fragmented_abspath_
|
|
|
229
239
|
email_address_pattern = make_string_pattern(email_address_regexp)
|
|
230
240
|
email_address_parser = make_string_parser(email_address_element)
|
|
231
241
|
|
|
242
|
+
numeric_version_pattern = make_string_pattern(numeric_version_regexp)
|
|
243
|
+
numeric_version_parser = make_string_parser(numeric_version_element)
|
|
244
|
+
|
|
232
245
|
semver_pattern = make_string_pattern(semver_regexp)
|
|
233
246
|
semver_parser = make_string_parser(semver_element)
|
|
234
247
|
|
{plexus_python_pulse_common-1.1.86.dist-info → plexus_python_pulse_common-1.1.88.dist-info}/RECORD
RENAMED
|
@@ -17,14 +17,14 @@ plexus/pulse/common/utils/dockerutils.py,sha256=7DF9yFGz8uHwpYk115xBWqOjs5etz0UE
|
|
|
17
17
|
plexus/pulse/common/utils/flowutils.py,sha256=MGu15KL9mw-LyGGLceVmY-c2FGer5PURnMZWFSJN--I,10182
|
|
18
18
|
plexus/pulse/common/utils/gisutils.py,sha256=Cx0ZB9-NA_7VuzbuzRArTJ5UfltG-KmvVC8YxPIgPE4,11600
|
|
19
19
|
plexus/pulse/common/utils/jsonutils.py,sha256=zSbW94klN2jrkMdBECu4RNJX6zzNaSxauQ3FuihuB6E,3342
|
|
20
|
-
plexus/pulse/common/utils/ormutils.py,sha256=
|
|
20
|
+
plexus/pulse/common/utils/ormutils.py,sha256=taaZzpIH0C-ShEmOWf-vt22bgcFFqH1FnJlNcH9tEFM,68222
|
|
21
21
|
plexus/pulse/common/utils/pathutils.py,sha256=eYfNK48wf1xZGK2IbpKxYOn4skZhWWA8OTyUwDXGMIc,9222
|
|
22
22
|
plexus/pulse/common/utils/s3utils.py,sha256=5q5wSOavt-n5EdrflQX7WPGgDeWQkGO9lG5Nq3QXjCk,35904
|
|
23
23
|
plexus/pulse/common/utils/sqlutils.py,sha256=D6kTBjhO5YlNRt3uFlPt6z3uH61m9ajEzPYmsI6NoFc,231
|
|
24
|
-
plexus/pulse/common/utils/strutils.py,sha256=
|
|
24
|
+
plexus/pulse/common/utils/strutils.py,sha256=8kl88-u89FApoaq-D2ulk5DNl9kenP6vqksvVJ0iH4s,17325
|
|
25
25
|
plexus/pulse/common/utils/tagutils.py,sha256=V8eUxtWSijX6nHSCuRX2dTJz3why3TCOzPVK5YXt28s,66944
|
|
26
26
|
plexus/pulse/common/utils/testutils.py,sha256=hKPRvoN4mg8QGQJH121l59a0uo8jN5rntGQAVYplGBw,6166
|
|
27
|
-
plexus_python_pulse_common-1.1.
|
|
28
|
-
plexus_python_pulse_common-1.1.
|
|
29
|
-
plexus_python_pulse_common-1.1.
|
|
30
|
-
plexus_python_pulse_common-1.1.
|
|
27
|
+
plexus_python_pulse_common-1.1.88.dist-info/METADATA,sha256=bNGz7B5jj23hayiYBRvtfRpfnsD21flHjbg-sgdiKMY,1555
|
|
28
|
+
plexus_python_pulse_common-1.1.88.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
29
|
+
plexus_python_pulse_common-1.1.88.dist-info/top_level.txt,sha256=ug_g7CVwaMQuas5UzAXbHUrQvKGCn8ezc6ZNvvRlJOE,7
|
|
30
|
+
plexus_python_pulse_common-1.1.88.dist-info/RECORD,,
|
|
File without changes
|