Skip to content

Releases: Baseryn/zcore

v0.1.0-rc.3 🚀

v0.1.0-rc.3 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 21 Oct 11:29
69eea8a

Welcome to v0.1.0-rc.3 (Release Candidate 3) of the ZCore Framework! 🚀

As we continue advancing through the release candidate evaluation cycle toward the official v1.0.0 Stable GA, v0.1.0-rc.3 delivers significant architectural hardening, strict fail-fast lifecycle enforcement, enterprise database optimizations, and concurrency safety guarantees across the entire framework.

This release introduces native composite primary key support with PostgreSQL partition pruning; dynamic compound keyset pagination and SQLAlchemy Date coercion; a hardened keyword-mapped constructor dependency injection engine supporting Union / Optional type unwrapping; an automatic anti-shadowing hook system for subclassed routers; a dedicated, context-aware GET /_schema introspection endpoint replacing the legacy ?schema=true query parameter; thread-safe background cache eviction using double-checked locking; and strict mutually exclusive pagination validation.


🚀 Core Features & Architectural Advancements

1. 🧩 Native Composite Primary Keys & PostgreSQL Partition Pruning (BaseRepository)

Previously, BaseRepository assumed models possessed a single primary key column (primary_key[0]). In v0.1.0-rc.3, the data layer natively supports composite primary keys and PostgreSQL table partitioning (e.g., Range/List partitioning across (id, operation_date)):

  • Tuple-Based Primary Key Reflection: Replaced single-column attributes with self.pks = tuple(mapper.primary_key) and self.pk_names = tuple(col.name for col in self.pks). Automatically sets self.is_composite_pk = len(self.pks) > 1.
  • Fail-Fast Guarded Properties: Accessing convenience properties self.pk, self.pk_name, or self.cursor_field on a composite model without an explicit override immediately raises a descriptive AttributeError, preventing partial or corrupted queries.
  • PostgreSQL Partition Pruning (tuple_.in_): _build_ids_condition() dynamically constructs tuple_(*self.pks).in_(...) expressions for batch operations. This compiles to native SQL (col1, col2) IN ((val1, val2), ...), enabling the PostgreSQL query planner to perform Partition Pruning and eliminate full table scans across partitions.
  • Polymorphic Target Resolution (_build_pk_condition): All CRUD operations (get, exist, update, delete, restore) now accept composite targets as ORM model instances, mapping dictionaries ({"id": 1, "operation_date": d}), or exact-length tuples/lists ((1, d)), enforcing strict key completeness and rejecting invalid scalars.
  • Batch Operations (update_multi, delete_multi, restore_multi): Fully refactored to validate and assemble multi-column DBAPI payloads without ORM hydration overhead.
from datetime import date
from sqlalchemy import Date, String
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.ext.asyncio import AsyncSession
from zcore import Base, BaseRepository

# Models with composite primary keys (e.g., Partitioned Tables in SQLAlchemy 2.0)
class AuditLog(Base):
    __tablename__ = "audit_logs"

    id: Mapped[int] = mapped_column(primary_key=True)
    created_date: Mapped[date] = mapped_column(Date, primary_key=True)
    message: Mapped[str] = mapped_column(String)

class AuditRepository(BaseRepository[AuditLog]):
    def __init__(self, db: AsyncSession):
        super().__init__(AuditLog, db)

# Single lookup via composite tuple or dict
log = await repo.get(repo._build_pk_condition((101, date(2026, 10, 20))))

# Partition-pruned batch retrieval: (id, created_date) IN ((101, '2026-10-20'), ...)
logs = await repo.get_by_ids([(101, date(2026, 10, 20)), (102, date(2026, 10, 20))])

2. ⚡ Dynamic Keyset Pagination & PostgreSQL Date Type Coercion (CursorPagination)

Resolved sorting conflicts between CursorPagination and SearchRequest, and hardened cursor boundaries for strictly-typed SQL engines:

  • Runtime Sort Overrides: Extended CursorParams with optional cursor_field and order attributes. BaseRouter.search_endpoint and BaseRepository.search now automatically propagate the primary sort criteria (SearchRequest.sort[0]) directly into CursorPagination.

  • Compound Keyset Ordering: Keyset pagination now supports arbitrary non-PK sorting columns using compound keyset boundary evaluations:

    • DESC: (col < val) OR (col == val AND pk < last_pk)
    • ASC: (col > val) OR (col == val AND pk > last_pk)

    This completely eliminates data collision, record skipping, and duplication across sequential pages. Existing ordering clauses are reset via query.order_by(None) before keyset application.

  • SQLAlchemy Date Column Coercion: In SQLAlchemy, Date does not inherit from DateTime. Drivers like asyncpg previously raised DatatypeMismatchError: operator does not exist: date < character varying. CursorPagination now explicitly detects isinstance(col.type, Date), strips optional ISO time fragments via val.split("T")[0], and coerces boundaries using datetime.date.fromisoformat().

  • Extended Serialization: _encode_cursor natively serializes date, datetime, UUID, and Decimal types to strings.


3. 🎯 Keyword-Based Constructor Auto-Wiring & Default Fallbacks (IoCContainer)

Completely overhauled IoCContainer._auto_wire to improve reliability, support optional type signatures, and enforce lifecycle boundaries:

  • Unregistered Default Skipping: Constructor parameters defining default values (e.g., timeout: int = 30 or client: Any = None) whose types are not explicitly registered in the container are skipped during dependency collection, allowing Python to fall back safely to the declared default.
  • Registration Precedence: If a dependency type defines a default value but is explicitly registered in the container, the container-managed instance takes precedence and is injected.
  • Keyword Auto-Wiring (**kwargs): Auto-wiring now constructs class instances using keyword arguments (**resolved_kwargs) rather than positional arguments (*args). This eliminates parameter misplacement when optional/default parameters precede registered dependencies or when KEYWORD_ONLY parameters are used.
  • Type Unwrapping (_unwrap_type): Added recursive type normalization for Annotated, typing.Union, and Python 3.10+ types.UnionType (e.g., IService | None = None).
  • Cache Compatibility (_DependencySignature): Implemented a specialized list subclass (_DependencySignature) that preserves list-like backward compatibility for test assertions (assert cache[cls] == [...]) while internally mapping parameter names to dependency types (kwargs_map).
  • Strict Scope Boundaries: Resolving scoped dependencies or AsyncSession outside an active scope context (_current_scope_id is None) now immediately raises DIException.

4. 🛡️ Subclass Route Auto-Sorting & Anti-Shadowing Hooks (BaseRouter)

