From ef26871fd6ed48d67f578e96884abe9b28ad630c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Nov 2025 01:14:14 +0000 Subject: [PATCH 1/5] Document absence package architecture Add comprehensive architecture documentation including: - System overview with component descriptions and relationships - Three ADRs documenting key architectural decisions: - ADR 001: Falsey behavior via inheritance from falsifier - ADR 002: Non-picklable sentinels for singleton semantics - ADR 003: Singleton implementation via __new__ override --- .../001-falsey-behavior-via-inheritance.rst | 103 ++++++++++++++ .../decisions/002-non-picklable-sentinels.rst | 110 +++++++++++++++ .../003-singleton-via-new-override.rst | 118 ++++++++++++++++ .../architecture/decisions/index.rst | 5 +- documentation/architecture/summary.rst | 129 +++++++++++++++++- 5 files changed, 462 insertions(+), 3 deletions(-) create mode 100644 documentation/architecture/decisions/001-falsey-behavior-via-inheritance.rst create mode 100644 documentation/architecture/decisions/002-non-picklable-sentinels.rst create mode 100644 documentation/architecture/decisions/003-singleton-via-new-override.rst diff --git a/documentation/architecture/decisions/001-falsey-behavior-via-inheritance.rst b/documentation/architecture/decisions/001-falsey-behavior-via-inheritance.rst new file mode 100644 index 0000000..7106f89 --- /dev/null +++ b/documentation/architecture/decisions/001-falsey-behavior-via-inheritance.rst @@ -0,0 +1,103 @@ +.. vim: set fileencoding=utf-8: +.. -*- coding: utf-8 -*- +.. +--------------------------------------------------------------------------+ + | | + | Licensed under the Apache License, Version 2.0 (the "License"); | + | you may not use this file except in compliance with the License. | + | You may obtain a copy of the License at | + | | + | http://www.apache.org/licenses/LICENSE-2.0 | + | | + | Unless required by applicable law or agreed to in writing, software | + | distributed under the License is distributed on an "AS IS" BASIS, | + | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | + | See the License for the specific language governing permissions and | + | limitations under the License. | + | | + +--------------------------------------------------------------------------+ + + +******************************************************************************* +001. Falsey Behavior via Inheritance +******************************************************************************* + +Status +=============================================================================== + +Accepted + +Context +=============================================================================== + +Absence sentinels need to evaluate to ``False`` in boolean contexts to enable +natural conditional checks like ``if not is_absent(value)``. Python provides +two approaches for creating falsey custom objects: + +1. Implement ``__bool__`` to return ``False`` +2. Inherit from a base class that provides this behavior + +The ``falsifier`` package provides a ``Falsifier`` base class specifically +designed for objects that should always evaluate to ``False``. This package is +maintained by the same author and follows the same design philosophy. + +The key requirement is that absence sentinels should be falsey without requiring +each sentinel class to implement the behavior independently. + +Decision +=============================================================================== + +Inherit from ``falsifier.Falsifier`` as the base class for ``AbsenceFactory`` +rather than implementing ``__bool__`` directly. + +Alternatives +=============================================================================== + +**Implement __bool__ directly in AbsenceFactory** + +Rejected because: + +- Duplicates functionality available in a focused, reusable library +- Violates DRY principle when a well-tested implementation exists +- Increases maintenance burden by duplicating logic across packages +- Misses opportunity to leverage specialized package for common pattern + +**Use composition with a falsey mixin** + +Rejected because: + +- Adds unnecessary complexity for single-behavior inheritance +- Inheritance hierarchy is already simple and focused +- No compelling reason to prefer composition over inheritance here +- Would require additional boilerplate for the same result + +**Do nothing (leave sentinels truthy)** + +Rejected because: + +- Contradicts the primary use case of representing absence +- Creates confusing semantics where "absent" evaluates to True +- Forces users to always use explicit ``is_absent()`` checks +- Inconsistent with Python conventions for sentinel values + +Consequences +=============================================================================== + +**Positive:** + +- Leverages well-tested, focused library for falsey behavior +- Maintains consistency with other packages in the project family +- Reduces code duplication across related packages +- Clear separation between absence semantics and falsey implementation +- Enables focus on absence-specific features rather than basic behavior + +**Negative:** + +- Adds external dependency on ``falsifier`` package +- Introduces small coupling between packages +- Users must install ``falsifier`` as a dependency +- Changes to ``falsifier`` API could require updates + +**Neutral:** + +- Establishes pattern for reusing behavior across package family +- Reinforces focus on single-purpose, composable packages diff --git a/documentation/architecture/decisions/002-non-picklable-sentinels.rst b/documentation/architecture/decisions/002-non-picklable-sentinels.rst new file mode 100644 index 0000000..d76161e --- /dev/null +++ b/documentation/architecture/decisions/002-non-picklable-sentinels.rst @@ -0,0 +1,110 @@ +.. vim: set fileencoding=utf-8: +.. -*- coding: utf-8 -*- +.. +--------------------------------------------------------------------------+ + | | + | Licensed under the Apache License, Version 2.0 (the "License"); | + | you may not use this file except in compliance with the License. | + | You may obtain a copy of the License at | + | | + | http://www.apache.org/licenses/LICENSE-2.0 | + | | + | Unless required by applicable law or agreed to in writing, software | + | distributed under the License is distributed on an "AS IS" BASIS, | + | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | + | See the License for the specific language governing permissions and | + | limitations under the License. | + | | + +--------------------------------------------------------------------------+ + + +******************************************************************************* +002. Non-Picklable Sentinels +******************************************************************************* + +Status +=============================================================================== + +Accepted + +Context +=============================================================================== + +Absence sentinels rely on identity checks (``is`` operator) for correctness. +The ``is_absent()`` predicate uses ``absent is value`` to determine if a value +is the global absence sentinel. Singleton semantics are critical for this +identity-based checking to work reliably. + +Python's ``pickle`` module serializes objects for storage or transmission and +recreates them during unpickling. By default, unpickling creates new object +instances, which breaks singleton semantics: + +- ``pickle.loads(pickle.dumps(absent)) is absent`` would be ``False`` +- Multiple "copies" of sentinels would exist after unpickling +- Identity-based checks would fail unpredictably + +While custom ``__reduce__`` implementations can preserve singleton semantics +during unpickling, this requires careful coordination and increases complexity. + +The fundamental question is whether absence sentinels should be serializable at +all, and if so, what guarantees should be provided. + +Decision +=============================================================================== + +Make absence sentinels explicitly non-picklable by implementing ``__reduce__`` +to raise ``OperationValidityError``. + +Alternatives +=============================================================================== + +**Implement singleton-preserving pickle support** + +Rejected because: + +- Absence sentinels represent transient "missing value" states in function calls +- Serializing absence sentinels indicates likely design smell in calling code +- Complexity of singleton preservation during unpickling outweighs benefits +- Better to fail explicitly than create subtle identity bugs +- Forces users to handle absence before serialization boundary + +**Allow default pickling behavior** + +Rejected because: + +- Breaks singleton semantics silently after unpickling +- Creates subtle bugs where ``is`` checks fail unexpectedly +- Users might not realize unpickling creates distinct instances +- Violates principle of least surprise for sentinel objects + +**Do nothing (inherit pickle behavior from base class)** + +Rejected because: + +- Unclear what ``falsifier.Falsifier`` pickle behavior is +- Leaves critical semantic guarantee to inherited implementation +- Should explicitly document this architectural constraint +- Making picklability explicit improves API clarity + +Consequences +=============================================================================== + +**Positive:** + +- Prevents subtle identity-based bugs from pickle/unpickle cycles +- Forces explicit handling of absence before serialization boundaries +- Clear error message when attempting invalid operation +- Maintains singleton semantic guarantees across all code paths +- Encourages better API design (resolve absence before serialization) + +**Negative:** + +- Cannot serialize data structures containing absence sentinels +- Users must explicitly handle absence before pickling +- Adds restriction that some users might find inconvenient +- Requires wrapping or transformation before serialization + +**Neutral:** + +- Aligns with common sentinel object patterns in Python ecosystem +- Consistent with ``Ellipsis`` and other singleton semantics +- Encourages treating absence as transient intermediate state diff --git a/documentation/architecture/decisions/003-singleton-via-new-override.rst b/documentation/architecture/decisions/003-singleton-via-new-override.rst new file mode 100644 index 0000000..4e70e7e --- /dev/null +++ b/documentation/architecture/decisions/003-singleton-via-new-override.rst @@ -0,0 +1,118 @@ +.. vim: set fileencoding=utf-8: +.. -*- coding: utf-8 -*- +.. +--------------------------------------------------------------------------+ + | | + | Licensed under the Apache License, Version 2.0 (the "License"); | + | you may not use this file except in compliance with the License. | + | You may obtain a copy of the License at | + | | + | http://www.apache.org/licenses/LICENSE-2.0 | + | | + | Unless required by applicable law or agreed to in writing, software | + | distributed under the License is distributed on an "AS IS" BASIS, | + | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | + | See the License for the specific language governing permissions and | + | limitations under the License. | + | | + +--------------------------------------------------------------------------+ + + +******************************************************************************* +003. Singleton via __new__ Override +******************************************************************************* + +Status +=============================================================================== + +Accepted + +Context +=============================================================================== + +The global ``absent`` sentinel must be a singleton to enable identity-based +checking via the ``is`` operator. The ``is_absent()`` predicate relies on +``absent is value`` comparisons, which only work reliably if exactly one +instance exists. + +Python provides several approaches for implementing singletons: + +1. **Module-level instance**: Create instance at module level during import +2. **__new__ override**: Control instance creation in ``__new__`` method +3. **Metaclass**: Use custom metaclass to control class instantiation +4. **Decorator**: Apply singleton decorator to the class +5. **Borg pattern**: Share state between instances rather than identity + +The chosen approach must ensure that ``AbsentSingleton()`` always returns the +same instance while maintaining clear, understandable code without excessive +complexity. + +Decision +=============================================================================== + +Implement singleton pattern by overriding ``__new__`` to check for existing +instance in module globals and return it if present, otherwise create new +instance. + +Alternatives +=============================================================================== + +**Module-level instance only (no enforced singleton)** + +Rejected because: + +- Users could accidentally create ``AbsentSingleton()`` and get different instance +- No protection against breaking identity-based checks +- Requires documentation and convention rather than enforcement +- Subtle bugs if users call constructor instead of using module constant + +**Metaclass-based singleton** + +Rejected because: + +- Adds complexity for functionality that doesn't benefit from metaclass features +- Harder to understand for developers unfamiliar with metaclasses +- Inheritance complications if subclasses needed different behavior +- Overkill for simple singleton requirement + +**Singleton decorator** + +Rejected because: + +- Requires external decorator implementation or dependency +- Adds layer of indirection in class definition +- Less explicit control over singleton behavior +- Decorator pattern less familiar in this context + +**Borg pattern (shared state)** + +Rejected because: + +- Identity checks (``is``) require same object, not shared state +- More complex than needed for immutable sentinel +- Doesn't actually solve the requirement +- Philosophical mismatch with sentinel concept + +Consequences +=============================================================================== + +**Positive:** + +- Explicit, self-contained singleton implementation +- No external dependencies or metaclass complexity +- Works naturally with existing inheritance from ``AbsenceFactory`` +- Clear control flow in ``__new__`` method +- Prevents accidental creation of multiple instances +- Familiar pattern to Python developers + +**Negative:** + +- Slightly more complex than module-level constant alone +- ``__new__`` override can be subtle for developers unfamiliar with pattern +- Checks globals on each construction attempt (minor performance cost) +- Does not prevent subclasses from breaking singleton semantics + +**Neutral:** + +- Standard Python idiom for enforced singletons +- Complements module-level ``absent`` constant +- Balances simplicity with enforcement diff --git a/documentation/architecture/decisions/index.rst b/documentation/architecture/decisions/index.rst index a84ce73..0fafa88 100644 --- a/documentation/architecture/decisions/index.rst +++ b/documentation/architecture/decisions/index.rst @@ -24,8 +24,9 @@ Architectural Decision Records .. toctree:: :maxdepth: 2 - -.. todo:: Add architectural decision records to toctree. + 001-falsey-behavior-via-inheritance + 002-non-picklable-sentinels + 003-singleton-via-new-override For ADR format and guidance, see the `architecture documentation guide `_. \ No newline at end of file diff --git a/documentation/architecture/summary.rst b/documentation/architecture/summary.rst index bab368a..064e796 100644 --- a/documentation/architecture/summary.rst +++ b/documentation/architecture/summary.rst @@ -21,4 +21,131 @@ System Overview ******************************************************************************* -.. todo:: Describe the high-level system architecture, major components, and their relationships. \ No newline at end of file +The ``absence`` package provides a falsey sentinel singleton and factory for +representing absent values in contexts where ``None`` or ``False`` might be +valid values. This is a focused, single-purpose library designed around +simplicity and type safety. + +Major Components +=============================================================================== + +The system consists of four primary components organized in a flat module +structure: + +Sentinel Factory (``objects.py``) +------------------------------------------------------------------------------- + +**AbsenceFactory** + Base class for creating arbitrary absence sentinels. Inherits from + ``falsifier.Falsifier`` to ensure falsey behavior in boolean contexts. + Provides customization hooks for ``__repr__`` and ``__str__`` representations. + Explicitly prevents pickling to maintain singleton semantics. + +**AbsentSingleton** + Specialized subclass implementing the global ``absent`` sentinel using + singleton pattern. Overrides ``__new__`` to ensure only one instance exists + per process. Provides fixed string representations. + +**Type Utilities** + - ``is_absence()``: Type guard checking if value is any AbsenceFactory instance + - ``is_absent()``: Type guard checking if value is the global ``absent`` singleton + - ``Absential[T]``: Type alias equivalent to ``T | AbsentSingleton`` + +Builtins Integration (``installers.py``) +------------------------------------------------------------------------------- + +**install() function** + Provides optional installation of ``absent`` sentinel and ``is_absent`` + predicate into Python's builtins module, following naming conventions of + built-in types (``None``, ``Ellipsis``) and predicates (``isinstance``). + Supports customizable names for both components. + +Exception Hierarchy (``exceptions.py``) +------------------------------------------------------------------------------- + +**Omniexception/Omnierror** + Base exception types integrating with ``classcore`` attribute visibility + system for consistent exception handling across the package. + +**OperationValidityError** + Raised when invalid operations are attempted (e.g., pickling absence sentinels). + +Import Management (``__/`` subpackage) +------------------------------------------------------------------------------- + +**Centralized imports hub** + - ``imports.py``: External library imports (``classcore``, ``dynadoc``, ``typing_extensions``) + - ``nomina.py``: Common type aliases and package-specific names + - ``__init__.py``: Re-exports for consistent access via ``from . import __`` pattern + +Component Relationships +=============================================================================== + +The architecture maintains strict separation of concerns: + +.. code-block:: + + __init__.py + ├── imports from objects.py + │ ├── AbsenceFactory (base) + │ ├── AbsentSingleton (singleton) + │ ├── absent (global instance) + │ ├── is_absence() (predicate) + │ ├── is_absent() (predicate) + │ └── Absential (type alias) + ├── imports from installers.py + │ └── install() (builtins integration) + └── imports from exceptions.py + └── exception hierarchy + + All modules depend on __/ for imports + objects.py depends on exceptions.py and falsifier + installers.py depends on objects.py + No circular dependencies + +Key Architectural Patterns +=============================================================================== + +**Singleton Pattern** + ``AbsentSingleton`` implements singleton via ``__new__`` override, + ensuring process-wide uniqueness of the ``absent`` sentinel. + +**Factory Pattern** + ``AbsenceFactory`` enables creation of package-specific absence sentinels + while maintaining consistent behavior. + +**Type Safety** + Type guards (``TypeIs``) and type aliases (``Absential``) provide static + type checking support for absence detection. + +**Inheritance-Based Behavior** + Falsey behavior inherited from ``falsifier.Falsifier`` rather than + implemented directly, following composition over duplication. + +**Immutability by Convention** + Absence sentinels prevent pickling and discourage modification, though + complete immutability is documented as a future enhancement. + +Data Flow +=============================================================================== + +The package has minimal data flow as it primarily provides sentinel values: + +1. Package initialization creates the global ``absent`` singleton +2. User code imports ``absent``, predicates, and types +3. User code uses ``absent`` as default values or sentinel markers +4. Predicates check identity (``is``) against sentinels +5. Optional: ``install()`` exposes symbols to builtins namespace + +Deployment Architecture +=============================================================================== + +Pure Python library with no external runtime dependencies beyond: + +- ``falsifier``: Provides base falsey object behavior +- ``classcore``: Standard object utilities and attribute management +- ``dynadoc``: Documentation annotation support +- ``typing_extensions``: Modern type hint support + +Deployed as a standard Python package via PyPI. No services, daemons, or +persistent state. \ No newline at end of file From 9944f141af26781e14119c038b28b9ef69c866a4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Nov 2025 02:08:14 +0000 Subject: [PATCH 2/5] Add comprehensive PRD and complete filesystem documentation - Create detailed Product Requirements Document with: - Executive summary and problem statement - Target user personas and use cases - Functional requirements (6 requirements with acceptance criteria) - Non-functional requirements (performance, compatibility, quality) - Constraints, assumptions, and out-of-scope items - Complete filesystem.rst with: - Component integration details (import flow, dependency graph) - Import pattern benefits and testing integration - Architecture evolution guidelines and future growth patterns - Migration guidance for API compatibility Co-Authored-By: Claude (Sonnet 4.5) --- documentation/architecture/filesystem.rst | 169 ++++++++++- documentation/prd.rst | 338 +++++++++++++++++++++- 2 files changed, 500 insertions(+), 7 deletions(-) diff --git a/documentation/architecture/filesystem.rst b/documentation/architecture/filesystem.rst index c9d0680..4284167 100644 --- a/documentation/architecture/filesystem.rst +++ b/documentation/architecture/filesystem.rst @@ -73,13 +73,174 @@ in the common architecture guide. Component Integration =============================================================================== +Module Import Flow +------------------------------------------------------------------------------- + +The package uses a layered import structure to maintain clean dependencies: + +.. code-block:: + + Package Initialization Flow: + 1. __/__init__.py imports from __/imports.py and __/nomina.py + 2. exceptions.py imports from __/ (gets common imports) + 3. objects.py imports from __/ and exceptions.py + 4. installers.py imports from __/ and objects.py + 5. __init__.py imports from all modules and creates public API + +Dependency Graph +------------------------------------------------------------------------------- + +The flat module structure maintains clear, acyclic dependencies: + +.. code-block:: + + __/imports.py ──┐ + ├──> __/__init__.py ──┐ + __/nomina.py ────┘ │ + ├──> exceptions.py ──┐ + │ │ + falsifier ─────────────────────────────┼────────────────────┼──> objects.py ──┐ + │ │ │ + │ │ ├──> installers.py + │ │ │ + └────────────────────┴──────────────────┴──> __init__.py + (public API) + +Import Pattern Benefits +------------------------------------------------------------------------------- + +**Centralized External Dependencies** + All external library imports (``classcore``, ``dynadoc``, ``typing_extensions``) + are centralized in ``__/imports.py``, making dependency management explicit + and version upgrades easier. + +**No Circular Dependencies** + The strict layering (``__/`` → ``exceptions`` → ``objects`` → ``installers`` + → ``__init__``) prevents circular import issues and makes the dependency + graph easy to understand. + +**Consistent Import Interface** + All modules use ``from . import __`` regardless of their position in the + package, providing a consistent and predictable pattern. + +Testing Integration +------------------------------------------------------------------------------- + +The test suite mirrors the source structure: + +.. code-block:: + + tests/ + ├── test_000_imports.py # Import validation and structure tests + ├── test_100_exceptions.py # Exception hierarchy tests + ├── test_200_objects.py # Sentinel factory and singleton tests + └── test_300_installers.py # Builtins integration tests + +Each test module corresponds to a source module and uses numbered functions +(e.g., ``test_201_absent_singleton_identity``) for systematic coverage tracking. + Architecture Evolution =============================================================================== -This filesystem organization provides a foundation that architect agents can -evolve as the project grows. For questions about organizational principles, -subpackage patterns, or testing strategies, refer to the comprehensive common -documentation: +Current Architecture Constraints +------------------------------------------------------------------------------- + +The package is intentionally kept simple with a flat module structure because: + +**Single Responsibility** + The package has one focused purpose: providing absence sentinels. This does + not require subpackage organization. + +**Minimal Dependencies** + Only four modules provide the complete feature set, making subpackages + unnecessary overhead. + +**API Stability** + A flat structure makes API stability easier to maintain and reduces the + chance of breaking changes from restructuring. + +Future Growth Patterns +------------------------------------------------------------------------------- + +If the package grows beyond its current scope, the following patterns would +apply: + +**Subpackage Creation Criteria** + Create a subpackage only when: + + - Five or more related modules share a common theme + - A feature requires internal implementation modules not part of public API + - Integration with external systems needs isolation (e.g., framework adapters) + +**Example: Framework Integrations Subpackage** + If framework-specific integrations are added: + + .. code-block:: + + sources/absence/ + ├── integrations/ # Subpackage for framework adapters + │ ├── __/ # Inherits from parent __/ + │ ├── __init__.py + │ ├── django.py # Django model field integration + │ ├── pydantic.py # Pydantic validator integration + │ └── sqlalchemy.py # SQLAlchemy type integration + ├── __/ + ├── objects.py + └── ... + + Each integration would follow the ``__`` import pattern and remain optional. + +**Example: Performance-Critical Subpackage** + If performance-critical implementations are needed: + + .. code-block:: + + sources/absence/ + ├── _speedups/ # Optional compiled extensions + │ ├── __init__.py + │ └── predicates.pyi # Type stubs for Rust/C implementations + ├── __/ + └── ... + + With fallback to pure Python implementations when extensions unavailable. + +Evolutionary Principles +------------------------------------------------------------------------------- + +**Maintain Backward Compatibility** + Public API at ``absence.*`` namespace must remain stable. Internal + reorganization should not affect user imports. + +**Resist Premature Optimization** + Keep the flat structure until concrete needs justify added complexity. + Each additional level of organization has cognitive cost. + +**Document Decisions** + Any structural changes should be documented via ADRs to preserve the + rationale for future maintainers. + +Migration Guidance +------------------------------------------------------------------------------- + +If subpackages are added, maintain public API compatibility: + +.. code-block:: python + + # sources/absence/__init__.py - maintain flat public API + from .core.objects import absent, is_absent, AbsenceFactory + from .core.installers import install + + # Users continue to import from top level + # from absence import absent + +This ensures existing user code continues to work while allowing internal +reorganization for maintainability. + +Reference Documentation +------------------------------------------------------------------------------- + +For questions about organizational principles, subpackage patterns, or testing +strategies, refer to the comprehensive common documentation: * `Architecture Patterns `_ * `Development Practices `_ diff --git a/documentation/prd.rst b/documentation/prd.rst index 85bf640..76af49f 100644 --- a/documentation/prd.rst +++ b/documentation/prd.rst @@ -21,7 +21,339 @@ Product Requirements Document ******************************************************************************* -.. todo:: Define product requirements, user stories, and acceptance criteria. +Executive Summary +=============================================================================== -For PRD format and guidance, see the `requirements documentation guide -`_. \ No newline at end of file +The ``absence`` package provides a type-safe, falsey sentinel singleton and +factory for representing absent values in Python code. It addresses the common +pattern where ``None`` is a valid value but developers need to distinguish +"value not provided" from "value provided as None". The package offers a global +``absent`` sentinel, factory for custom sentinels, type predicates, and type +alias support for static type checking. + +Problem Statement +=============================================================================== + +**Who experiences the problem:** + Python library developers, API designers, and application developers working + with optional parameters, partial updates, and configuration systems. + +**When and where it occurs:** + - Functions with optional parameters where ``None`` is a valid value + - Partial update operations (e.g., PATCH APIs, configuration updates) + - Query builders where absence means "don't filter" vs ``None`` means "filter for NULL" + - Default value scenarios where ``None`` has semantic meaning + +**Impact and consequences:** + - Developers resort to creating ad-hoc sentinel objects (``_MISSING = object()``) + - Inconsistent patterns across codebases and libraries + - Lack of type safety for sentinel detection + - Verbose code with repeated sentinel creation + - Potential bugs from truthy sentinel objects in boolean contexts + +**Current workarounds and limitations:** + +* ``object()`` sentinels: Truthy (confusing in conditionals), not standardized +* ``dataclasses.MISSING``: Truthy, dataclasses-specific, limited scope +* ``typing.NoDefault``: Truthy, typing-specific, Python 3.13+ +* PEP 661 sentinel: Not widely adopted, complex API, truthy by default +* Custom classes: Requires boilerplate in every project + +Goals and Objectives +=============================================================================== + +Primary Objectives +------------------------------------------------------------------------------- + +**Provide globally unique absence sentinel (REQ-001)** + Success metrics: + - Single ``absent`` instance per Python process + - Identity checks (``is absent``) work reliably + - Falsey in boolean contexts + +**Enable type-safe absence detection (REQ-002)** + Success metrics: + - Type checkers (Pyright, mypy) recognize ``Absential[T]`` type alias + - Type guards (``is_absent``, ``is_absence``) provide type narrowing + - Zero false positives in type checking + +**Support package-specific sentinels (REQ-003)** + Success metrics: + - Factory creates distinct sentinel instances + - Custom string representations supported + - Maintains falsey behavior and singleton semantics + +Secondary Objectives +------------------------------------------------------------------------------- + +**Minimal runtime overhead** + Success metrics: + - Sentinel checks compile to identity checks (``is`` operator) + - No performance penalty vs ``object()`` sentinels + - Import time under 50ms + +**Clear, discoverable API** + Success metrics: + - Complete type hints for all public symbols + - Comprehensive documentation with practical examples + - Common use cases covered in examples + +Target Users +=============================================================================== + +Python Library Developers +------------------------------------------------------------------------------- + +**Technical proficiency:** Advanced Python developers +**Primary needs:** + +- Standardized sentinel pattern for library APIs +- Type-safe sentinel detection for better IDE support +- Consistent behavior across different Python versions (3.10+) + +**Usage context:** Public library APIs with optional parameters where ``None`` +has semantic meaning distinct from "not provided" + +API Designers +------------------------------------------------------------------------------- + +**Technical proficiency:** Intermediate to advanced +**Primary needs:** + +- Clear distinction between "field not included" and "field set to null" +- Type annotations that express optional-but-distinct-from-None semantics +- Minimal cognitive overhead for API consumers + +**Usage context:** REST APIs, GraphQL resolvers, configuration systems, database +query builders + +Application Developers +------------------------------------------------------------------------------- + +**Technical proficiency:** Intermediate +**Primary needs:** + +- Simple predicate for checking absence +- Compatible with standard Python patterns (boolean checks, identity checks) +- Clear error messages for misuse + +**Usage context:** Business logic with partial updates, optional configurations, +conditional processing + +Functional Requirements +=============================================================================== + +Global Absence Sentinel (Critical) +------------------------------------------------------------------------------- + +**REQ-F001: Provide ``absent`` singleton** + Priority: Critical + + As a Python developer, I want a globally unique ``absent`` sentinel so that I + can use identity checks to detect absent values reliably. + + Acceptance Criteria: + + - ``absent`` is a module-level singleton + - ``absent is absent`` evaluates to ``True`` + - ``bool(absent)`` evaluates to ``False`` + - ``str(absent)`` returns ``'absent'`` + - ``repr(absent)`` returns ``'absence.absent'`` + - Multiple imports of ``absent`` reference the same object + +**REQ-F002: Prevent sentinel pickling** + Priority: Critical + + As a library developer, I want absence sentinels to be non-picklable so that + singleton semantics are guaranteed and bugs from unpickling are prevented. + + Acceptance Criteria: + + - ``pickle.dumps(absent)`` raises ``OperationValidityError`` + - Error message clearly explains why pickling is not supported + - Behavior is consistent across pickle protocols + +Type Safety (Critical) +------------------------------------------------------------------------------- + +**REQ-F003: Provide type predicate functions** + Priority: Critical + + As a Python developer, I want type-safe predicates for checking absence so + that type checkers can narrow types correctly. + + Acceptance Criteria: + + - ``is_absent(value)`` returns ``True`` only for the global ``absent`` singleton + - ``is_absence(value)`` returns ``True`` for any ``AbsenceFactory`` instance + - Both functions are ``TypeIs`` type guards for type checker support + - Functions work correctly with ``None``, ``False``, and other falsey values + +**REQ-F004: Provide ``Absential[T]`` type alias** + Priority: Critical + + As an API designer, I want a type alias for "value or absent" so that I can + express optional-but-not-None parameters in type signatures. + + Acceptance Criteria: + + - ``Absential[T]`` is equivalent to ``T | AbsentSingleton`` + - Type checkers recognize ``Absential[int]`` as accepting ``int`` or ``absent`` + - Type narrowing works after ``is_absent()`` checks + - Supports generic types (e.g., ``Absential[dict[str, Any]]``) + +Custom Sentinels (High) +------------------------------------------------------------------------------- + +**REQ-F005: Provide ``AbsenceFactory`` for custom sentinels** + Priority: High + + As a library developer, I want to create package-specific absence sentinels + so that I can avoid conflicts with the global sentinel in specialized contexts. + + Acceptance Criteria: + + - ``AbsenceFactory()`` creates new sentinel instance + - Custom ``__repr__`` and ``__str__`` functions supported via parameters + - All created sentinels are falsey (``bool(sentinel) == False``) + - Each factory instance is non-picklable + - ``is_absence(custom_sentinel)`` returns ``True`` + - ``is_absent(custom_sentinel)`` returns ``False`` + +Builtins Integration (Medium) +------------------------------------------------------------------------------- + +**REQ-F006: Provide ``install()`` function for builtins** + Priority: Medium + + As an application developer, I want to optionally install ``absent`` into + builtins so that I can use it without imports in frequently-used contexts. + + Acceptance Criteria: + + - ``install()`` with no arguments adds ``Absent`` and ``isabsent`` to builtins + - Custom names supported via ``sentinel_name`` and ``predicate_name`` parameters + - ``sentinel_name=None`` skips sentinel installation + - ``predicate_name=None`` skips predicate installation + - Multiple calls to ``install()`` with different names do not conflict + +Non-Functional Requirements +=============================================================================== + +Performance Requirements +------------------------------------------------------------------------------- + +**REQ-NF001: Minimal runtime overhead** + - Sentinel identity checks must compile to single ``is`` comparison + - Import time under 100ms on modern hardware + - Memory footprint under 10KB for core functionality + +**REQ-NF002: Zero cost abstraction for type checking** + - Type checking should not require runtime overhead + - ``Absential`` type alias has no runtime cost + +Compatibility Requirements +------------------------------------------------------------------------------- + +**REQ-NF003: Python version support** + - Support Python 3.10 and later + - Use ``typing_extensions`` for backports as needed + - All features work consistently across supported versions + +**REQ-NF004: Type checker compatibility** + - Full support for Pyright (latest version) + - Compatible with mypy strict mode + - Type hints pass validation without errors + +Quality Requirements +------------------------------------------------------------------------------- + +**REQ-NF005: Code quality standards** + - 100% test coverage for public API + - All public symbols have comprehensive docstrings + - Code passes Ruff linting with project configuration + - Code passes Pyright type checking in strict mode + +**REQ-NF006: API stability** + - Semantic versioning for all releases + - Deprecation warnings for API changes (minimum one minor version) + - No breaking changes in patch releases + +Documentation Requirements +------------------------------------------------------------------------------- + +**REQ-NF007: Comprehensive documentation** + - README with quick start and common examples + - Sphinx documentation with: + + - API reference for all public symbols + - Usage examples for each feature + - Comparison with alternatives + - Migration guides for common patterns + + - Architecture documentation (ADRs, system overview) + +Usability Requirements +------------------------------------------------------------------------------- + +**REQ-NF008: Developer experience** + - Clear, actionable error messages for misuse + - Consistent naming following Python conventions + - Discoverable via IDE autocomplete + - Examples cover 90% of common use cases + +Constraints and Assumptions +=============================================================================== + +Technical Constraints +------------------------------------------------------------------------------- + +- Depends on ``falsifier`` package for falsey base class +- Depends on ``classcore`` for standard object utilities +- Depends on ``dynadoc`` for documentation annotations +- Python 3.10+ required (for modern type hint syntax) + +Design Constraints +------------------------------------------------------------------------------- + +- Singleton semantics must be maintained across all code paths +- Falsey behavior is non-negotiable for the sentinel pattern +- Identity-based checks (``is``) are the only reliable detection method +- No pickle support due to singleton requirements (architectural decision) + +Assumptions +------------------------------------------------------------------------------- + +- Users understand the difference between ``None`` and "absent" +- Type checker usage is common in target user base +- Most use cases involve the global ``absent`` sentinel, not custom factories +- Performance of sentinel checks is critical (hot path usage) + +Out of Scope +=============================================================================== + +The following features are explicitly excluded from this product: + +**Pickle/serialization support** + Pickling absence sentinels would violate singleton semantics. Users must + handle absence before serialization boundaries. + +**Mutable sentinels** + Sentinels should be immutable to prevent confusion. While complete immutability + enforcement is a future enhancement, mutable sentinels are not supported. + +**Deep integration with standard library** + This package does not modify ``typing``, ``dataclasses``, or other standard + library modules. Integration is opt-in via imports. + +**Automatic None-to-absent conversion** + Users must explicitly use ``absent`` vs ``None``. Automatic conversion would + hide bugs and create confusion. + +**Sentinel comparison operations** + Sentinels do not support ``<``, ``>``, ``<=``, ``>=`` comparisons. Only + identity (``is``) and equality (``==``) checks are supported. + +**Multi-process/distributed uniqueness** + Singleton guarantees are per-process only. Distributed systems must handle + absence detection at serialization boundaries. \ No newline at end of file From f26682c797e2556b18881c4e4d6d833a25ae8389 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Nov 2025 04:30:38 +0000 Subject: [PATCH 3/5] Refine documentation based on feedback - Remove Future Growth Patterns section from filesystem.rst to keep focus on minimal dependency footprint - Add Preserve Small Footprint principle to Evolutionary Principles - Clarify PEP 661 status as deferred draft, not adopted into Python Co-Authored-By: Claude (Sonnet 4.5) --- documentation/architecture/filesystem.rst | 70 +++-------------------- documentation/prd.rst | 2 +- 2 files changed, 8 insertions(+), 64 deletions(-) diff --git a/documentation/architecture/filesystem.rst b/documentation/architecture/filesystem.rst index 4284167..75480b3 100644 --- a/documentation/architecture/filesystem.rst +++ b/documentation/architecture/filesystem.rst @@ -153,57 +153,13 @@ The package is intentionally kept simple with a flat module structure because: **Minimal Dependencies** Only four modules provide the complete feature set, making subpackages - unnecessary overhead. + unnecessary overhead. The package maintains a small dependency footprint + to remain lightweight and focused. **API Stability** A flat structure makes API stability easier to maintain and reduces the chance of breaking changes from restructuring. -Future Growth Patterns -------------------------------------------------------------------------------- - -If the package grows beyond its current scope, the following patterns would -apply: - -**Subpackage Creation Criteria** - Create a subpackage only when: - - - Five or more related modules share a common theme - - A feature requires internal implementation modules not part of public API - - Integration with external systems needs isolation (e.g., framework adapters) - -**Example: Framework Integrations Subpackage** - If framework-specific integrations are added: - - .. code-block:: - - sources/absence/ - ├── integrations/ # Subpackage for framework adapters - │ ├── __/ # Inherits from parent __/ - │ ├── __init__.py - │ ├── django.py # Django model field integration - │ ├── pydantic.py # Pydantic validator integration - │ └── sqlalchemy.py # SQLAlchemy type integration - ├── __/ - ├── objects.py - └── ... - - Each integration would follow the ``__`` import pattern and remain optional. - -**Example: Performance-Critical Subpackage** - If performance-critical implementations are needed: - - .. code-block:: - - sources/absence/ - ├── _speedups/ # Optional compiled extensions - │ ├── __init__.py - │ └── predicates.pyi # Type stubs for Rust/C implementations - ├── __/ - └── ... - - With fallback to pure Python implementations when extensions unavailable. - Evolutionary Principles ------------------------------------------------------------------------------- @@ -215,27 +171,15 @@ Evolutionary Principles Keep the flat structure until concrete needs justify added complexity. Each additional level of organization has cognitive cost. +**Preserve Small Footprint** + The package intentionally avoids integrations with large frameworks or + libraries to maintain minimal dependencies. Users can build their own + integrations as needed. + **Document Decisions** Any structural changes should be documented via ADRs to preserve the rationale for future maintainers. -Migration Guidance -------------------------------------------------------------------------------- - -If subpackages are added, maintain public API compatibility: - -.. code-block:: python - - # sources/absence/__init__.py - maintain flat public API - from .core.objects import absent, is_absent, AbsenceFactory - from .core.installers import install - - # Users continue to import from top level - # from absence import absent - -This ensures existing user code continues to work while allowing internal -reorganization for maintainability. - Reference Documentation ------------------------------------------------------------------------------- diff --git a/documentation/prd.rst b/documentation/prd.rst index 76af49f..caa777b 100644 --- a/documentation/prd.rst +++ b/documentation/prd.rst @@ -56,7 +56,7 @@ Problem Statement * ``object()`` sentinels: Truthy (confusing in conditionals), not standardized * ``dataclasses.MISSING``: Truthy, dataclasses-specific, limited scope * ``typing.NoDefault``: Truthy, typing-specific, Python 3.13+ -* PEP 661 sentinel: Not widely adopted, complex API, truthy by default +* PEP 661 sentinel: Still in draft with deferred status, not adopted into Python * Custom classes: Requires boilerplate in every project Goals and Objectives From 5523c76f74740caac28ce398c6f95ba1937241d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Nov 2025 05:00:33 +0000 Subject: [PATCH 4/5] Address documentation feedback - Update sources tree in filesystem.rst to show actual modules (exceptions.py, objects.py, installers.py) instead of placeholder - Move ADR 002 (non-picklable sentinels) to .auxiliary/notes/pickling.md as exploratory note since pickle support needs more investigation - Remove "As a , I want..." language from PRD functional requirements and state them more directly - Update ADR index to remove reference to ADR 002 Co-Authored-By: Claude (Sonnet 4.5) --- .auxiliary/notes/pickling.md | 90 ++++++++++++++ .../decisions/002-non-picklable-sentinels.rst | 110 ------------------ .../architecture/decisions/index.rst | 1 - documentation/architecture/filesystem.rst | 20 ++-- documentation/prd.rst | 24 ++-- 5 files changed, 113 insertions(+), 132 deletions(-) create mode 100644 .auxiliary/notes/pickling.md delete mode 100644 documentation/architecture/decisions/002-non-picklable-sentinels.rst diff --git a/.auxiliary/notes/pickling.md b/.auxiliary/notes/pickling.md new file mode 100644 index 0000000..a30c69e --- /dev/null +++ b/.auxiliary/notes/pickling.md @@ -0,0 +1,90 @@ +# Pickling Support for Absence Sentinels + +## Current Implementation + +Absence sentinels currently raise `OperationValidityError` when pickling is attempted via `__reduce__`. This was a pragmatic decision to avoid the complexity of implementing singleton-preserving pickle support without adequate time to think through the implications. + +## The Challenge + +Absence sentinels rely on identity checks (`is` operator) for correctness. The `is_absent()` predicate uses `absent is value` to determine if a value is the global absence sentinel. Singleton semantics are critical for this identity-based checking to work reliably. + +Python's `pickle` module serializes objects for storage or transmission and recreates them during unpickling. By default, unpickling creates new object instances, which breaks singleton semantics: + +- `pickle.loads(pickle.dumps(absent)) is absent` would be `False` +- Multiple "copies" of sentinels would exist after unpickling +- Identity-based checks would fail unpredictably + +## Options to Explore + +### 1. Implement Singleton-Preserving Pickle Support + +Custom `__reduce__` implementations can preserve singleton semantics during unpickling. This requires careful coordination but is achievable. + +**Considerations:** +- What should the pickle representation look like? +- How do we ensure the unpickler returns the same singleton instance? +- Does this work across process boundaries (e.g., multiprocessing)? +- What happens if someone unpickles in a process without the absence package? +- How do we handle custom `AbsenceFactory` instances (each is its own singleton)? + +**Possible approach:** +```python +def __reduce__(self): + # Return a callable that will return the singleton on unpickling + return (self.__class__, ()) +``` +But this still creates a new instance unless `__new__` returns the existing singleton. + +### 2. Keep Current Non-Picklable Behavior + +**Arguments for:** +- Absence sentinels represent transient "missing value" states in function calls +- Serializing absence sentinels may indicate design smell in calling code +- Failing explicitly is better than creating subtle identity bugs +- Forces users to handle absence before serialization boundaries +- Simpler implementation with fewer edge cases + +**Arguments against:** +- Users cannot serialize data structures containing absence sentinels +- Requires wrapping or transformation before serialization +- May be inconvenient for some use cases (e.g., caching function results) + +### 3. Allow Default Pickling (No Custom __reduce__) + +**Arguments for:** +- Simpler - let Python handle it +- Inherits behavior from `falsifier.Falsifier` base class +- Less code to maintain + +**Arguments against:** +- Breaks singleton semantics silently after unpickling +- Creates subtle bugs where `is` checks fail unexpectedly +- Violates principle of least surprise for sentinel objects +- Need to verify what `falsifier.Falsifier` actually does + +## Questions to Answer + +1. What are the actual use cases for pickling absence sentinels? +2. Can we examine how other singleton objects in Python handle pickling? + - `None` (how does it pickle?) + - `Ellipsis` + - `NotImplemented` + - `dataclasses.MISSING` + - `typing.NoDefault` +3. What does PEP 661 say about pickling sentinels? +4. How do similar libraries (e.g., `attrs`, `cattrs`) handle sentinel pickling? +5. Is there a real-world scenario where pickling an absence sentinel is the right design? + +## Next Steps + +- Research how Python's built-in singletons handle pickling +- Investigate PEP 661 recommendations +- Survey similar libraries for precedent +- Gather user feedback on whether pickle support is needed +- If implementing pickle support, write comprehensive tests for edge cases +- Consider whether to implement pickle support for `AbsenceFactory` instances + +## References + +- Current implementation: `sources/absence/objects.py:59-61` (`__reduce__` raises error) +- Exception: `sources/absence/exceptions.py:38-42` (`OperationValidityError`) diff --git a/documentation/architecture/decisions/002-non-picklable-sentinels.rst b/documentation/architecture/decisions/002-non-picklable-sentinels.rst deleted file mode 100644 index d76161e..0000000 --- a/documentation/architecture/decisions/002-non-picklable-sentinels.rst +++ /dev/null @@ -1,110 +0,0 @@ -.. vim: set fileencoding=utf-8: -.. -*- coding: utf-8 -*- -.. +--------------------------------------------------------------------------+ - | | - | Licensed under the Apache License, Version 2.0 (the "License"); | - | you may not use this file except in compliance with the License. | - | You may obtain a copy of the License at | - | | - | http://www.apache.org/licenses/LICENSE-2.0 | - | | - | Unless required by applicable law or agreed to in writing, software | - | distributed under the License is distributed on an "AS IS" BASIS, | - | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | - | See the License for the specific language governing permissions and | - | limitations under the License. | - | | - +--------------------------------------------------------------------------+ - - -******************************************************************************* -002. Non-Picklable Sentinels -******************************************************************************* - -Status -=============================================================================== - -Accepted - -Context -=============================================================================== - -Absence sentinels rely on identity checks (``is`` operator) for correctness. -The ``is_absent()`` predicate uses ``absent is value`` to determine if a value -is the global absence sentinel. Singleton semantics are critical for this -identity-based checking to work reliably. - -Python's ``pickle`` module serializes objects for storage or transmission and -recreates them during unpickling. By default, unpickling creates new object -instances, which breaks singleton semantics: - -- ``pickle.loads(pickle.dumps(absent)) is absent`` would be ``False`` -- Multiple "copies" of sentinels would exist after unpickling -- Identity-based checks would fail unpredictably - -While custom ``__reduce__`` implementations can preserve singleton semantics -during unpickling, this requires careful coordination and increases complexity. - -The fundamental question is whether absence sentinels should be serializable at -all, and if so, what guarantees should be provided. - -Decision -=============================================================================== - -Make absence sentinels explicitly non-picklable by implementing ``__reduce__`` -to raise ``OperationValidityError``. - -Alternatives -=============================================================================== - -**Implement singleton-preserving pickle support** - -Rejected because: - -- Absence sentinels represent transient "missing value" states in function calls -- Serializing absence sentinels indicates likely design smell in calling code -- Complexity of singleton preservation during unpickling outweighs benefits -- Better to fail explicitly than create subtle identity bugs -- Forces users to handle absence before serialization boundary - -**Allow default pickling behavior** - -Rejected because: - -- Breaks singleton semantics silently after unpickling -- Creates subtle bugs where ``is`` checks fail unexpectedly -- Users might not realize unpickling creates distinct instances -- Violates principle of least surprise for sentinel objects - -**Do nothing (inherit pickle behavior from base class)** - -Rejected because: - -- Unclear what ``falsifier.Falsifier`` pickle behavior is -- Leaves critical semantic guarantee to inherited implementation -- Should explicitly document this architectural constraint -- Making picklability explicit improves API clarity - -Consequences -=============================================================================== - -**Positive:** - -- Prevents subtle identity-based bugs from pickle/unpickle cycles -- Forces explicit handling of absence before serialization boundaries -- Clear error message when attempting invalid operation -- Maintains singleton semantic guarantees across all code paths -- Encourages better API design (resolve absence before serialization) - -**Negative:** - -- Cannot serialize data structures containing absence sentinels -- Users must explicitly handle absence before pickling -- Adds restriction that some users might find inconvenient -- Requires wrapping or transformation before serialization - -**Neutral:** - -- Aligns with common sentinel object patterns in Python ecosystem -- Consistent with ``Ellipsis`` and other singleton semantics -- Encourages treating absence as transient intermediate state diff --git a/documentation/architecture/decisions/index.rst b/documentation/architecture/decisions/index.rst index 0fafa88..804fc61 100644 --- a/documentation/architecture/decisions/index.rst +++ b/documentation/architecture/decisions/index.rst @@ -25,7 +25,6 @@ Architectural Decision Records :maxdepth: 2 001-falsey-behavior-via-inheritance - 002-non-picklable-sentinels 003-singleton-via-new-override For ADR format and guidance, see the `architecture documentation guide diff --git a/documentation/architecture/filesystem.rst b/documentation/architecture/filesystem.rst index 75480b3..af75952 100644 --- a/documentation/architecture/filesystem.rst +++ b/documentation/architecture/filesystem.rst @@ -57,15 +57,17 @@ The main Python package follows the standard ``sources/`` directory pattern: .. code-block:: sources/ - ├── absence/ # Main Python package - │ ├── __/ # Centralized import hub - │ │ ├── __init__.py # Re-exports core utilities - │ │ ├── imports.py # External library imports - │ │ └── nomina.py # python-absence-specific naming constants - │ ├── __init__.py # Package entry point - │ ├── py.typed # Type checking marker - │ └── [modules].py # Feature-specific modules - + └── absence/ # Main Python package + ├── __/ # Centralized import hub + │ ├── __init__.py # Re-exports core utilities + │ ├── imports.py # External library imports + │ └── nomina.py # Naming constants and type aliases + ├── __init__.py # Package entry point and public API + ├── exceptions.py # Exception hierarchy + ├── installers.py # Builtins integration + ├── objects.py # Sentinel factory and global singleton + └── py.typed # Type checking marker + All package modules use the standard ``__`` import pattern as documented in the common architecture guide. diff --git a/documentation/prd.rst b/documentation/prd.rst index caa777b..8e923b2 100644 --- a/documentation/prd.rst +++ b/documentation/prd.rst @@ -149,8 +149,8 @@ Global Absence Sentinel (Critical) **REQ-F001: Provide ``absent`` singleton** Priority: Critical - As a Python developer, I want a globally unique ``absent`` sentinel so that I - can use identity checks to detect absent values reliably. + The package must provide a globally unique ``absent`` sentinel to enable + reliable identity-based detection of absent values. Acceptance Criteria: @@ -164,8 +164,8 @@ Global Absence Sentinel (Critical) **REQ-F002: Prevent sentinel pickling** Priority: Critical - As a library developer, I want absence sentinels to be non-picklable so that - singleton semantics are guaranteed and bugs from unpickling are prevented. + Absence sentinels must be non-picklable to guarantee singleton semantics and + prevent bugs from unpickling. Acceptance Criteria: @@ -179,8 +179,8 @@ Type Safety (Critical) **REQ-F003: Provide type predicate functions** Priority: Critical - As a Python developer, I want type-safe predicates for checking absence so - that type checkers can narrow types correctly. + The package must provide type-safe predicates for checking absence that enable + type checkers to narrow types correctly. Acceptance Criteria: @@ -192,8 +192,8 @@ Type Safety (Critical) **REQ-F004: Provide ``Absential[T]`` type alias** Priority: Critical - As an API designer, I want a type alias for "value or absent" so that I can - express optional-but-not-None parameters in type signatures. + The package must provide a type alias for "value or absent" to enable + expressing optional-but-not-None parameters in type signatures. Acceptance Criteria: @@ -208,8 +208,8 @@ Custom Sentinels (High) **REQ-F005: Provide ``AbsenceFactory`` for custom sentinels** Priority: High - As a library developer, I want to create package-specific absence sentinels - so that I can avoid conflicts with the global sentinel in specialized contexts. + The package must provide an ``AbsenceFactory`` to enable creation of + package-specific absence sentinels for specialized contexts. Acceptance Criteria: @@ -226,8 +226,8 @@ Builtins Integration (Medium) **REQ-F006: Provide ``install()`` function for builtins** Priority: Medium - As an application developer, I want to optionally install ``absent`` into - builtins so that I can use it without imports in frequently-used contexts. + The package must provide an ``install()`` function to optionally add ``absent`` + to builtins for use without imports in frequently-used contexts. Acceptance Criteria: From fe8492933a7eb83aa85eccc462ac4a94b4c5bb6d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Nov 2025 05:28:30 +0000 Subject: [PATCH 5/5] Renumber ADR 003 to ADR 002 After removing the pickling ADR, renumber the singleton implementation ADR from 003 to 002 to maintain sequential numbering. Co-Authored-By: Claude (Sonnet 4.5) --- ...-via-new-override.rst => 002-singleton-via-new-override.rst} | 2 +- documentation/architecture/decisions/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename documentation/architecture/decisions/{003-singleton-via-new-override.rst => 002-singleton-via-new-override.rst} (99%) diff --git a/documentation/architecture/decisions/003-singleton-via-new-override.rst b/documentation/architecture/decisions/002-singleton-via-new-override.rst similarity index 99% rename from documentation/architecture/decisions/003-singleton-via-new-override.rst rename to documentation/architecture/decisions/002-singleton-via-new-override.rst index 4e70e7e..b1f8b6c 100644 --- a/documentation/architecture/decisions/003-singleton-via-new-override.rst +++ b/documentation/architecture/decisions/002-singleton-via-new-override.rst @@ -18,7 +18,7 @@ ******************************************************************************* -003. Singleton via __new__ Override +002. Singleton via __new__ Override ******************************************************************************* Status diff --git a/documentation/architecture/decisions/index.rst b/documentation/architecture/decisions/index.rst index 804fc61..47fb244 100644 --- a/documentation/architecture/decisions/index.rst +++ b/documentation/architecture/decisions/index.rst @@ -25,7 +25,7 @@ Architectural Decision Records :maxdepth: 2 001-falsey-behavior-via-inheritance - 003-singleton-via-new-override + 002-singleton-via-new-override For ADR format and guidance, see the `architecture documentation guide `_. \ No newline at end of file