dazzle-lib 0.1.0__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.
- dazzle_lib/__init__.py +51 -0
- dazzle_lib/__main__.py +11 -0
- dazzle_lib/_version.py +92 -0
- dazzle_lib/exceptions.py +43 -0
- dazzle_lib/mixins.py +42 -0
- dazzle_lib/payloads.py +127 -0
- dazzle_lib/protocols.py +60 -0
- dazzle_lib-0.1.0.dist-info/METADATA +118 -0
- dazzle_lib-0.1.0.dist-info/RECORD +12 -0
- dazzle_lib-0.1.0.dist-info/WHEEL +5 -0
- dazzle_lib-0.1.0.dist-info/licenses/LICENSE +21 -0
- dazzle_lib-0.1.0.dist-info/top_level.txt +1 -0
dazzle_lib/__init__.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""dazzle-lib -- the DazzleLib stack's bedrock.
|
|
2
|
+
|
|
3
|
+
Shared Protocols, TypedDict payload schemas, and the exception root that every
|
|
4
|
+
``dazzle-*`` library builds on. Types only: this package is stdlib-only forever
|
|
5
|
+
and, by charter, contains no I/O, no path handling, no platform probing, and no
|
|
6
|
+
"utils". See the architecture contract:
|
|
7
|
+
https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from ._version import PIP_VERSION, __app_name__, __version__
|
|
11
|
+
from .exceptions import (
|
|
12
|
+
DazzleError,
|
|
13
|
+
FileOperationError,
|
|
14
|
+
LinkError,
|
|
15
|
+
PathIdentityError,
|
|
16
|
+
PreserveError,
|
|
17
|
+
)
|
|
18
|
+
from .mixins import DazzleDataMixin
|
|
19
|
+
from .payloads import (
|
|
20
|
+
FileMetadataDict,
|
|
21
|
+
HashResultDict,
|
|
22
|
+
LinkTargetDict,
|
|
23
|
+
TimestampsDict,
|
|
24
|
+
UnixMetadataDict,
|
|
25
|
+
WindowsMetadataDict,
|
|
26
|
+
)
|
|
27
|
+
from .protocols import Serializable, Viewable
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"__version__",
|
|
31
|
+
"__app_name__",
|
|
32
|
+
"PIP_VERSION",
|
|
33
|
+
# protocols
|
|
34
|
+
"Viewable",
|
|
35
|
+
"Serializable",
|
|
36
|
+
# payload schemas
|
|
37
|
+
"TimestampsDict",
|
|
38
|
+
"WindowsMetadataDict",
|
|
39
|
+
"UnixMetadataDict",
|
|
40
|
+
"FileMetadataDict",
|
|
41
|
+
"LinkTargetDict",
|
|
42
|
+
"HashResultDict",
|
|
43
|
+
# exceptions
|
|
44
|
+
"DazzleError",
|
|
45
|
+
"PathIdentityError",
|
|
46
|
+
"FileOperationError",
|
|
47
|
+
"LinkError",
|
|
48
|
+
"PreserveError",
|
|
49
|
+
# mixin
|
|
50
|
+
"DazzleDataMixin",
|
|
51
|
+
]
|
dazzle_lib/__main__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""``python -m dazzle_lib`` -- print version and charter (a library, not a tool)."""
|
|
2
|
+
|
|
3
|
+
from ._version import DISPLAY_VERSION, __app_name__
|
|
4
|
+
|
|
5
|
+
if __name__ == "__main__":
|
|
6
|
+
print(f"{__app_name__} {DISPLAY_VERSION}")
|
|
7
|
+
print(
|
|
8
|
+
"The DazzleLib stack's bedrock: Protocols, TypedDict payload schemas, "
|
|
9
|
+
"and the exception root.\nTypes only -- by charter this package "
|
|
10
|
+
"contains no behavior. https://github.com/DazzleLib/dazzle-lib"
|
|
11
|
+
)
|
dazzle_lib/_version.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Version information for dazzle-lib.
|
|
3
|
+
|
|
4
|
+
This file is the canonical source for version numbers.
|
|
5
|
+
The __version__ string is automatically updated by git hooks
|
|
6
|
+
with build metadata (branch, build number, date, commit hash).
|
|
7
|
+
|
|
8
|
+
Format: MAJOR.MINOR.PATCH[-PHASE]_BRANCH_BUILD-YYYYMMDD-COMMITHASH
|
|
9
|
+
Example: 0.1.0_main_1-20260101-a1b2c3d4
|
|
10
|
+
|
|
11
|
+
Version levels:
|
|
12
|
+
PROJECT_PHASE: Global project maturity (prealpha -> alpha -> beta -> stable).
|
|
13
|
+
Changes rarely, when the overall project hits a threshold.
|
|
14
|
+
PHASE: Per-MINOR feature set maturity (alpha -> beta -> "" for stable).
|
|
15
|
+
Drops when a MINOR's feature set is complete.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Version components - edit these for version bumps
|
|
19
|
+
MAJOR = 0
|
|
20
|
+
MINOR = 1
|
|
21
|
+
PATCH = 0
|
|
22
|
+
PHASE = "" # Per-MINOR feature set: "" (stable), "alpha", "beta", "rc1", etc.
|
|
23
|
+
|
|
24
|
+
# Project-level phase (independent of version phase)
|
|
25
|
+
PROJECT_PHASE = "" # "prealpha", "alpha", "beta", "stable", or ""
|
|
26
|
+
|
|
27
|
+
# Auto-updated by git hooks - do not edit manually
|
|
28
|
+
__version__ = "0.1.0_main_5-20260611-b3174573"
|
|
29
|
+
__app_name__ = "dazzle-lib"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_version():
|
|
33
|
+
"""Return the full version string including branch and build info."""
|
|
34
|
+
return __version__
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_base_version():
|
|
38
|
+
"""Return the semantic version string (MAJOR.MINOR.PATCH[-PHASE])."""
|
|
39
|
+
if "_" in __version__:
|
|
40
|
+
return __version__.split("_")[0]
|
|
41
|
+
base = f"{MAJOR}.{MINOR}.{PATCH}"
|
|
42
|
+
if PHASE:
|
|
43
|
+
base = f"{base}-{PHASE}"
|
|
44
|
+
return base
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_display_version():
|
|
48
|
+
"""Return a human-friendly version string with project phase.
|
|
49
|
+
|
|
50
|
+
Example: 'PREALPHA 0.1.0-alpha' or 'BETA 0.5.1' or '1.0.0'
|
|
51
|
+
"""
|
|
52
|
+
base = get_base_version()
|
|
53
|
+
if PROJECT_PHASE and PROJECT_PHASE != "stable":
|
|
54
|
+
return f"{PROJECT_PHASE.upper()} {base}"
|
|
55
|
+
return base
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_pip_version():
|
|
59
|
+
"""
|
|
60
|
+
Return PEP 440 compliant version for pip/setuptools.
|
|
61
|
+
|
|
62
|
+
Converts our version format to PEP 440:
|
|
63
|
+
- Main branch: 0.1.0_main_3-20260404-hash -> 0.1.0
|
|
64
|
+
- Dev branch: 0.1.0_dev_3-20260404-hash -> 0.1.0.dev3
|
|
65
|
+
- Alpha: 0.1.0-alpha_main_3 -> 0.1.0a0
|
|
66
|
+
"""
|
|
67
|
+
base = f"{MAJOR}.{MINOR}.{PATCH}"
|
|
68
|
+
|
|
69
|
+
# Map phase to PEP 440 pre-release segment
|
|
70
|
+
phase_map = {"alpha": "a0", "beta": "b0"}
|
|
71
|
+
if PHASE:
|
|
72
|
+
base += phase_map.get(PHASE, PHASE)
|
|
73
|
+
|
|
74
|
+
if "_" not in __version__:
|
|
75
|
+
return base
|
|
76
|
+
|
|
77
|
+
parts = __version__.split("_")
|
|
78
|
+
branch = parts[1] if len(parts) > 1 else "unknown"
|
|
79
|
+
|
|
80
|
+
if branch == "main":
|
|
81
|
+
return base
|
|
82
|
+
else:
|
|
83
|
+
build_info = "_".join(parts[2:]) if len(parts) > 2 else ""
|
|
84
|
+
build_num = build_info.split("-")[0] if "-" in build_info else "0"
|
|
85
|
+
return f"{base}.dev{build_num}"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# For convenience in imports
|
|
89
|
+
VERSION = get_version()
|
|
90
|
+
BASE_VERSION = get_base_version()
|
|
91
|
+
PIP_VERSION = get_pip_version()
|
|
92
|
+
DISPLAY_VERSION = get_display_version()
|
dazzle_lib/exceptions.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""The DazzleLib exception bedrock.
|
|
2
|
+
|
|
3
|
+
One catchable root (:class:`DazzleError`) for the whole stack, plus one base
|
|
4
|
+
per layer domain. Each stack library derives ITS OWN exceptions from the
|
|
5
|
+
matching domain base (e.g. dazzle-preservelib's ``ManifestVersionError`` would
|
|
6
|
+
subclass :class:`PreserveError`), so consumers can choose their catch
|
|
7
|
+
granularity: a specific error, a domain, or the whole stack.
|
|
8
|
+
|
|
9
|
+
Charter reminder: these are plain exception types. No behavior beyond
|
|
10
|
+
``__init__``-style state, no I/O.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DazzleError",
|
|
15
|
+
"PathIdentityError",
|
|
16
|
+
"FileOperationError",
|
|
17
|
+
"LinkError",
|
|
18
|
+
"PreserveError",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DazzleError(Exception):
|
|
23
|
+
"""Root of every exception raised by a DazzleLib stack library."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PathIdentityError(DazzleError):
|
|
27
|
+
"""Domain base for path-identity failures (dazzle-unctools, L0):
|
|
28
|
+
UNC/drive mapping, origin classification, identity probing."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class FileOperationError(DazzleError):
|
|
32
|
+
"""Domain base for filesystem-primitive failures (dazzle-filekit, L1):
|
|
33
|
+
copy/move, metadata collect/apply, hashing, link creation."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LinkError(DazzleError):
|
|
37
|
+
"""Domain base for link-serialization failures (dazzle-linklib, L2):
|
|
38
|
+
.dazzlelink parsing, export/import, rebase."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PreserveError(DazzleError):
|
|
42
|
+
"""Domain base for orchestration failures (dazzle-preservelib, L3):
|
|
43
|
+
manifests, transactional copy/move/restore/verify, conflict policy."""
|
dazzle_lib/mixins.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""The one permitted mixin: derive presentation from ``to_dict``.
|
|
2
|
+
|
|
3
|
+
:class:`DazzleDataMixin` is convenience, not contract -- objects may satisfy
|
|
4
|
+
the protocols without it. It exists so simple data objects don't each rewrite
|
|
5
|
+
``to_json``/``__str__``/``summary`` plumbing. Anything fancier belongs in the
|
|
6
|
+
object itself, not here (charter: this module stays this small).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
__all__ = ["DazzleDataMixin"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DazzleDataMixin:
|
|
16
|
+
"""Derives ``to_json``, ``summary`` and ``__str__`` from ``to_dict``.
|
|
17
|
+
|
|
18
|
+
The host class supplies ``to_dict()`` (and thereby decides what is
|
|
19
|
+
JSON-safe); the mixin only formats. Combined with a ``from_dict``
|
|
20
|
+
classmethod and a ``SCHEMA_VERSION`` attribute, a host class satisfies
|
|
21
|
+
both :class:`~dazzle_lib.protocols.Serializable` and
|
|
22
|
+
:class:`~dazzle_lib.protocols.Viewable`.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def to_dict(self) -> Dict[str, Any]: # pragma: no cover - host overrides
|
|
26
|
+
raise NotImplementedError(
|
|
27
|
+
f"{type(self).__name__} must implement to_dict() to use DazzleDataMixin"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def to_json(self, *, indent: int = 2, sort_keys: bool = False) -> str:
|
|
31
|
+
"""JSON form of :meth:`to_dict` (``default=str`` catches stragglers)."""
|
|
32
|
+
return json.dumps(
|
|
33
|
+
self.to_dict(), indent=indent, sort_keys=sort_keys, default=str
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def summary(self) -> str:
|
|
37
|
+
"""One-line description: class name plus top-level keys."""
|
|
38
|
+
keys = ", ".join(list(self.to_dict().keys())[:6])
|
|
39
|
+
return f"{type(self).__name__}({keys})"
|
|
40
|
+
|
|
41
|
+
def __str__(self) -> str:
|
|
42
|
+
return self.to_json()
|
dazzle_lib/payloads.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""TypedDict payload schemas shared across the DazzleLib stack.
|
|
2
|
+
|
|
3
|
+
These are THE cross-layer payload shapes (STACK-MAP D10): when one stack
|
|
4
|
+
library hands file metadata, timestamps, link descriptions, or hash results to
|
|
5
|
+
another, the value conforms to a shape defined here. Rich objects in upper
|
|
6
|
+
layers serialize themselves INTO these shapes; primitive functions in lower
|
|
7
|
+
layers take and return them directly.
|
|
8
|
+
|
|
9
|
+
The shapes are not invented -- they mirror what ``dazzle-filekit`` actually
|
|
10
|
+
produces today (``collect_file_metadata`` / ``collect_timestamp_info`` /
|
|
11
|
+
``calculate_file_hash``), so adopting them is a typing change, not a behavior
|
|
12
|
+
change. Admission policy (rule of two): a shape lives here only once two or
|
|
13
|
+
more stack libraries need it.
|
|
14
|
+
|
|
15
|
+
Charter reminder: this module is types-only. No I/O, no behavior.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Dict, Optional, TypedDict
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"TimestampsDict",
|
|
22
|
+
"WindowsMetadataDict",
|
|
23
|
+
"UnixMetadataDict",
|
|
24
|
+
"FileMetadataDict",
|
|
25
|
+
"LinkTargetDict",
|
|
26
|
+
"HashResultDict",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TimestampsDict(TypedDict, total=False):
|
|
31
|
+
"""File timestamps, epoch floats plus ISO-8601 projections.
|
|
32
|
+
|
|
33
|
+
Mirrors ``dazzle_filekit.metadata.collect_timestamp_info``. Note that
|
|
34
|
+
``created`` carries ``st_ctime``, which is creation time on Windows but
|
|
35
|
+
inode-change time on Unix -- consumers must not assume birth-time
|
|
36
|
+
semantics cross-platform.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
created: float
|
|
40
|
+
modified: float
|
|
41
|
+
accessed: float
|
|
42
|
+
created_iso: str
|
|
43
|
+
modified_iso: str
|
|
44
|
+
accessed_iso: str
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class WindowsMetadataDict(TypedDict, total=False):
|
|
48
|
+
"""Windows-specific metadata (mirrors filekit's ``_collect_windows_metadata``).
|
|
49
|
+
|
|
50
|
+
With pywin32 available the rich fields are present (``attributes``,
|
|
51
|
+
owner/group + SIDs, ``security_descriptor_sddl``); without it the
|
|
52
|
+
``attrib``-fallback path fills only the boolean flags and
|
|
53
|
+
``attrib_output``. ``security_descriptor_sddl`` may be present-but-None
|
|
54
|
+
when the descriptor could not be stringified.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
attributes: int
|
|
58
|
+
is_hidden: bool
|
|
59
|
+
is_system: bool
|
|
60
|
+
is_readonly: bool
|
|
61
|
+
is_archive: bool
|
|
62
|
+
owner: str
|
|
63
|
+
group: str
|
|
64
|
+
owner_sid: str
|
|
65
|
+
group_sid: str
|
|
66
|
+
security_descriptor_sddl: Optional[str]
|
|
67
|
+
attrib_output: str
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class UnixMetadataDict(TypedDict, total=False):
|
|
71
|
+
"""Unix-specific metadata (mirrors filekit's unix branch)."""
|
|
72
|
+
|
|
73
|
+
uid: int
|
|
74
|
+
gid: int
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _FileMetadataRequired(TypedDict):
|
|
78
|
+
mode: int
|
|
79
|
+
size: int
|
|
80
|
+
timestamps: TimestampsDict
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class FileMetadataDict(_FileMetadataRequired, total=False):
|
|
84
|
+
"""A file's preservable metadata snapshot.
|
|
85
|
+
|
|
86
|
+
Mirrors ``dazzle_filekit.metadata.collect_file_metadata``: ``mode``,
|
|
87
|
+
``size``, and ``timestamps`` are always present; exactly one of
|
|
88
|
+
``windows`` / ``unix`` appears depending on platform; ``xattrs`` (name ->
|
|
89
|
+
base64-encoded value) appears on Unix when extended attributes exist.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
windows: WindowsMetadataDict
|
|
93
|
+
unix: UnixMetadataDict
|
|
94
|
+
xattrs: Dict[str, str]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LinkTargetDict(TypedDict, total=False):
|
|
98
|
+
"""An intrinsic description of a filesystem link, as data.
|
|
99
|
+
|
|
100
|
+
The cross-layer shape for "what is this link": produced by link analysis
|
|
101
|
+
(dazzle-filekit's ``analyze_link``), embedded in serialized link files
|
|
102
|
+
(dazzle-linklib), and consumed by orchestration policy (dazzle-preservelib).
|
|
103
|
+
Intrinsic properties only -- anything relative to a specific operation
|
|
104
|
+
(e.g. "does this link point inside the move destination") is computed at
|
|
105
|
+
the orchestration layer and does NOT belong here.
|
|
106
|
+
|
|
107
|
+
``kind`` is one of ``"symlink"``, ``"junction"``, ``"hardlink"``.
|
|
108
|
+
``raw_target`` is the stored target text exactly as written; ``resolved_target``
|
|
109
|
+
is its absolute resolution (when resolvable). ``target_is_directory`` is
|
|
110
|
+
best-effort (False when the target is missing).
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
kind: str
|
|
114
|
+
raw_target: str
|
|
115
|
+
resolved_target: str
|
|
116
|
+
is_broken: bool
|
|
117
|
+
is_circular: bool
|
|
118
|
+
target_is_directory: bool
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
HashResultDict = Dict[str, str]
|
|
122
|
+
"""Hash results keyed by algorithm name, hex digests as values.
|
|
123
|
+
|
|
124
|
+
Mirrors ``dazzle_filekit.verification.calculate_file_hash`` (e.g.
|
|
125
|
+
``{"sha256": "ab12...", "md5": "cd34..."}``). A plain ``Dict`` alias rather
|
|
126
|
+
than a TypedDict because the key set is open (any hashlib algorithm).
|
|
127
|
+
"""
|
dazzle_lib/protocols.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Structural protocols every DazzleLib stack object is expected to satisfy.
|
|
2
|
+
|
|
3
|
+
These are :class:`typing.Protocol` definitions -- STRUCTURAL contracts, not base
|
|
4
|
+
classes. Nothing in the stack is required to subclass anything here; an object
|
|
5
|
+
satisfies a protocol simply by implementing its methods (and can be checked at
|
|
6
|
+
runtime with ``isinstance`` because both are ``@runtime_checkable``).
|
|
7
|
+
|
|
8
|
+
The design stance (STACK-MAP D10): "the dict is the interface; objects know how
|
|
9
|
+
to become dicts." Rich objects in upper layers (manifest, link-data, results)
|
|
10
|
+
serialize themselves INTO the plain TypedDict payload shapes defined in
|
|
11
|
+
:mod:`dazzle_lib.payloads`; lower-layer functions take and return those dicts.
|
|
12
|
+
|
|
13
|
+
Charter reminder: this module is types-only. No I/O, no behavior.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import Any, Dict, Protocol, runtime_checkable
|
|
17
|
+
|
|
18
|
+
__all__ = ["Viewable", "Serializable"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@runtime_checkable
|
|
22
|
+
class Viewable(Protocol):
|
|
23
|
+
"""An object that can present itself to a human.
|
|
24
|
+
|
|
25
|
+
Expectations:
|
|
26
|
+
|
|
27
|
+
- ``summary()`` returns a SHORT one-line description suitable for list
|
|
28
|
+
views, log lines, and progress output.
|
|
29
|
+
- ``__str__`` may be longer (multi-line is fine) but must never raise.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def summary(self) -> str:
|
|
33
|
+
"""One-line human-readable description of this object."""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@runtime_checkable
|
|
38
|
+
class Serializable(Protocol):
|
|
39
|
+
"""An object that can round-trip through a plain, JSON-safe dict.
|
|
40
|
+
|
|
41
|
+
Expectations:
|
|
42
|
+
|
|
43
|
+
- ``to_dict()`` returns a dict containing only JSON-safe values
|
|
44
|
+
(str/int/float/bool/None/list/dict). Where a shared payload shape
|
|
45
|
+
exists in :mod:`dazzle_lib.payloads`, the dict conforms to it.
|
|
46
|
+
- ``from_dict(data)`` is a classmethod constructing an equivalent object.
|
|
47
|
+
- ``SCHEMA_VERSION`` identifies the dict layout so readers can migrate
|
|
48
|
+
old serialized forms. Bump it on any breaking shape change.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
SCHEMA_VERSION: int
|
|
52
|
+
|
|
53
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
54
|
+
"""Return a JSON-safe dict representation of this object."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Serializable":
|
|
59
|
+
"""Construct an instance from a dict produced by :meth:`to_dict`."""
|
|
60
|
+
...
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dazzle-lib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The DazzleLib bedrock: shared Protocols, TypedDict payload schemas, and exception root for the dazzle-* library stack. Stdlib-only by charter.
|
|
5
|
+
Author-email: djdarcy <djdarcy@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/DazzleLib/dazzle-lib
|
|
8
|
+
Project-URL: Repository, https://github.com/DazzleLib/dazzle-lib
|
|
9
|
+
Project-URL: Issues, https://github.com/DazzleLib/dazzle-lib/issues
|
|
10
|
+
Keywords: dazzlelib,protocols,typeddict,serialization,bedrock,stack
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# dazzle-lib
|
|
32
|
+
|
|
33
|
+
**The DazzleLib stack's bedrock: shared Protocols, TypedDict payload schemas, and the exception root.**
|
|
34
|
+
|
|
35
|
+
[](https://pypi.org/project/dazzle-lib/)
|
|
36
|
+
[](https://pypi.org/project/dazzle-lib/)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](docs/platform-support.md)
|
|
39
|
+
|
|
40
|
+
Every `dazzle-*` library ([the stack](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md)) builds on this package: it defines what stack objects can be expected to do (view themselves, serialize themselves) and what shapes cross-layer payloads have. **Types only** -- by charter this package contains no I/O, no path handling, no platform probing, and no behavior, forever.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install dazzle-lib
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## What's inside (all of it)
|
|
47
|
+
|
|
48
|
+
| Module | Contents |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `dazzle_lib.protocols` | `Viewable` (`summary()`/`__str__`), `Serializable` (`to_dict`/`from_dict`/`to_json`, `SCHEMA_VERSION`) -- structural `Protocol`s, `runtime_checkable`, nothing is forced to subclass |
|
|
51
|
+
| `dazzle_lib.payloads` | The cross-layer TypedDict schemas: `FileMetadataDict`, `TimestampsDict`, `WindowsMetadataDict`, `UnixMetadataDict`, `LinkTargetDict`, `HashResultDict` -- mirroring what `dazzle-filekit` actually produces |
|
|
52
|
+
| `dazzle_lib.exceptions` | `DazzleError` root + per-domain bases (`PathIdentityError`, `FileOperationError`, `LinkError`, `PreserveError`) |
|
|
53
|
+
| `dazzle_lib.mixins` | `DazzleDataMixin` -- derives `to_json`/`summary`/`__str__` from your `to_dict` |
|
|
54
|
+
|
|
55
|
+
## The idea: the dict is the interface
|
|
56
|
+
|
|
57
|
+
Rich objects in upper layers know how to become plain dicts; lower-layer functions take and return those dicts. The TypedDicts here are the agreed shapes, so a manifest object in `dazzle-preservelib` and a metadata collector in `dazzle-filekit` speak the same payload without sharing a class hierarchy:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from dataclasses import dataclass
|
|
61
|
+
from dazzle_lib import DazzleDataMixin, Serializable, FileMetadataDict
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class TransferResult(DazzleDataMixin):
|
|
65
|
+
SCHEMA_VERSION = 1
|
|
66
|
+
path: str
|
|
67
|
+
metadata: FileMetadataDict
|
|
68
|
+
|
|
69
|
+
def to_dict(self):
|
|
70
|
+
return {"schema_version": self.SCHEMA_VERSION,
|
|
71
|
+
"path": self.path, "metadata": dict(self.metadata)}
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_dict(cls, data):
|
|
75
|
+
return cls(path=data["path"], metadata=data["metadata"])
|
|
76
|
+
|
|
77
|
+
result = TransferResult("a.txt", {"mode": 0o644, "size": 10, "timestamps": {}})
|
|
78
|
+
assert isinstance(result, Serializable) # structural -- no subclassing needed
|
|
79
|
+
print(result.summary()) # one-liner for logs
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
And one catchable root for the whole stack:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from dazzle_lib import DazzleError
|
|
86
|
+
try:
|
|
87
|
+
... # any dazzle-* library call
|
|
88
|
+
except DazzleError as e:
|
|
89
|
+
... # caught, whichever layer raised it
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## The charter (enforced by tests)
|
|
93
|
+
|
|
94
|
+
This package is **stdlib-only forever** and contains **no behavior**. `tests/test_charter.py` fails on any banned import (`os`, `shutil`, `pathlib`, `subprocess`, ...) anywhere in the package -- a PR that needs to weaken that test is adding something that belongs in a higher layer. Admission follows the **rule of two**: a Protocol or TypedDict enters the bedrock only when two or more stack libraries need it.
|
|
95
|
+
|
|
96
|
+
## The stack
|
|
97
|
+
|
|
98
|
+
| Layer | Library | Role |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| B | **dazzle-lib** (this) | bedrock contracts |
|
|
101
|
+
| L0 | [dazzle-unctools](https://github.com/DazzleLib/UNCtools) | path identity (UNC/drive/origin) |
|
|
102
|
+
| L1 | [dazzle-filekit](https://github.com/DazzleLib/dazzle-filekit) | filesystem primitives |
|
|
103
|
+
| L2 | dazzle-linklib *(planned)* | link serialization |
|
|
104
|
+
| L3 | dazzle-preservelib *(planned)* | operation orchestration |
|
|
105
|
+
| ⊥ | [dazzle-treelib](https://github.com/DazzleLib/dazzle-tree-lib) | traversal engine |
|
|
106
|
+
|
|
107
|
+
Full architecture contract: [STACK-MAP.md](https://github.com/DazzleLib/.github/blob/main/docs/STACK-MAP.md). API stability policy: [docs/api-stability.md](docs/api-stability.md).
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install -e ".[dev]"
|
|
113
|
+
python -m pytest tests/ -v
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT -- the bedrock sits beneath MIT and GPL stack members alike, so it carries the permissive license.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
dazzle_lib/__init__.py,sha256=uxV2FjmpcWyjTVmeaVERrXhdRMUnECU7fJcafgPEW_c,1270
|
|
2
|
+
dazzle_lib/__main__.py,sha256=rQt-ikb4ZbOP_81eR13beNDGtGEwT8lHvaJVNv2oihs,456
|
|
3
|
+
dazzle_lib/_version.py,sha256=Sr_O8_AIZZH5bnUUI46Ez4i4BxV5Um9IXOCzUABWObA,2772
|
|
4
|
+
dazzle_lib/exceptions.py,sha256=0UPGcyO6TTmB5iBhbeM3oXIsLTeoSiUOLiXBpzQE0lc,1429
|
|
5
|
+
dazzle_lib/mixins.py,sha256=fvaUx9Q8y72Eck9QWJKxDF_tf8pxmhDVPSYop6HePJ4,1613
|
|
6
|
+
dazzle_lib/payloads.py,sha256=ktr15Vj4hgmI9UzLdgCr_xHN8nCgXy_KY5a9sjor-x8,4258
|
|
7
|
+
dazzle_lib/protocols.py,sha256=vmH8fco_bz_VSeGoUAgny3KMf04VqhU5IAe50MxcAZA,2195
|
|
8
|
+
dazzle_lib-0.1.0.dist-info/licenses/LICENSE,sha256=DlZy6_97dpafvd_ObU8YNvyk5Gzk_qX070j7pKyDJBY,1069
|
|
9
|
+
dazzle_lib-0.1.0.dist-info/METADATA,sha256=n4Ybhg5AQrl6MBBl_udbGpFtiGjQo3RCgdrZDdSwyDg,5611
|
|
10
|
+
dazzle_lib-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
11
|
+
dazzle_lib-0.1.0.dist-info/top_level.txt,sha256=Ng-Ykc6wxyQQ9gb3Oxgzrb1SdTbWF8HaqI485_tM_JM,11
|
|
12
|
+
dazzle_lib-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dustin Darcy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dazzle_lib
|