Eliminated route shadowing bugs where custom endpoints registered in BaseRouter subclasses were shadowed by dynamic parameterized paths (e.g., /{id:uuid} capturing requests intended for /summary or /reports):

  • Dynamic Sorting Hooks (_bind_router_sorting_hooks): Wraps self.router.add_api_route and self.router.add_route during initialization. Whenever a subclass registers a route (even inside __init__ after super().__init__()), routes are automatically re-sorted based on path specificity.
  • Subclass Lifecycle Hook (register_custom_routes): Added a declarative hook method invoked at the tail of _register_routes() allowing clean custom endpoint registration without overriding __init__.
  • Public Sorting Method: Added router.sort_routes() exposing on-demand specificity sorting.

5. 🔍 Dedicated Schema Introspection Endpoint (GET /_schema)

Replaced the legacy ?schema=true query parameter with a dedicated, standardized schema introspection endpoint:

  • Endpoint Mapping (RouteKey.SCHEMA): Scaffolds GET /_schema (customizable via schema_path, defaults to /_schema) ranked with high specificity to prevent path capture.
  • Action-Specific & Full Resource Introspection:
    • GET /_schema — Returns all available schemas for the resource (create, update, view, lookup).
    • GET /_schema?action=create|update|view|lookup — Returns the targeted JSON schema.
  • Security & Context Pruning: Dynamically evaluates schemas under active request context, pruning fields defined in ctx.restricted_fields or Zchema.__private__.
  • Cleaned Route Handler: Stripped inline schema generation and reflection logic from ZCoreAPIRoute, improving HTTP request execution throughput.
# Full resource introspection
curl -X GET "http://localhost:8000/api/v1/products/_schema"

# Action-specific introspection (sensitive fields pruned based on credentials)
curl -X GET "http://localhost:8000/api/v1/products/_schema?action=view" \
     -H "Authorization: Bearer <token>"

6. 🚦 Fail-Fast Pagination Validation (SearchRequest)

Enforced contract mutual exclusivity between offset pagination and keyset pagination:

  • Conflicting Parameter Rejection: SearchRequest now validates that cursor and page > 1 are never submitted simultaneously.
  • Strict 422 Response: If both are supplied, a ValueError is raised at the Pydantic validation boundary, returning a standard 422 RequestValidationError envelope. Standard single-page requests (page=1 with cursor) remain fully supported.

7. 🔒 Thread-Safe Cache Eviction Loop (_eviction_lock)

