registro


Nameregistro JSON
Version 0.2.2 PyPI version JSON
download
home_pageNone
SummaryResource-based model system for SQLModel with automatic Resource creation and validation
upload_time2025-10-13 20:54:04
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords models pydantic resources sqlalchemy sqlmodel
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Registro

[![PyPI version](https://badge.fury.io/py/registro.svg)](https://badge.fury.io/py/registro)
[![Python Versions](https://img.shields.io/pypi/pyversions/registro.svg)](https://pypi.org/project/registro/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Overview

Registro is a resource management framework for Python applications that provides structured resource identification, validation, and lifecycle management. Built on SQLModel, it offers a consistent approach to defining, creating, and interacting with resources across applications and services.

## Key Features

- **Resource Identifiers (RIDs)**: Globally unique, structured identifiers (`{prefix}.{service}.{instance}.{resource_type}.{id}`) for all resources.
- **Configurable Validation:** Customize regex patterns and reserved words for RIDs and API names via settings or environment variables.
- **Dual Implementation Approaches**: Support for both decorator-based (`@resource`) and inheritance-based (`ResourceTypeBaseModel`) implementation.
- **SQLModel Integration**: Seamless integration with SQLModel and SQLAlchemy for database operations.
- **Validation**: Field and pattern validation through Pydantic using the configured rules.
- **Type Safety**: Comprehensive type hints for improved IDE support and runtime type checking.
- **Status Management**: Built-in resource status tracking with customizable status values.
- **Relationship Handling**: Tools for defining and navigating resource relationships.
- **Metadata Management**: Automatic tracking of creation, update, and lifecycle events.
- **Relationship Helpers**: Simplified resource linking and relationship management.
- **Enhanced Serialization**: Comprehensive `to_dict()` method with relationship support.

## Installation

### Standard Installation

```bash
pip install registro
```

### With Rye (Recommended)

```bash
rye add registro
```

## Configuration

Registro allows customization of default behaviors, validation patterns, and reserved words through the `registro.config.settings` object or environment variables. Configuration should typically be done **before** defining or importing your resource models.

**Precedence:** Environment variables override programmatic settings, which override library defaults.

**Configurable Items:**

1.  **RID Prefix:**
    *   `settings.RID_PREFIX = "myprefix"`
    *   `export REGISTRO_RID_PREFIX="myprefix"` (Default: "ri")
2.  **Default Service & Instance:**
    *   `settings.DEFAULT_SERVICE = "my-service"`
    *   `settings.DEFAULT_INSTANCE = "staging"`
    *   `export REGISTRO_DEFAULT_SERVICE="my-service"`
    *   `export REGISTRO_DEFAULT_INSTANCE="staging"` (Defaults: "default", "prod")
3.  **Reserved Words:**
    *   `settings.RESERVED_WORDS = {"internal", "system", "config"}`
    *   `export REGISTRO_RESERVED_WORDS="internal,system,config"` (See `models/patterns.py` for defaults)
4.  **Validation Patterns:** Define the regex used for validating RID components and API names.
    *   `settings.set_pattern("SERVICE", r"^[a-z]{3,10}$")` # Override service pattern
    *   `settings.set_pattern("API_NAME_OBJECT_TYPE", r"^[A-Z][a-zA-Z]*$")` # Override object type API name pattern
    *   `export REGISTRO_PATTERN_SERVICE="^[a-z]{3,10}$"`
    *   `export REGISTRO_PATTERN_API_NAME_OBJECT_TYPE="^[A-Z][a-zA-Z]*$"`
    *   *Pattern Names (used in `set_pattern` and env vars):* `RID_PREFIX`, `SERVICE`, `INSTANCE`, `TYPE`, `LOCATOR`, `API_NAME_OBJECT_TYPE`, `API_NAME_LINK_TYPE`, `API_NAME_ACTION_TYPE`, `API_NAME_QUERY_TYPE`. (See `config/settings.py` for defaults).
5.  **API Name Pattern Mapping:** Map specific `resource_type` strings to the *name* of the pattern used for their `api_name` validation.
    *   ```python
        settings.API_NAME_PATTERNS_BY_TYPE = {
            "object-type": "API_NAME_OBJECT_TYPE", # Default mapping
            "link-type": "API_NAME_LINK_TYPE",   # Default mapping
            "my-custom-type": "MY_CUSTOM_PATTERN_NAME", # Custom mapping
            "default": "API_NAME_ACTION_TYPE" # Fallback pattern name
        }
        # Ensure "MY_CUSTOM_PATTERN_NAME" is also set via settings.set_pattern()
        settings.set_pattern("MY_CUSTOM_PATTERN_NAME", r"^[a-z_]+$")
        ```
    *   `export REGISTRO_API_NAME_MAPPING='{"my-custom-type": "MY_CUSTOM_PATTERN_NAME"}'` (Overrides are merged with defaults).

## Usage

Registro offers two implementation approaches: inheritance-based and decorator-based.

### Inheritance Approach

Extend `ResourceTypeBaseModel` for explicit control and reliability when running scripts directly:

```python
from registro import ResourceTypeBaseModel
from sqlmodel import Field, Session, SQLModel, create_engine

class Book(ResourceTypeBaseModel, table=True):
    __resource_type__ = "book" # Used in RID generation & API name validation mapping

    # Define fields
    title: str = Field()
    author: str = Field()

    # Optionally specify service and instance in constructor (overrides settings defaults)
    def __init__(self, **data):
        # You can pass service and instance directly to constructor
        super().__init__(**data) # Handles setting _service and _instance
```

### Decorator Approach

Use the `@resource` decorator for a cleaner, more concise syntax:

```python
from registro import resource
from sqlmodel import Field

@resource(
    resource_type="book", # Explicitly set resource type
    service="library",    # Optional: Override settings.DEFAULT_SERVICE
    instance="main"       # Optional: Override settings.DEFAULT_INSTANCE
)
class Book:
    title: str = Field(...)
    author: str = Field(...)
```

## Relationship Management

ResourceTypeBaseModel provides powerful relationship management tools:

```python
from typing import List, Optional
from registro import ResourceTypeBaseModel
from sqlmodel import Field, Relationship, Session

# Define models with relationships
class Author(ResourceTypeBaseModel, table=True):
    __resource_type__ = "author"
    name: str = Field()
    books: List["Book"] = Relationship(back_populates="author")

class Book(ResourceTypeBaseModel, table=True):
    __resource_type__ = "book"
    title: str = Field()
    author_rid: Optional[str] = Field(default=None, foreign_key="author.rid", index=True)
    author_api_name: Optional[str] = Field(default=None, index=True) # Optional, for convenience
    author: Optional[Author] = Relationship(back_populates="books")

    def link_author(self, session: Session, author: Optional[Author] = None,
                   author_rid: Optional[str] = None, author_api_name: Optional[str] = None) -> Author:
        """Link to author using enhanced link_resource helper."""
        # Example: Link by API name
        return self.link_resource(
            session=session,
            resource=author, # Optional: pass fetched resource directly
            model_class=Author,
            rid_field="author_rid", # Field on Book to store Author's RID
            api_name_field="author_api_name", # Field on Book to store Author's API name
            rid_value=author_rid, # Optional: find author by RID
            api_name_value=author_api_name # Optional: find author by API name
        )
```

## Examples

See the `examples/` directory for complete working examples:

- **Basic Usage**: Simple resource creation and querying using both approaches.
- **Custom Resources**: Advanced resource types with relationships, custom base classes, and custom status values.
- **Integration Example**: Demonstrates using Registro with FastAPI.

## Advanced Features

### Resource Relationships

ResourceTypeBaseModel includes helper methods for relationship management:

```python
# Find related resources by RID or API name
related_author = book.get_related_resource(
    Author,
    api_name="john-doe",
    session=session
)

# Link resources with a single method call
author = book.link_author(session=session, author_api_name="jane-roe")
```

### Data Serialization

Enhanced serialization with the `to_dict()` method, including RID components:

```python
# Get a dictionary representation of a resource
book_dict = book.to_dict()
print(book_dict["rid"])         # Full Resource ID (e.g., ri.library.main.book.ulid)
print(book_dict["service"])     # Service name (e.g., 'library')
print(book_dict["instance"])    # Instance name (e.g., 'main')
print(book_dict["resource_type"])# Resource type (e.g., 'book')
print(book_dict["resource_id"]) # ULID locator part
print(book_dict["title"])       # Model field
```

### Field Validation

Utility methods for common validation tasks:

```python
from pydantic import field_validator

class Department(ResourceTypeBaseModel, table=True):
    # ...
    code: str = Field()

    @field_validator("code")
    @classmethod
    def validate_code(cls, v):
        # Use built-in identifier validation
        return cls.validate_identifier(v, "Department code")

# In another model or logic:
# Validate relationships
# employee.validate_related_field_match(department, "status", "ACTIVE")
```
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "registro",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "models, pydantic, resources, sqlalchemy, sqlmodel",
    "author": null,
    "author_email": "Kevin Saltarelli <kevinqz@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/06/57/e63153b7eff3f649bbaf0c179a09c18419943ab0928a5e9d40c841d11654/registro-0.2.2.tar.gz",
    "platform": null,
    "description": "# Registro\n\n[![PyPI version](https://badge.fury.io/py/registro.svg)](https://badge.fury.io/py/registro)\n[![Python Versions](https://img.shields.io/pypi/pyversions/registro.svg)](https://pypi.org/project/registro/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Overview\n\nRegistro is a resource management framework for Python applications that provides structured resource identification, validation, and lifecycle management. Built on SQLModel, it offers a consistent approach to defining, creating, and interacting with resources across applications and services.\n\n## Key Features\n\n- **Resource Identifiers (RIDs)**: Globally unique, structured identifiers (`{prefix}.{service}.{instance}.{resource_type}.{id}`) for all resources.\n- **Configurable Validation:** Customize regex patterns and reserved words for RIDs and API names via settings or environment variables.\n- **Dual Implementation Approaches**: Support for both decorator-based (`@resource`) and inheritance-based (`ResourceTypeBaseModel`) implementation.\n- **SQLModel Integration**: Seamless integration with SQLModel and SQLAlchemy for database operations.\n- **Validation**: Field and pattern validation through Pydantic using the configured rules.\n- **Type Safety**: Comprehensive type hints for improved IDE support and runtime type checking.\n- **Status Management**: Built-in resource status tracking with customizable status values.\n- **Relationship Handling**: Tools for defining and navigating resource relationships.\n- **Metadata Management**: Automatic tracking of creation, update, and lifecycle events.\n- **Relationship Helpers**: Simplified resource linking and relationship management.\n- **Enhanced Serialization**: Comprehensive `to_dict()` method with relationship support.\n\n## Installation\n\n### Standard Installation\n\n```bash\npip install registro\n```\n\n### With Rye (Recommended)\n\n```bash\nrye add registro\n```\n\n## Configuration\n\nRegistro allows customization of default behaviors, validation patterns, and reserved words through the `registro.config.settings` object or environment variables. Configuration should typically be done **before** defining or importing your resource models.\n\n**Precedence:** Environment variables override programmatic settings, which override library defaults.\n\n**Configurable Items:**\n\n1.  **RID Prefix:**\n    *   `settings.RID_PREFIX = \"myprefix\"`\n    *   `export REGISTRO_RID_PREFIX=\"myprefix\"` (Default: \"ri\")\n2.  **Default Service & Instance:**\n    *   `settings.DEFAULT_SERVICE = \"my-service\"`\n    *   `settings.DEFAULT_INSTANCE = \"staging\"`\n    *   `export REGISTRO_DEFAULT_SERVICE=\"my-service\"`\n    *   `export REGISTRO_DEFAULT_INSTANCE=\"staging\"` (Defaults: \"default\", \"prod\")\n3.  **Reserved Words:**\n    *   `settings.RESERVED_WORDS = {\"internal\", \"system\", \"config\"}`\n    *   `export REGISTRO_RESERVED_WORDS=\"internal,system,config\"` (See `models/patterns.py` for defaults)\n4.  **Validation Patterns:** Define the regex used for validating RID components and API names.\n    *   `settings.set_pattern(\"SERVICE\", r\"^[a-z]{3,10}$\")` # Override service pattern\n    *   `settings.set_pattern(\"API_NAME_OBJECT_TYPE\", r\"^[A-Z][a-zA-Z]*$\")` # Override object type API name pattern\n    *   `export REGISTRO_PATTERN_SERVICE=\"^[a-z]{3,10}$\"`\n    *   `export REGISTRO_PATTERN_API_NAME_OBJECT_TYPE=\"^[A-Z][a-zA-Z]*$\"`\n    *   *Pattern Names (used in `set_pattern` and env vars):* `RID_PREFIX`, `SERVICE`, `INSTANCE`, `TYPE`, `LOCATOR`, `API_NAME_OBJECT_TYPE`, `API_NAME_LINK_TYPE`, `API_NAME_ACTION_TYPE`, `API_NAME_QUERY_TYPE`. (See `config/settings.py` for defaults).\n5.  **API Name Pattern Mapping:** Map specific `resource_type` strings to the *name* of the pattern used for their `api_name` validation.\n    *   ```python\n        settings.API_NAME_PATTERNS_BY_TYPE = {\n            \"object-type\": \"API_NAME_OBJECT_TYPE\", # Default mapping\n            \"link-type\": \"API_NAME_LINK_TYPE\",   # Default mapping\n            \"my-custom-type\": \"MY_CUSTOM_PATTERN_NAME\", # Custom mapping\n            \"default\": \"API_NAME_ACTION_TYPE\" # Fallback pattern name\n        }\n        # Ensure \"MY_CUSTOM_PATTERN_NAME\" is also set via settings.set_pattern()\n        settings.set_pattern(\"MY_CUSTOM_PATTERN_NAME\", r\"^[a-z_]+$\")\n        ```\n    *   `export REGISTRO_API_NAME_MAPPING='{\"my-custom-type\": \"MY_CUSTOM_PATTERN_NAME\"}'` (Overrides are merged with defaults).\n\n## Usage\n\nRegistro offers two implementation approaches: inheritance-based and decorator-based.\n\n### Inheritance Approach\n\nExtend `ResourceTypeBaseModel` for explicit control and reliability when running scripts directly:\n\n```python\nfrom registro import ResourceTypeBaseModel\nfrom sqlmodel import Field, Session, SQLModel, create_engine\n\nclass Book(ResourceTypeBaseModel, table=True):\n    __resource_type__ = \"book\" # Used in RID generation & API name validation mapping\n\n    # Define fields\n    title: str = Field()\n    author: str = Field()\n\n    # Optionally specify service and instance in constructor (overrides settings defaults)\n    def __init__(self, **data):\n        # You can pass service and instance directly to constructor\n        super().__init__(**data) # Handles setting _service and _instance\n```\n\n### Decorator Approach\n\nUse the `@resource` decorator for a cleaner, more concise syntax:\n\n```python\nfrom registro import resource\nfrom sqlmodel import Field\n\n@resource(\n    resource_type=\"book\", # Explicitly set resource type\n    service=\"library\",    # Optional: Override settings.DEFAULT_SERVICE\n    instance=\"main\"       # Optional: Override settings.DEFAULT_INSTANCE\n)\nclass Book:\n    title: str = Field(...)\n    author: str = Field(...)\n```\n\n## Relationship Management\n\nResourceTypeBaseModel provides powerful relationship management tools:\n\n```python\nfrom typing import List, Optional\nfrom registro import ResourceTypeBaseModel\nfrom sqlmodel import Field, Relationship, Session\n\n# Define models with relationships\nclass Author(ResourceTypeBaseModel, table=True):\n    __resource_type__ = \"author\"\n    name: str = Field()\n    books: List[\"Book\"] = Relationship(back_populates=\"author\")\n\nclass Book(ResourceTypeBaseModel, table=True):\n    __resource_type__ = \"book\"\n    title: str = Field()\n    author_rid: Optional[str] = Field(default=None, foreign_key=\"author.rid\", index=True)\n    author_api_name: Optional[str] = Field(default=None, index=True) # Optional, for convenience\n    author: Optional[Author] = Relationship(back_populates=\"books\")\n\n    def link_author(self, session: Session, author: Optional[Author] = None,\n                   author_rid: Optional[str] = None, author_api_name: Optional[str] = None) -> Author:\n        \"\"\"Link to author using enhanced link_resource helper.\"\"\"\n        # Example: Link by API name\n        return self.link_resource(\n            session=session,\n            resource=author, # Optional: pass fetched resource directly\n            model_class=Author,\n            rid_field=\"author_rid\", # Field on Book to store Author's RID\n            api_name_field=\"author_api_name\", # Field on Book to store Author's API name\n            rid_value=author_rid, # Optional: find author by RID\n            api_name_value=author_api_name # Optional: find author by API name\n        )\n```\n\n## Examples\n\nSee the `examples/` directory for complete working examples:\n\n- **Basic Usage**: Simple resource creation and querying using both approaches.\n- **Custom Resources**: Advanced resource types with relationships, custom base classes, and custom status values.\n- **Integration Example**: Demonstrates using Registro with FastAPI.\n\n## Advanced Features\n\n### Resource Relationships\n\nResourceTypeBaseModel includes helper methods for relationship management:\n\n```python\n# Find related resources by RID or API name\nrelated_author = book.get_related_resource(\n    Author,\n    api_name=\"john-doe\",\n    session=session\n)\n\n# Link resources with a single method call\nauthor = book.link_author(session=session, author_api_name=\"jane-roe\")\n```\n\n### Data Serialization\n\nEnhanced serialization with the `to_dict()` method, including RID components:\n\n```python\n# Get a dictionary representation of a resource\nbook_dict = book.to_dict()\nprint(book_dict[\"rid\"])         # Full Resource ID (e.g., ri.library.main.book.ulid)\nprint(book_dict[\"service\"])     # Service name (e.g., 'library')\nprint(book_dict[\"instance\"])    # Instance name (e.g., 'main')\nprint(book_dict[\"resource_type\"])# Resource type (e.g., 'book')\nprint(book_dict[\"resource_id\"]) # ULID locator part\nprint(book_dict[\"title\"])       # Model field\n```\n\n### Field Validation\n\nUtility methods for common validation tasks:\n\n```python\nfrom pydantic import field_validator\n\nclass Department(ResourceTypeBaseModel, table=True):\n    # ...\n    code: str = Field()\n\n    @field_validator(\"code\")\n    @classmethod\n    def validate_code(cls, v):\n        # Use built-in identifier validation\n        return cls.validate_identifier(v, \"Department code\")\n\n# In another model or logic:\n# Validate relationships\n# employee.validate_related_field_match(department, \"status\", \"ACTIVE\")\n```",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Resource-based model system for SQLModel with automatic Resource creation and validation",
    "version": "0.2.2",
    "project_urls": {
        "Bug Tracker": "https://github.com/simbolico/registro/issues",
        "Documentation": "https://simbolico.github.io/registro",
        "Homepage": "https://github.com/simbolico/registro",
        "Source Code": "https://github.com/simbolico/registro"
    },
    "split_keywords": [
        "models",
        " pydantic",
        " resources",
        " sqlalchemy",
        " sqlmodel"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "279b2cdcceb36008a2c2511605ade6b5903b5a1eda84776827520fb6fd275849",
                "md5": "95bf49727bd4fd7f04d4a122ddbcaa1c",
                "sha256": "e50ae06af98d36b1995230a889a40e11b2144075294156a5e06c94e5becb5124"
            },
            "downloads": -1,
            "filename": "registro-0.2.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "95bf49727bd4fd7f04d4a122ddbcaa1c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 27617,
            "upload_time": "2025-10-13T20:54:01",
            "upload_time_iso_8601": "2025-10-13T20:54:01.541456Z",
            "url": "https://files.pythonhosted.org/packages/27/9b/2cdcceb36008a2c2511605ade6b5903b5a1eda84776827520fb6fd275849/registro-0.2.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0657e63153b7eff3f649bbaf0c179a09c18419943ab0928a5e9d40c841d11654",
                "md5": "069ec44058766c8ad77d73f989fbdd78",
                "sha256": "1351d74957d28efc60ec51f9a1bfefde77d4306ec5cb68371362c6dbf1f17f21"
            },
            "downloads": -1,
            "filename": "registro-0.2.2.tar.gz",
            "has_sig": false,
            "md5_digest": "069ec44058766c8ad77d73f989fbdd78",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 36367,
            "upload_time": "2025-10-13T20:54:04",
            "upload_time_iso_8601": "2025-10-13T20:54:04.645531Z",
            "url": "https://files.pythonhosted.org/packages/06/57/e63153b7eff3f649bbaf0c179a09c18419943ab0928a5e9d40c841d11654/registro-0.2.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-13 20:54:04",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "simbolico",
    "github_project": "registro",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "registro"
}
        
Elapsed time: 2.05309s