dbx-tools-core 0.6.78__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.
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.3
2
+ Name: dbx-tools-core
3
+ Version: 0.6.78
4
+ Summary: Dependency-free cross-runtime identity helpers for dbx-tools Python packages
5
+ Requires-Python: >=3.10
6
+ Project-URL: Source, https://github.com/reggie-db/dbx-tools/tree/main/packages/py/core
7
+ Description-Content-Type: text/markdown
8
+
9
+ # `dbx-tools-core`
10
+
11
+ Dependency-free Python helpers shared by dbx-tools packages.
12
+
13
+ Install directly from this monorepo:
14
+
15
+ ```bash
16
+ pip install "dbx-tools-core @ git+https://github.com/reggie-db/dbx-tools.git@main#subdirectory=packages/py/core"
17
+ ```
18
+
19
+ The package intentionally contains only the cross-runtime identity primitives
20
+ currently needed by more than one implementation:
21
+
22
+ - `hash.fnv_hash()` — the single-string subset of TypeScript
23
+ `fnvHashWithOptions`, including UTF-16 code-unit hashing and base-32 output;
24
+ - `object.to_stable_key()` — strict structured identity canonicalization;
25
+ - `string.to_identifier()` — readable identifier tokenization, with the same
26
+ hyphen default as TypeScript and an explicit delimiter override for consumers
27
+ such as the underscore-delimited Postgres bus channel.
28
+
29
+ These functions exist so Python packages do not copy the TypeScript algorithms
30
+ locally and silently drift. Add broader helpers only when another Python package
31
+ actually needs them. Their shared behavior is tested from
32
+ `packages/test/polyglot/fixtures/core/fixture.json`, not duplicated in this
33
+ package.
@@ -0,0 +1,25 @@
1
+ # `dbx-tools-core`
2
+
3
+ Dependency-free Python helpers shared by dbx-tools packages.
4
+
5
+ Install directly from this monorepo:
6
+
7
+ ```bash
8
+ pip install "dbx-tools-core @ git+https://github.com/reggie-db/dbx-tools.git@main#subdirectory=packages/py/core"
9
+ ```
10
+
11
+ The package intentionally contains only the cross-runtime identity primitives
12
+ currently needed by more than one implementation:
13
+
14
+ - `hash.fnv_hash()` — the single-string subset of TypeScript
15
+ `fnvHashWithOptions`, including UTF-16 code-unit hashing and base-32 output;
16
+ - `object.to_stable_key()` — strict structured identity canonicalization;
17
+ - `string.to_identifier()` — readable identifier tokenization, with the same
18
+ hyphen default as TypeScript and an explicit delimiter override for consumers
19
+ such as the underscore-delimited Postgres bus channel.
20
+
21
+ These functions exist so Python packages do not copy the TypeScript algorithms
22
+ locally and silently drift. Add broader helpers only when another Python package
23
+ actually needs them. Their shared behavior is tested from
24
+ `packages/test/polyglot/fixtures/core/fixture.json`, not duplicated in this
25
+ package.
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "dbx-tools-core"
3
+ version = "0.6.78"
4
+ description = "Dependency-free cross-runtime identity helpers for dbx-tools Python packages"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = []
8
+
9
+ [project.urls]
10
+ Source = "https://github.com/reggie-db/dbx-tools/tree/main/packages/py/core"
11
+
12
+ [build-system]
13
+ requires = ["uv_build>=0.11.28,<0.12.0"]
14
+ build-backend = "uv_build"
15
+
16
+ [tool.uv.build-backend]
17
+ module-name = "dbx_tools.core"
18
+ module-root = "src"
19
+ namespace = true
@@ -0,0 +1,21 @@
1
+ # ~~ Generated by projen. To modify, edit .projenrc.js and run "bunx projen".
2
+
3
+ [project]
4
+ name = "dbx-tools-core"
5
+ version = "0.6.78"
6
+ description = "Dependency-free cross-runtime identity helpers for dbx-tools Python packages"
7
+ readme = "README.md"
8
+ requires-python = ">=3.10"
9
+ dependencies = [ ]
10
+
11
+ [project.urls]
12
+ Source = "https://github.com/reggie-db/dbx-tools/tree/main/packages/py/core"
13
+
14
+ [build-system]
15
+ requires = [ "uv_build>=0.11.28,<0.12.0" ]
16
+ build-backend = "uv_build"
17
+
18
+ [tool.uv.build-backend]
19
+ module-name = "dbx_tools.core"
20
+ module-root = "src"
21
+ namespace = true
@@ -0,0 +1,16 @@
1
+ from .hash import fnv_hash
2
+ from .object import to_stable_key
3
+ from .string import to_identifier
4
+
5
+ fnvHash = fnv_hash
6
+ toIdentifier = to_identifier
7
+ toStableKey = to_stable_key
8
+
9
+ __all__ = [
10
+ "fnvHash",
11
+ "fnv_hash",
12
+ "toIdentifier",
13
+ "toStableKey",
14
+ "to_identifier",
15
+ "to_stable_key",
16
+ ]
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ _BASE32_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz"
4
+
5
+
6
+ def fnv_hash(value: str, *, length: int = 6) -> str:
7
+ """Match TypeScript ``fnvHashWithOptions`` for one string value."""
8
+ digest = 0x811C9DC5
9
+ for token in ("[", "string:", value, ",", "]"):
10
+ encoded = token.encode("utf-16-le", "surrogatepass")
11
+ for index in range(0, len(encoded), 2):
12
+ code_unit = encoded[index] | (encoded[index + 1] << 8)
13
+ digest ^= code_unit
14
+ digest = (digest * 0x01000193) & 0xFFFFFFFF
15
+ encoded_digest = _to_base32(digest).rjust(7, _BASE32_ALPHABET[0])
16
+ return encoded_digest[: min(length, 7)]
17
+
18
+
19
+ def _to_base32(value: int) -> str:
20
+ if value == 0:
21
+ return _BASE32_ALPHABET[0]
22
+ encoded = ""
23
+ while value:
24
+ encoded = _BASE32_ALPHABET[value & 31] + encoded
25
+ value >>= 5
26
+ return encoded
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from collections.abc import Mapping
5
+ from datetime import date, datetime
6
+
7
+
8
+ def to_stable_key(value: object, seen: set[int] | None = None) -> str:
9
+ """Build the Python equivalent of shared-core's strict stable identity key."""
10
+ if value is None:
11
+ return "null"
12
+ if isinstance(value, str):
13
+ utf16_length = len(value.encode("utf-16-le", "surrogatepass")) // 2
14
+ return f"string:{utf16_length}:{value}"
15
+ if isinstance(value, bool):
16
+ return f"boolean:{str(value).lower()}"
17
+ if isinstance(value, int):
18
+ return f"number:{value}"
19
+ if isinstance(value, float):
20
+ if not math.isfinite(value):
21
+ raise TypeError("Stable keys require finite numbers")
22
+ if value == 0 and math.copysign(1, value) < 0:
23
+ return "number:-0"
24
+ return f"number:{format(value, '.15g')}"
25
+ if isinstance(value, (datetime, date)):
26
+ timestamp = value.isoformat()
27
+ if isinstance(value, datetime) and value.tzinfo is not None:
28
+ timestamp = timestamp.replace("+00:00", "Z")
29
+ return f"date:{timestamp}"
30
+ seen = seen or set()
31
+ identity = id(value)
32
+ if identity in seen:
33
+ raise TypeError("Stable keys cannot contain cycles")
34
+ seen.add(identity)
35
+ try:
36
+ if isinstance(value, (list, tuple)):
37
+ return f"array:[{','.join(to_stable_key(item, seen) for item in value)}]"
38
+ if isinstance(value, (set, frozenset)):
39
+ return f"set:[{','.join(sorted(to_stable_key(item, seen) for item in value))}]"
40
+ if isinstance(value, Mapping):
41
+ entries = sorted(
42
+ f"{to_stable_key(key, seen)}={to_stable_key(item, seen)}"
43
+ for key, item in value.items()
44
+ )
45
+ return f"object:{{{','.join(entries)}}}"
46
+ finally:
47
+ seen.remove(identity)
48
+ raise TypeError(f"Unsupported stable key type: {type(value).__name__}")
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+
6
+ def to_identifier(*values: object, delimiter: str = "-") -> str:
7
+ """Match TypeScript identifier tokenization with an overridable delimiter."""
8
+ tokens: list[str] = []
9
+ for value in values:
10
+ text_value = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1 \2", str(value))
11
+ text_value = re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", text_value)
12
+ tokens.extend(token.lower() for token in re.findall(r"[A-Za-z0-9]+", text_value))
13
+ return delimiter.join(tokens)