{"id":48877052,"url":"https://github.com/nexustech101/decorates","last_synced_at":"2026-04-16T01:01:18.734Z","repository":{"id":351181169,"uuid":"1209903405","full_name":"nexustech101/decorates","owner":"nexustech101","description":"A Python framework built with Developer Experience (DX) in mind. decorators uses a clean, ergonomic decorator registry design pattern to eliminate boilerplate when building CLI tools and database-backed applications.","archived":false,"fork":false,"pushed_at":"2026-04-15T20:08:11.000Z","size":221,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T21:31:34.729Z","etag":null,"topics":["cli-framework","data-engineering","data-modeling","design-patterns","framework","open-source","orm-framework","python-package"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nexustech101.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-13T22:38:18.000Z","updated_at":"2026-04-15T20:08:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nexustech101/decorates","commit_stats":null,"previous_names":["nexustech101/decorators","nexustech101/registers","nexustech101/framework"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/nexustech101/decorates","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nexustech101%2Fdecorates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nexustech101%2Fdecorates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nexustech101%2Fdecorates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nexustech101%2Fdecorates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nexustech101","download_url":"https://codeload.github.com/nexustech101/decorates/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nexustech101%2Fdecorates/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31866357,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli-framework","data-engineering","data-modeling","design-patterns","framework","open-source","orm-framework","python-package"],"created_at":"2026-04-16T01:01:04.916Z","updated_at":"2026-04-16T01:01:18.729Z","avatar_url":"https://github.com/nexustech101.png","language":"Python","readme":"# Decorates\n\n[![PyPI version](https://img.shields.io/pypi/v/decorates)](https://pypi.org/project/decorates/)\n[![Python versions](https://img.shields.io/pypi/pyversions/decorates)](https://pypi.org/project/decorates/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![CLI](https://img.shields.io/badge/module-decorates.cli-blue)](#decoratescli)\n[![DB](https://img.shields.io/badge/module-decorates.db-darkgreen)](#decoratesdb)\n[![Tests](https://img.shields.io/badge/tested-170%2B%20tests-brightgreen)](#testing)\n\nDecorates is a production-oriented toolkit for two common Python surfaces:\n\n- `decorates.cli` for module-first command registration, typed arguments, and built-in help\n- `decorates.db` for Pydantic model persistence and additive schema operations on SQLAlchemy\n\nThe package emphasizes explicit APIs, predictable behavior, and test-backed reliability.\n\n## Install\n\n```bash\npip install decorates\n```\n\n## Quick Start Guide\n\n1. Build one CLI command with a decorator.\n2. Build one DB model with a decorator.\n3. Use `Model.objects` for CRUD.\n\n### CLI in 60 seconds\n\n```python\nimport decorates.cli as cli\nimport decorates.db as db\nfrom pydantic import BaseModel\n\n@db.database_registry(\"users.db\", table_name=\"users\", key_field=\"id\")\nclass User(BaseModel):\n    id: int | None = None\n    name: str\n\n@cli.register(name=\"add\", description=\"Create a user\")\n@cli.argument(\"name\", type=str)\n@cli.option(\"--add\")\n@cli.option(\"-a\")\ndef add_user(name: str) -\u003e str:\n    user = User(name=name)\n    user.save()\n    return f\"Created user {user.id}: {user.name}\"\n\n@cli.register(name=\"list\", description=\"List users\")\n@cli.option(\"--list\")\n@cli.option(\"-l\")\ndef list_users() -\u003e str:\n    users = User.objects.all()\n    if not users:\n        return \"No users found.\"\n    return \"\\n\".join(f\"{u.id}: {u.name}\" for u in users)\n\nif __name__ == \"__main__\":\n    cli.run()\n```\n\n```bash\npython users.py add \"Alice\"\npython users.py --add \"Bob\"\npython users.py list\npython users.py --help\n```\n\n### Database + FastAPI in 5 minutes\n\n```python\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI, HTTPException\nfrom pydantic import BaseModel\nfrom decorates.db import (\n    RecordNotFoundError,\n    UniqueConstraintError,\n    database_registry,\n)\n\nDB_URL = \"sqlite:///shop.db\"\n\n@database_registry(DB_URL, table_name=\"customers\", unique_fields=[\"email\"])\nclass Customer(BaseModel):\n    id: int | None = None\n    name: str\n    email: str\n\n@database_registry(DB_URL, table_name=\"products\")\nclass Product(BaseModel):\n    id: int | None = None\n    name: str\n    price: float\n\n@database_registry(DB_URL, table_name=\"orders\")\nclass Order(BaseModel):\n    id: int | None = None\n    customer_id: int\n    product_id: int\n    quantity: int\n    total: float\n\nclass CreateCustomer(BaseModel):\n    name: str\n    email: str\n\nclass CreateProduct(BaseModel):\n    name: str\n    price: float\n\nclass CreateOrder(BaseModel):\n    customer_id: int\n    product_id: int\n    quantity: int\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    for model in (Customer, Product, Order):\n        model.create_schema()\n    yield\n    for model in (Customer, Product, Order):\n        model.objects.dispose()\n\napp = FastAPI(lifespan=lifespan)\n\n@app.post(\"/customers\", response_model=Customer, status_code=201)\ndef create_customer(payload: CreateCustomer):\n    try:\n        return Customer.objects.create(**payload.model_dump())\n    except UniqueConstraintError:\n        raise HTTPException(status_code=409, detail=\"Email already exists\")\n\n\n@app.get(\"/customers/{customer_id}\", response_model=Customer)\ndef get_customer(customer_id: int):\n    try:\n        return Customer.objects.require(customer_id)\n    except RecordNotFoundError:\n        raise HTTPException(status_code=404, detail=\"Customer not found\")\n\n\n@app.post(\"/products\", response_model=Product, status_code=201)\ndef create_product(payload: CreateProduct):\n    return Product.objects.create(**payload.model_dump())\n\n\n@app.post(\"/orders\", response_model=Order, status_code=201)\ndef create_order(payload: CreateOrder):\n    customer = Customer.objects.get(payload.customer_id)\n    if customer is None:\n        raise HTTPException(status_code=404, detail=\"Customer not found\")\n\n    product = Product.objects.get(payload.product_id)\n    if product is None:\n        raise HTTPException(status_code=404, detail=\"Product not found\")\n\n    return Order.objects.create(\n        customer_id=customer.id,\n        product_id=product.id,\n        quantity=payload.quantity,\n        total=product.price * payload.quantity,\n    )\n\n@app.get(\"/orders/desc\", response_model=list[Order])\ndef list_orders_desc(limit: int = 20, offset: int = 0):  # Filter by oldest   (1, 2, 3...n)\n    return Order.objects.filter(order_by=\"id\", limit=limit, offset=offset)\n\n\n@app.get(\"/orders/asc\", response_model=list[Order])\ndef list_orders_asc(limit: int = 20, offset: int = 0):  # Filter by newest  (n...3, 2, 1)\n    return Order.objects.filter(order_by=\"-id\", limit=limit, offset=offset)\n```\n\n## Core Concepts\n\n### `decorates.cli`\n\n- Register functions with module-level decorators: `@register`, `@argument`, `@option`.\n- Run command handlers through the module registry via `decorates.cli.run()`.\n- Support positional + named argument forms (for non-bool args), with bool flags as `--flag`.\n- Command aliases are declared with `@option(\"-x\")` / `@option(\"--long\")`.\n- Built-in help command is always available: `help`, `--help`, and `-h`.\n- Runtime wraps unexpected handler crashes as `CommandExecutionError` (with original exception chaining).\n- Operational logs use standard Python logging namespaces under `decorates.cli.*`.\n\n### `decorates.db`\n\n- Register `BaseModel` classes with `@database_registry(...)`.\n- Access all persistence through `Model.objects`.\n- `id: int | None = None` gives database-managed autoincrement IDs.\n- Schema helpers are available as class methods: `create_schema`, `drop_schema`, `schema_exists`, `truncate`.\n- Unexpected SQLAlchemy runtime failures are normalized into `SchemaError` for cleaner, predictable error handling.\n- Operational logs use standard Python logging namespaces under `decorates.db.*`.\n- DB exceptions provide structured metadata (`exc.context`, `exc.to_dict()`) for production diagnostics.\n\n## `decorates.db` Usage Snapshot\n\n```python\n# Filtering operators\nOrder.objects.filter(total__gte=100)\nCustomer.objects.filter(email__ilike=\"%@example.com\")\nOrder.objects.filter(quantity__in=[1, 2, 3])\n\n# Sorting and pagination\nOrder.objects.filter(order_by=\"-id\", limit=20, offset=0)\n\n# Bulk writes\nProduct.objects.bulk_create([...])\nProduct.objects.bulk_upsert([...])\n\n# Additive migration helpers\nCustomer.objects.ensure_column(\"phone\", str | None, nullable=True)\nCustomer.objects.rename_table(\"customers_archive\")\n```\n\nAfter `rename_table(...)` succeeds, the same `Model.objects` manager and\nschema helpers are immediately bound to the new table name.\n\nIf your model contains a field named `password`, password values are automatically hashed on write, and instances receive `verify_password(...)`.\n\n## Documentation\n\n- DB guide: `src/decorates/db/USAGE.md`\n- CLI source API: `src/decorates/cli`\n- DB source API: `src/decorates/db`\n\n## Requirements\n\n- Python 3.10+\n- `pydantic\u003e=2.0`\n- `sqlalchemy\u003e=2.0`\n\n## Testing\n\n- Default `pytest` includes SQLite plus PostgreSQL/MySQL rename-state integration tests.\n- Start Docker Desktop (or another Docker engine) before running tests so\n  `docker-compose.test-db.yml` services can boot.\n- The decorates is backed by a rigorous, production-focused test suite (170+ tests) that covers unit, edge-case, and multi-dialect integration behavior.\n\n## License\n\nMIT\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnexustech101%2Fdecorates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnexustech101%2Fdecorates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnexustech101%2Fdecorates/lists"}