Resolved a multi-threaded race condition in zcore.cache.base._ensure_eviction_task:

  • Double-Checked Locking: Guarded background memory eviction loop instantiation with _eviction_lock = threading.Lock(). When multiple threads (e.g...
Read more

v0.1.0-rc.2 🚀

v0.1.0-rc.2 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 06 Oct 08:37
7c5fff7

Unified Exception Envelope, End-to-End Soft-Delete Lifecycle, Lightweight LOOKUP Projections, Inverted Search Operators, Re-entrant Unit of Work & Polymorphic Identifiers

Welcome to v0.1.0-rc.2 (Release Candidate 2) of the ZCore Framework! 🚀

As we approach the final validation phase before tagging our official v1.0.0 Stable GA, v0.1.0-rc.2 focuses on framework-wide standardization, operational robustness, and developer ergonomic refinements.

This release introduces an automated, normalized exception handling system conforming all HTTP errors to the ResponseWrapper envelope; implements an end-to-end soft-delete, restoration, and forced hard-delete lifecycle; scaffolds a high-performance, schema-projected /lookup endpoint; expands the dynamic AST search engine with negated operators and logical NOT grouping; establishes re-entrant, depth-aware Unit of Work coordination; generalizes user identifiers (int, str, UUID) across all subsystems; and introduces schema-level private field shielding for unauthenticated guests.


🚀 Core Features & Architectural Advancements

1. 🛡️ Unified Exception Handling & Error Normalization (ResponseWrapper)

Previously, standard FastAPI validation failures (422) and Starlette HTTP exceptions bypassed ZCore's standardized response structure. In v0.1.0-rc.2, all exceptions are normalized into a unified, consistent JSON envelope:

  • Pydantic Error Sanitization (_sanitize_error_item, _format_error_location): Converts raw Pydantic error loc tuples into clean, dot-separated field paths (e.g., body.email, query.limit) and safely serializes context payloads.
  • Unified Handlers:
    • request_validation_exception_handler: Formats 422 errors with clean field-level error lists and a human-readable summary.
    • http_exception_handler: Preserves original response headers while wrapping Starlette/FastAPI HTTPException instances.
    • response_validation_exception_handler: Intercepts internal schema mismatch errors (500) and displays field diagnostics only when settings.DEBUG is enabled.
    • unhandled_exception_handler: Catch-all fallback for uncaught runtime crashes with debug-gated stack traces.
  • Centralized Registration (register_exception_handlers): A single call register_exception_handlers(app) binds all handlers to FastAPI with granular feature toggles.
  • Lazy Module Packaging: Handlers are exported at the root package level via dynamic __getattr__ resolution to avoid circular import issues.

2. 🗑️ End-to-End Soft-Delete Lifecycle & Restoration (restore, force)

Soft deletion is now fully integrated through the Repository, Service, and Web layers:

  • Atomic Batch Soft-Delete: delete_multi() executes atomic UPDATE ... SET deleted_at=now() with SQL dialect-aware RETURNING support and pre-fetch fallbacks.
  • Single & Batch Restoration: Added restore(target) and restore_multi(ids) to WriteRepositoryMixin and WriteServiceMixin to recover soft-deleted records (deleted_at=None).
  • Forced Physical Hard Deletes: Added force: bool = False across repository, service execution orchestrators, and lifecycle hooks (pre_delete, post_delete, pre_delete_multi, post_delete_multi).
  • Web Router Query Flag: Auto-scaffolded DELETE endpoints accept ?force=true (e.g., DELETE /tasks/{id}?force=true) to permanently purge records when requested.

3. 🔍 Lightweight LOOKUP Endpoint & Schema-Driven Projections (POST /lookup)

For autocomplete selectors, dropdown lists, and relational references where fetching full database entities is wasteful:

  • Automatic Schema Projection (_resolve_lookup_projections): Introspects lookup_schema against SQLAlchemy model metadata to selectively generate load_only(*columns) statements, always including primary keys.
  • Automatic Eager-Loader Configuration: Detects relationships declared in lookup_schema and attaches optimal loader strategies (selectinload for collections, joinedload for scalar relations).
  • Strict Whitelist Protection (allowed_lookup_fields): Client queries attempting to filter or sort on unauthorized fields are immediately rejected with a 400 ValidationError.
  • RBAC Integration: Mapped to Actions.LOOKUP (model.actions().LOOKUP) for granular permission checks.

4. ⚡ Search Engine Negation & Logical NOT Grouping (SearchEngine)

The dynamic JSON search engine now supports inverted filter operations:

  • Inverted Operators: Added not_like, not_ilike, not_contains, not_startswith, not_endswith, not_in, not_between, and is_not_null.
  • SQL Expression Wrapping: _compare_column detects not_ prefixes and wraps clauses in SQLAlchemy's not_() expression.
  • Logical NOT Blocks: Evaluates nested filter groups under op="not" as not_(and_(*sub_exprs)) or single-field inversions.
  • Null Semantics: is_null and is_not_null now accept boolean flags (True/False) or None values consistently.

5. 🔗 Re-entrant Unit of Work & Deferred Domain Events (UnitOfWork)

To coordinate multi-service business transactions across complex modular monolith architectures:

  • Depth-Aware Re-entrancy (uow_depth): Tracks execution nesting depth in session.info['uow_depth']. Nested UnitOfWork blocks execute safe intermediate session.flush() calls, delegating the physical session.commit() solely to the outermost root boundary.
  • Buffered Post-Commit Events: Domain events registered via uow.register_event() across nested scopes accumulate in session.info['uow_events'] and dispatch atomically only after the root transaction commits.
  • Global Rollback Safeguard: Any unhandled exception resets depth to 0, clears pending events, and executes an immediate session rollback.

6. 🆔 Polymorphic User Identifiers (int, str, UUID)

Removed strict uuid.UUID type enforcement from core identity layers:

  • Polymorphic ZContext & UserProtocol: ZContext.user_id and UserProtocol.id now accept Any | None (supporting integer IDs, UUIDs, Auth0/Firebase string IDs, or custom tenant keys).
  • Real-time Streaming (StreamManager): Subscriber mapping queues (users_queues) now accept arbitrary user ID types with mixed-type string comparison routing.
  • Cursor Pagination PK Inspection: Keyset cursor pagination dynamically inspects column types (mapper.primary_key[0].type.python_type), coercing cursor payload keys to int or UUID automatically.

7. 🔒 Optional Authentication & Schema-Level Private Fields (Zchema.__private__)

Enables clean guest-friendly routes and sensitive field filtering:

  • Optional Authentication (BaseAuth(auto_error=False)): Allows endpoints to accept optional authentication without raising 401 Unauthorized on missing or invalid tokens.
  • Dependency Anchor (get_optional_user_stub): Exported across zcore and zcore.security for optional route parameters.
  • Private Field Pruning (__private__): Schemas can declare sensitive fields via __private__: ClassVar[set[str]] = {"cost_price", "notes"}. ZCore automatically prunes these fields from response payloads, input validations, and generated JSON schemas (?schema=true) when no authenticated user is present.

8. ⚙️ Tunable Framework Settings & Request Boundary Clamping

Replaced magic numbers across subsystems with environment-driven settings in Settings:

  • Subsystem Tuning: Added PAGINATION_DEFAULT_SIZE (20), PAGINATION_MAX_SIZE (100), SEARCH_MAX_DEPTH (3), CACHE_LOCAL_MAXSIZE (1000), CACHE_DEFAULT_TTL (3600), CACHE_EVICTION_INTERVAL (60), STREAM_QUEUE_MAXSIZE (100), and AUTH_CACHE_TTL (300).
  • Dynamic Parameter Validation: Added Pydantic @model_validator across PageNumberParams, CursorParams, and SearchRequest to clamp requested page sizes within configured minimum and maximum boundaries.

9. 🧪 Advanced Testing Harness & Sandboxing Engine (zcore.testing)

Comprehensive upgrades to make automated testing deterministic and isolated:

  • Database Table Lifecycle (setup_test_database): Drop and recreate test database tables across sync fixtures or running event loops via ThreadPoolExecutor safely.
  • Event Sandboxing (EventDispatcherSandbox): Snapshots and restores registered event subscribers across test runs, eliminating listener pollution.
  • Multi-Dependency Overrides: DatabaseRollback and ZTestClient support custom engine injections, multiple authentication stub overrides, and strict Pydantic user model validation (user_model).

10. 🪵 Non-Intrusive Logging & Context Isolation

Refined logging signal-to-noise ratio across development and production modes:

  • Status-Code-Aware Exception Logging: Application and HTTP exceptions log at DEBUG for client errors (< 500) and ERROR for server failures (>= 500).
  • Independent Slow-Query Interceptor: Slow queries exceeding slow_query_threshold_ms are logged even if general log_sql_queries is disabled.
  • Developer Console Mode: Development mode preserves native Uvicorn console formatting via passthrough_loggers, switching cleanly to ProcessorFormatter JSON rendering in production.
  • Background Scope Context: background_scope isolates Structlog contextvars, binds task_id=scope_id, and restores parent context on exit.

11. 🖥️ Cascading CLI Server Configuration & Dynamic Scaffolding

Upgraded the zc command-line tool:

  • Cascading Precedence (zc run): Server parameters resolve with the precedence: CLI Arguments > .env File > Defaults.
  • Transparent Uvicorn Passthrough: Uses parse_known_args() to forward arbitrary flags (e.g., --root-path, --proxy-headers) directly to Uvicorn.
  • Multi-Worker Safety: Automatically d...
Read more

v0.1.0-rc.1 🚀

v0.1.0-rc.1 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 18 Sep 17:00
85964b5

Dynamic Primary Keys, Weighted Route Specificity, Enterprise Logging, Dialect-Aware Fallbacks & Hardened Storage Sandbox

Welcome to v0.1.0-rc.1 (Release Candidate 1) of the ZCore Framework! 🚀

As we officially transition from the Beta phase toward the v1.0.0 Stable GA, v0.1.0-rc.1 marks a major milestone in framework stability, security hardening, and production readiness.

This release delivers dynamic primary key detection across REST endpoints, introduces an anti-shadowing weighted route sorting algorithm, optimizes ORM mutations to accept model instances directly (bypassing duplicate database fetches), adds dialect-aware batch fallbacks for non-RETURNING databases (such as MySQL), integrates a centralized IANA timezone subsystem (ZDateTime), redesigns the structured logging pipeline via ProcessorFormatter, completely hardens the file storage layer with strict sandboxing and active XSS/executable scanning, and achieves complete unit and integration test coverage across all subsystems.


🚀 Core Features & Architectural Advancements

1. 🎯 Dynamic Primary Key Resolution & Generalized Routing (BaseRouter)

Previously, BaseRouter assumed model primary keys were strictly UUIDs. In v0.1.0-rc.1, routing infrastructure dynamically inspects ORM metadata:

  • Automatic PK Type Detection (_resolve_pk_type): Automatically resolves whether the primary key is an int, uuid.UUID, or other scalar type via SQLAlchemy column inspection.
  • Dynamic Path Mapping (_get_pk_path): Generates type-safe URL converter paths (e.g., /{id:int} for integer PKs, /{id:uuid} for UUIDs, or /{id} as a fallback).
  • Explicit PK Override: Models or routers can explicitly declare pk_type = int to override dynamic reflection.
  • Streamlined Endpoint Signatures: Cleaned up internal wrapper signatures by removing unused **kwargs and enforcing explicit parameter typing.

2. ⚖️ Weighted Route Specificity Sorting (Anti-Path Shadowing)

A notorious issue in REST frameworks is Path Shadowing—where dynamic parameterized routes accidentally capture requests intended for static endpoints (e.g., GET /{id} shadowing POST /search).

  • Multi-Tier Weighted Scoring (_sort_routes): Replaced basic string sorting with an advanced scoring tuple:
    (has_dynamic, total_dynamic, segment_scores, -len(segments))
  • Strict Route Precedence: Guarantees that static segment routes (/search) are always matched before single dynamic parameters (/{id}) and catch-all path parameters (/{path:path}), regardless of declaration order.

3. ⚡ Optimized Instance-Aware ORM Mutations (update, delete, update_multi)

To eliminate redundant database lookups when an entity is already loaded in memory:

  • Direct Model Acceptance: BaseRepository.update() and BaseRepository.delete() now accept target: ModelType | Any. If a model instance is passed directly, the preliminary SELECT query is completely skipped.
  • Instance-Keyed Batch Updates: update_multi now accepts dictionaries keyed by either model instances or primary key scalars (dict[ModelType | Any, BaseModel]), resolving the PK seamlessly via getattr(key, self.pk_name, key).
  • Aligned Service Hooks: Updated pre_update, on_update, pre_delete, and on_delete in BaseService to accept target: ModelType | Any.

4. 🔄 Cross-Dialect Batch Compatibility (Dialect-Aware Fallbacks)

Enables seamless database portability across SQL engines that lack native RETURNING support:

  • Dialect-Aware create_multi: Inspects the engine's insert_returning capability. If supported (PostgreSQL, modern SQLite), it executes insert().returning(). Otherwise (MySQL, older SQLite), it falls back gracefully to db.add_all() and db.flush().
  • Dialect-Aware delete_multi: Inspects delete_returning. If unsupported, it fetches records via get_by_ids() prior to atomic deletion, guaranteeing deleted instances are returned intact.

5. ⏱️ Centralized Timezone Management & ZDateTime Subsystem

Introduced zcore.utils.timezone for robust, timezone-aware datetime manipulation:

  • IANA ZoneInfo Resolution (get_app_timezone): Resolves timezones with LRU caching and falls back safely to UTC if an invalid timezone is provided.
  • ZDateTime Type Alias: Custom type alias utilizing Pydantic's PlainSerializer to automatically format datetimes as timezone-aware ISO 8601 strings with offsets during JSON serialization.
  • Timezone-Aware Helpers: Added now(), utc_now(), to_app_timezone(), and format_iso_with_app_timezone().
  • Soft Delete & JSON Sync: SoftDeleteMixin and CustomJSONEncoder now natively utilize timezone-aware timestamps.
  • Windows Support: Added tzdata>=2024.1 for guaranteed IANA timezone database resolution on Windows environments.

6. 🪵 Enterprise Structured Logging (LoggingSettings & ProcessorFormatter)

Redesigned the logging pipeline to seamlessly unify structlog and Python's standard logging library:

  • ProcessorFormatter Integration: Eliminates duplicate log emissions and bridges third-party loggers (uvicorn, sqlalchemy.engine) into a unified structlog pipeline.
  • LoggingSettings Model: Configured via Settings.LOGGING with backward-compatible bidirectional synchronization to legacy LOG_LEVEL.
  • Slow Query Interceptor: Added slow_query_threshold_ms to silently filter out fast queries and log only statements exceeding latency thresholds.
  • Built-in File Rotation & DictConfig: Added RotatingFileHandler support (10MB, 5 backups) via file_path setting and support for full logging.config.dictConfig overrides.
  • Enhanced Telemetry: RequestLogMiddleware now captures real HTTP status codes from http.response.start and client IPs.

7. 📦 Sandboxed Local Storage & Advanced Threat Validation

Expanded zcore.storage into an enterprise-grade, sandboxed asset management subsystem:

  • Path Traversal Defense: LocalStorageProvider incorporates _extract_key, _key_to_path, and _resolve_path to strictly bound all read/write/delete operations within base_path.
  • URL Resolution & Existence Check: Added get_url(file_path_or_url) and exists(file_path_or_url) to the StorageProvider base interface.
  • Executable & XSS Threat Detection (SafeMimeTypeValidator):
    • Increased sample inspection buffer from 2KB to 8KB.
    • Rejects binary executable headers (MZ for .exe/.dll, #!/ for shell scripts, \x7fELF for Linux binaries).
    • Scans file payloads for Stored XSS and active script injections (<script>, <?php, javascript:, onload=, onerror=, <iframe>, etc.).

8. 🔍 Search Engine Hardening & Operator Unification

  • Consolidated Text Operators: Unified contains into the ilike operator family with automatic SQL wildcard escaping (%, _).
  • Non-String Column Text Search: Automatically casts non-string database columns (e.g., UUIDs, integers) via cast(col, String) when text operators (ilike, startswith, endswith, contains) are applied.
  • Expanded Type Coercion: Supports set collections alongside list and tuple for in operators, and provides clear, column-aware ValidationError messages when coercion fails.

9. 🗄️ Native Engine JSON Serialization & Guaranteed Lifespan Teardown

  • Custom JSON Column Serializer: DatabaseManager passes json_dumps and json_loads directly to create_async_engine, enabling native persistence of UUID, Decimal, date, and datetime types in database JSON/JSONB columns.
  • Guaranteed Shutdown Teardown: Kernel.lifespan automatically executes await close_cache() and await db_manager.close() in its finally block, ensuring clean connection pool disposal upon application shutdown.
  • Event-Loop Safe Cache Eviction: Background cache eviction task in BaseCache lazily schedules sweeps only when an active event loop is running, preventing RuntimeError during CLI/import time.

⚠️ Breaking Changes & Migration Guide

1. get_list Positional Parameter Ordering

In BaseRepository and BaseService, variadic filter expressions (*criterion) have been moved to the beginning of the parameter list to match standard Python conventions and prevent positional shifting.

Old:

# Ambiguous positional parameter placement
items = await repo.get_list(pagination_params, fields_list, User.is_active == True)

New:

# Positional criterion first, optional controls via keyword arguments
items = await repo.get_list(
    User.is_active == True,
    pagination=pagination_params,
    fields=fields_list
)

2. Passing Model Instances to update() and delete()

You can now pass either the primary key or the instantiated model directly to service.update() and service.delete().

Old:

user = await user_service.get(id=user_id)
# Required passing ID, triggering an unnecessary second SELECT query
updated = await user_service.update(user.id, user_update_schema)

New:

user = await user_service.get(id=user_id)
# Direct instance update (zero redundant SELECT queries)
updated = await user_service.update(user, user_update_schema)

3. Structured Database Configuration

db_manager.init_app now accepts structured DatabaseSettings objects directly.

Old:

db_manager.init_app(
    db_url=settings.DATABASE_URL,
    pool_size=settings.POOL_SIZE,
    max_overflow=settings.MAX_OVERFLOW,
    echo=settings.DEBUG
)

New:

# Pass structured settings directly
db_manager.init_app(config=settings.DATABASE)

4. Storage Upload Method Returns Web URL

StorageProvider.upload() and `upload_stream...

Read more

v0.1.0-beta.8 🚀

v0.1.0-beta.8 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 28 Aug 12:36
efa4be9

Interactive TUI Engine, Bulk Mutation Optimizations, Isolated Background Tasks & Built-in Soft Delete

Welcome to v0.1.0-beta.8 of the ZCore Framework! 🚀

As we approach our milestone v1.0.0 stable release, v0.1.0-beta.8 delivers major performance breakthroughs in relational data mutations, introduces an enterprise-grade isolated background task execution pipeline with automatic dependency injection, embeds native soft-delete capabilities, significantly expands dynamic search operators, completely redesigns the developer experience with a rich, interactive Terminal User Interface (TUI), and aligns all CLI scaffolding templates with the latest single-generic architecture.


🚀 Core Features & Architectural Advancements

1. ⚡ High-Performance Bulk DBAPI Mutations (create_multi, update_multi, delete_multi)

We completely revamped the bulk data modification pipelines in BaseRepository to bypass individual ORM hydration overhead and leverage native database acceleration:

  • create_multi via INSERT ... RETURNING: Batch insertions now execute in a single roundtrip using atomic INSERT ... RETURNING clauses, eliminating individual entity flushes while returning fully hydrated model instances.
  • update_multi via DBAPI executemany: Batch updates now bypass pre-fetching and hydrating ORM entities in memory. Updates are executed directly at the DBAPI level with compiled parameterized payloads, followed by a single consolidated batch query fetch.
  • delete_multi via DELETE ... RETURNING: Bulk deletions are executed in a single atomic SQL statement without prior entity fetching, returning all deleted records cleanly.

2. ⏱️ Isolated Background Tasks & Scope Management (background_scope, @background_task)

Running background jobs in FastAPI that access request-scoped dependencies or database sessions often triggers RuntimeError: Session is closed. ZCore resolves this at the architectural layer:

  • background_scope Async Context Manager: Provisions an independent IoC scope, allocates a fresh, dedicated AsyncSession from db_manager, and safely clones the active user context (inherit_context=True) for background execution.
  • @background_task Decorator: Automatically wraps any async coroutine or synchronous function (executed in a non-blocking threadpool via anyio.to_thread) inside background_scope().
  • Automatic IoC Auto-Wiring: Any type-annotated parameter omitted during invocation is automatically resolved directly from the IoC container.

3. 🗑️ Built-in Soft Deletion Infrastructure (SoftDeleteMixin)

Soft deletion is now an out-of-the-box framework primitive rather than a manual scoping pattern:

  • SoftDeleteMixin: Add timestamp-based soft deletion to any model with class Task(Base, SoftDeleteMixin):.
  • Automatic Query Scoping: Injects an indexed, timezone-aware deleted_at: Mapped[datetime | None] column and automatically filters all repository operations (get(), get_list(), count(), exist(), search()) with WHERE deleted_at IS NULL.
  • Lifecycle Helpers: Provides clean task.soft_delete(), task.restore(), and task.is_deleted properties.

4. 🔍 Advanced Dynamic Search Operators & Configurable Depth (SearchEngine)

The dynamic query engine has been significantly expanded with powerful string-matching and range operators:

  • contains: Performs substring matching on text columns (with automatic wildcard sanitization) and collection containment.
  • startswith & endswith: String prefix and suffix matching with automated SQL wildcard escaping.
  • between: Range evaluations for numerical, date, and timestamp columns accepting inclusive [min, max] boundaries with automatic type coercion.
  • Configurable __max_search_depth__: Filter and relation inclusion (include) depth limits can now be customized per model via the __max_search_depth__ attribute (defaulting safely to 3 to defend against query DoS attacks).

5. 🛡️ Scope-Aware Existence Verifications (exist)

  • BaseRepository.exist() now strictly evaluates and enforces model-level scope_query and soft-delete filters before checking record existence via limit(1), preventing cross-tenant or deleted entity enumeration.

6. 🖥️ Interactive TUI & Scaffolding Engine (zc)

The zc CLI has been completely rewritten into an interactive Terminal User Interface powered by Rich and Questionary:

  • Context-Aware Dashboard: Running zc automatically detects whether you are in an active project or a workspace with multiple microservices.
  • Multi-Database Bootstrapping: zc init interactively configures SQLite (aiosqlite), PostgreSQL (asyncpg), or MySQL (aiomysql) with pre-configured .env and requirements.txt files.
  • Automated Virtual Environment Setup: Automatically initializes isolated .venv environments and installs dependencies using ultra-fast uv or standard pip.
  • Granular 7-Layer Scaffolding: zc startapp supports Full Boilerplate, Clean/Blank, or Custom Layer Selection across Models, Schemas, Repositories, Services, Routers, Plugins, and Pytest suites.
  • Automatic Secret Key Generation: zc init automatically provisions fresh 64-character cryptographic SECRET_KEY strings into .env.

🐛 Bug Fixes & Refinements

7. 🛠️ CLI Scaffolding Template Alignment

  • Fixed BaseRepository & BaseService Boilerplate: Corrected scaffolding templates in zcore.cli.templates (REPOSITORY_TEMPLATE and SERVICE_TEMPLATE) to use the streamlined single-generic signature (BaseRepository[Model] and BaseService[Model]) introduced in v0.1.0-beta.7, removing obsolete schema generic parameters and redundant imports.
  • AST Compilation Test Guard: Added explicit AST compilation testing to the test suite (test_scaffolded_templates_are_valid_python) to guarantee all generated boilerplates are 100% valid, error-free Python code.

⚠️ Breaking Changes & Migration Guide

1. Simplified Bulk Mutation Return Signatures

Because create_multi, update_multi, and delete_multi now utilize database-level RETURNING clauses and direct DBAPI execution, manual refresh=True loops are no longer needed.

Old:

# Iteratively flushed and refreshed entities in python
created_items = await repo.create_multi(schemas, refresh=True)

New:

# Instant atomic database RETURNING execution
created_items = await repo.create_multi(schemas)

2. Upgrading Soft Deletion to SoftDeleteMixin

If you previously wrote manual WHERE is_deleted == False clauses in scope_query, you can now inherit directly from SoftDeleteMixin.

Old:

class Task(Base):
    __tablename__ = "tasks"
    is_deleted: Mapped[bool] = mapped_column(default=False)

    @classmethod
    def scope_query(cls, query: Select) -> Select:
        return query.where(cls.is_deleted.is_(False))

New:

from zcore import Base, SoftDeleteMixin

class Task(Base, SoftDeleteMixin):
    __tablename__ = "tasks"
    # Automatically provides deleted_at, is_deleted, soft_delete(), restore(), and scope_query

3. Background Task Execution

Replace manual session allocations inside background routines with @background_task.

Old:

# Fragile: risks closed session errors after request terminates
async def process_task(task_id: uuid.UUID):
    session = container.resolve(AsyncSession)
    await session.execute(...)

New:

from zcore.kernel import background_task
from zcore import Inject

@background_task
async def process_task(task_id: uuid.UUID, service: Inject[TaskService]):
    # Automatically runs in an isolated IoC scope & independent AsyncSession
    await service.process(task_id)

🛠 How to Install / Upgrade

To install or upgrade to v0.1.0-beta.8:

pip install --upgrade fastapi-zcore-framework[all]

📖 Interactive Documentation: https://baseryn.github.io/zcore-docs/

v0.1.0-beta.7 🚀

v0.1.0-beta.7 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 16 Aug 15:11
49b2d22

Core Architecture Overhaul, Testing Subsystem & Unified Security

Welcome to v0.1.0-beta.7 of the ZCore Framework! 🚀

This is one of the most substantial and transformative releases in ZCore's history. Guided by our foundational philosophy of Engineered Simplicity (KISS) and Clean Architecture, v0.1.0-beta.7 eliminates generic boilerplate across data layers, introduces a complete asynchronous testing framework with transaction rollback guarantees, unifies request-scoped context state and security primitives, and introduces event-driven subscriber mechanics.


🚀 Core Features & Architectural Advancements

1. 🧪 Comprehensive Async Testing Infrastructure (zcore.testing)

Writing integration and unit tests for database-backed FastAPI services is now effortless and boilerplate-free:

  • ZTestFixture Engine: Modular async fixtures including ContainerSandbox (snapshots and restores IoC state), DatabaseRollback (wraps tests in isolated savepoint transactions), UserContext (injects user identities and security scopes), and AppLifespan (manages FastAPI lifecycle hooks).
  • ZTestClient: An asynchronous test client extending httpx.AsyncClient that coordinates test sandboxes with zero manual mock setups.
  • BaseZTest: An abstract class providing clean, class-based test suites using an intuitive async with self.run(use_db=True) context manager.
  • CLI Test Generator: Run zc startapp <domain> --test to scaffold ready-to-run test files automatically.

2. 🌐 Centralized Request Context & Scopes (ZContext & ctx)

Replaced fragmented context functions with a unified, high-performance, contextvar-isolated ZContext class:

  • Global ctx Singleton: Access context variables anywhere in the request lifecycle via strongly-typed properties (ctx.user_id, ctx.restricted_fields, ctx.action).
  • ctx.scope() Context Manager: Create isolated, temporary context overrides for background tasks, cron jobs, or test sandboxing.
  • Action-Aware Routing: Route operations (create, view, update, delete) are automatically detected and injected into ZContext, driving action-scoped field pruning in Zchema.

3. 🧹 Streamlined Data Layer Generics & Dynamic Hook Propagation

We eliminated the verbose schema generic clutter from data access layers:

  • Single Generic Parameter: BaseRepository[Model] and BaseService[Model] now only require the database model type.
  • Dynamic Criterion & Query Filters: Repository get(), exist(), and get_list() now accept arbitrary positional SQLAlchemy binary expressions (*criterion) and keyword arguments (**filters) consolidated via _apply_filters.
  • Dynamic Pre-Hook Data Injection: Service pre_create and pre_update lifecycle hooks can now return a dictionary (Optional[dict[str, Any]]) that automatically merges context-derived data (e.g., user_id from ctx) directly into repository database flushes.

4. 🔐 Consolidated Security & Generic Authentication (BaseAuth)

  • Unified Security Class: Merged scattered hashing and JWT logic into a centralized Security coordinator supporting Argon2id password hashing, PyJWT token encoding/decoding, and cryptographic token generation.
  • Template Method BaseAuth: A generic authentication dependency that extracts and validates tokens, integrates with BaseCache for user-lookup caching, and automatically binds the resolved user to ZContext.
  • Dynamic Scope Resolution: HasScopes permission dependencies now evaluate scopes dynamically from ZContext (ctx.get("scopes")) with automatic type coercion for lists, tuples, and sets.

5. ⚡ Modern Constructor Auto-Wiring (Inject[T])

  • Converted Inject into a subscriptable type marker (Inject[T]), which seamlessly maps to Annotated[T, Depends(Injector(T))].
  • Allows clean, idiomatic constructor type-hinting in services and routes without importing or declaring raw FastAPI Depends() chains.

6. 📡 Event-Driven Architecture (@on_event) & SQL Diagnostics

  • @on_event Decorator: Mark service methods as event subscribers with automatic container-backed resolution via EventDispatcher.register_listeners().
  • SQL Execution Telemetry: Built-in connection interceptors log SQL execution durations in milliseconds and filter out Postgres system catalog query noise.
  • Static Route Protection: Added _sort_routes() to ensure parameterized paths (e.g., /{id:uuid}) never shadow sibling static endpoints like /search.

🛠️ CLI & Subsystem Enhancements

  • zc genenv: Introspects registered Pydantic Settings classes and scaffolds a clean .env.example file automatically.
  • Settings Simplification: Renamed ZCoreCoreSettings to Settings and replaced ENVIRONMENT strings with boolean DEBUG flags.
  • ZCoreJSONResponse: Replaces standard JSON responses to safely serialize complex types (UUIDs, ISO datetimes, Decimals).
  • Standalone Documentation Portal: Decoupled documentation from the core repository into the official standalone repository Baseryn/zcore-docs.

⚠️ Breaking Changes & Migration Guide

1. Repository and Service Generic Declarations

You no longer need to pass schema types to BaseRepository or BaseService.

Old:

class TaskRepo(BaseRepository[Task, TaskCreate, TaskUpdate]):
    ...

class TaskService(BaseService[Task, TaskCreate, TaskUpdate]):
    ...

New:

class TaskRepo(BaseRepository[Task]):
    ...

class TaskService(BaseService[Task]):
    ...

2. Base Settings & Debug Configuration

The settings class name has been simplified, and ENVIRONMENT is now DEBUG.

Old:

from zcore import ZCoreCoreSettings

class AppSettings(ZCoreCoreSettings):
    ENVIRONMENT: str = "development"

New:

from zcore import Settings

class AppSettings(Settings):
    DEBUG: bool = True

3. Dependency Injection Syntax

Update route handlers and constructors to use Inject[T] type annotations.

Old:

@router.post("/tasks")
async def create_task(service: TaskService = Inject(TaskService)):
    ...

New:

from zcore import Inject

@router.post("/tasks")
async def create_task(service: Inject[TaskService]):
    ...

4. Consolidated Security Utilities

Imports from zcore.security.hashing and zcore.security.jwt are merged under Security.

Old:

from zcore.security.hashing import hash_password, verify_password
from zcore.security.jwt import create_access_token

New:

from zcore import Security

hashed = Security.hash_password(raw_password)
token = Security.create_jwt({"sub": str(user_id)})

🛠 How to Install / Upgrade

To install or upgrade to the latest beta:

pip install --upgrade fastapi-zcore-framework

📖 Interactive Documentation: https://baseryn.github.io/zcore-docs/

v0.1.0-beta.6 🚀

v0.1.0-beta.6 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 16 Jul 10:11
0f4dfbf

Welcome to v0.1.0-beta.6 of the ZCore Framework! 🚀

This release represents a significant step forward in robust data handling, security validation, and comprehensive project governance. Following our core philosophy of Engineered Simplicity (KISS), we have addressed critical edge-cases in timezone serialization, refined schema semantics to align with Domain-Driven Design (DDD), and overhauled our repository's testing and automation pipelines to provide an out-of-the-box, production-ready developer experience (DX).


🚀 Core Architectural Refinements

🌐 Decoupled Presentation Layer Domain (__model__)

In Zchema (zcore/web/projection.py), we have officially deprecated the class attribute __db_name__ in favor of a more semantic and architecturally accurate attribute: __model__.

  • The Rationale: Schemas (DTOs) belong purely to the presentation layer and should not be tightly coupled to database nomenclature by name.
  • The Alignment: All internal recursion, pruning logics, and CLI code-generation templates have been fully migrated to use __model__ for strict domain binding.

🔌 Lazy-Loaded Root Namespace Imports

Developers can now import highly utilized core framework constructs directly from the root namespace:

from zcore import RouteKey, Plugin
  • Previously, this forced developers to write long, internal import paths.
  • We implemented dynamic, lazy-loaded import resolutions within src/zcore/__init__.py using Python's dynamic __getattr__ block, ensuring no performance or startup overhead during initialization.

🐛 Data Integrity & Security Fixes

🔢 Precision-Preserving Decimal Serialization

Changed JSON encoding for Decimal instances from standard floating-point (float) to string (str) inside the CustomJSONEncoder.

  • The Fix: Converting Decimals to floats introduces floating-point arithmetic inaccuracies and precision loss right before data is dispatched to clients.
  • The Result: Preserves exact decimal precision, establishing the industry standard for financial calculations and highly accurate APIs.

🗺️ Timezone-Aware Pagination Cursors

Refactored the _encode_cursor method inside zcore/db/pagination.py to intercept naive datetime instances.

  • Naive datetimes are now explicitly normalized to UTC, and timezone-aware instances are converted safely to UTC before being base64 encoded into cursor payloads.
  • Resolves cursor misalignment and timezone-awareness dropouts in geographically distributed deployments.

🛡️ Robust Security Path Normalization

Eliminated overzealous string prefix stripping in path resolutions inside SearchEngine and Zchema.

  • Previously, path.replace("resource.", "") acted globally and would corrupt legitimate nested relation paths (e.g., project.resource.status incorrectly mutated to project.status).
  • Replaced with a strict, localized prefix-matching resolution to secure nested relationships without false positives during access checks.

⏱️ Native ISO-8601 Datetime Parsing

Simplified UTC datetime coercion in zcore/db/search.py by removing legacy .replace("Z", "+00:00") string manipulation workarounds. The coercion layer now fully relies on native, optimized Python 3.11+ datetime.fromisoformat() UTC suffix parsing out-of-the-box.


⚙️ Automated CI/CD & Project Governance

To prepare ZCore for robust open-source contributions and strict production deployments, we have fully automated our repository workflows:

  1. GitHub Actions Test Suite (CI): Deployed automated pull-request code quality gates using ruff check linting and the hatch test suite (blocking merges unless all status checks are green).
  2. Standardized Issue Templates: Added customized Bug Report and Feature Request templates to capture environmental diagnostics (DB engines, SQLAlchemy, and Redis versions).
  3. Automated Semantic Changelogs: Configured .github/release.yml to automatically categorize pull requests under semantic sections based on metadata tags.
  4. Community Guidelines: Introduced formal CONTRIBUTING.md and SECURITY.md files to clarify local setup steps and private vulnerability reporting workflows.

v0.1.0-Beta.5 🚀

v0.1.0-Beta.5 🚀 Pre-release
Pre-release

Choose a tag to compare

@alialfostovar alialfostovar released this 13 Jul 15:07
7f50572

Welcome to v0.1.0-beta.5 of the ZCore Framework! This release represents a significant architectural evolution, centering on Engineered Simplicity (KISS). We have deprecated complex, reflection-based configuration variables and brittle runtime hacks, replacing them with standard Object-Oriented patterns, native Pydantic V2 integrations, and cleaner dependency structures.


🚀 Core Features & Enhancements

🛡️ Unified Schema Security (Zchema)

We have completely overhaulled our data pruning and shielding mechanisms. Previously, data masking was handled globally in the web layer via a generic resource.* prefix, which was prone to namespace collisions.

  • The Zchema Class: All schemas now inherit from Zchema (extending BaseModel).
  • Domain-Specific Namespacing: You now define a class-level __db_name__ attribute (e.g., __db_name__ = "user").
  • 3-Tier Protection: Zchema handles security natively within Pydantic boundaries:
    1. Dynamic Schemas (?schema=true): Automatically prunes restricted fields from the exposed JSON Schema.
    2. Input Filtering: Automatically strips restricted fields from incoming payloads, defending against Mass Assignment.
    3. Response Pruning: Automatically masks fields during serialization.

🚦 Template Method Service Hooks

To guarantee that business-critical pre_ and post_ lifecycle hooks and transaction boundaries are never skipped, we separated orchestration from execution.

  • Custom Database Operations: Developers now override dedicated on_create, on_update, and on_delete callback hooks instead of overriding the public create() orchestrator.
  • The orchestrator guarantees that validation, hooks, and transaction commits (_safe_commit) are executed in the exact correct order.

🔗 FastAPI-Native Route Dependencies

We have conceptually renamed the routing security layer from "Permissions" to "Dependencies", matching FastAPI's design.

  • Removed 8 static class variables (DEFAULT_PERMISSIONS, POST_PERMISSIONS, etc.).
  • Introduced a single, elegant factory method: get_route_dependencies(self, route_key: RouteKey, action: str) -> list[Any].
  • Developers can now use standard Python inheritance and super() to customize specific endpoints while maintaining default scopes for others.

📦 Robust Modular Monolith Imports

Resolved ModuleNotFoundError across nested modules (such as importing from shared/ within modules/identity/) using a 3-layer approach:

  1. CLI: Auto-injects the root into PYTHONPATH during zc run.
  2. Runtime: Bootstraps sys.path dynamically at the top of main.py.
  3. IDE: Generates PYTHONPATH=. in .env to eliminate warning lines in VSCode/PyCharm.

⚠️ Breaking Changes & Migration Guide

1. Schema Base Class Update

You must replace BaseModel with Zchema and define __db_name__ on your schemas.

  • Old:
    from pydantic import BaseModel
    class UserResponse(BaseModel):
        email: str
  • New:
    from zcore import Zchema
    class UserResponse(Zchema):
        __db_name__ = "user"
        email: str

2. Router Security Configuration

Static permission variables have been deprecated. Move your custom security logic to the new factory method.

  • Old:
    class ProductRouter(BaseRouter):
        DEFAULT_PERMISSIONS = "AUTO"
        DELETE_PERMISSIONS = [AdminOnly()]
  • New:
    class ProductRouter(BaseRouter):
        def get_route_dependencies(self, route_key: RouteKey, action: str):
            if route_key == RouteKey.DELETE:
                return [AdminOnly()]
            return super().get_route_dependencies(route_key, action)

3. Service Overrides

If you previously overrode create, update, or delete directly, move that database logic to the corresponding on_ hook.

  • Old:
    async def create(self, schema: ProductCreate):
        return await self.repository.create(schema)
  • New:
    async def on_create(self, schema: ProductCreate) -> Product:
        return await self.repository.create(schema)

🐛 Bug Fixes & Refactoring

  • Fixed ModuleNotFoundError in subfolders of modular directory layouts (#53).
  • Corrected IoCContainer resolution for Annotated type hints and manual request-scope instance registration (#48).
  • Simplified ZCoreJSONResponse and removed ResponseProjector to avoid redundant serialization overhead.

v0.1.0-beta.4 - DI Overhaul and Architecture Refinement

Choose a tag to compare

@alialfostovar alialfostovar released this 07 Jul 14:58
0463115

ZCore Framework — v0.1.0-beta.4

We are pleased to announce the release of v0.1.0-beta.4. This version introduces critical architectural refinements, improves ASGI/Async performance by eliminating thread-pool bottlenecks, and simplifies the developer experience (DX) for database-backed microservices.


🚀 Key Improvements & Features

1. Complete DI Overhaul & Web-Decoupled Scoping

The dependency injection (DI) layer has been restructured to strictly adhere to Clean Architecture:

  • ASGI Middleware Session Scoping: The lifetime of the AsyncSession database connection is now managed at the ASGI middleware level (ScopedDependencyMiddleware). This guarantees that a request-scoped database session is initialized and registered in the DI container before FastAPI resolves any routing dependency graphs, preventing race conditions.
  • Annotated Type Unpacking: The custom DI container (_auto_wire) now supports python's native typing.Annotated patterns, gracefully extracting the core class definition (e.g., resolving AsyncSession from Annotated[AsyncSession, ...]).
  • High-Performance Async Injector: Modified Injector.__call__ to be asynchronous (async def). This allows FastAPI to evaluate dependencies directly on the main event loop, bypassing costly anyio.to_thread.run_sync context switches.

2. Base Query Hook & Extension Layer

Introduced structured hook methods in the base repository layer to allow granular control over queries:

  • _get_base_query(): A reusable hook returning the default Select statement (defaults to select(self.model)). Custom repositories can override this to pre-select columns or include defaults.
  • _extend_query(): Enables developers to dynamically extend queries (e.g., chaining default criteria or global filters).
  • Integrated base query pipeline seamlessly with the SearchEngine dynamic filter compiler.

3. One-Command Database Bootstrapping

  • Running zc init <project_name> through the ZCore CLI now automatically touches and creates the default SQLite database file (zcore_dev.db) on the filesystem, ensuring a zero-configuration developer onboarding experience.

4. Dependency Management

  • Added uvicorn as a primary core dependency inside pyproject.toml to guarantee the ZCore orchestration server commands (zc run) function correctly out of the box in all environments.

⚠️ Breaking Changes & Migration Guide

If you are migrating your test projects or showcase apps from beta.3 to beta.4, please review the following changes:

1. Clean Constructor Parameters (Decoupling Web Defaults)

You no longer need to write = Inject(...) default values or rely on SessionDep in repository/service constructors.

Before (v0.1.0-beta.3):

class UsersRepository(BaseRepository):
    def __init__(self, db: SessionDep):
        super().__init__(model=Users, db=db)

class UsersService(BaseService):
    def __init__(self, repository: UsersRepository = Inject(UsersRepository)):
        super().__init__(model=Users, repository=repository)

After (v0.1.0-beta.4):

class UsersRepository(BaseRepository):
    def __init__(self, db: AsyncSession):  # Standard AsyncSession
        super().__init__(model=Users, db=db)

class UsersService(BaseService):
    def __init__(self, repository: UsersRepository):  # Pure Python, no defaults
        super().__init__(model=Users, repository=repository)

2. Using Inject over Depends in Custom Web Deps

When writing custom FastAPI dependencies (e.g., get_current_user), ensure you use Inject(YourService) instead of Depends(YourService) so that the container handles recursive resolution:

async def get_current_user(
    token: str = Depends(oauth2_scheme),
    service: UsersService = Inject(UsersService)  # Bypasses FastAPI default-resolver errors
) -> Users:
    ...

🛠 How to Install / Upgrade

To fetch the latest beta package:

pip install --upgrade fastapi-zcore-framework