Skip to main content

The Python port of Node in Layers Core.

Project description

In Layers Core

Python port of the Node-in-Layers core framework. Supports Domains, config and layers loading, and cross-layer logging.

Key points:

  • Domains explicitly provided in config (no convention discovery)
  • Layers are loaded in configured order (supports composite layers)
  • Cross-layer logging with automatic id propagation and function wraps

Pecularities, Limitations, and Recommendations

No Keyword Arguments for Layer level Functions

For the public functions for a given layer, the arguments cannot use kwargs. The reason behind this is it creates a consistent interface to allow the framework and other tools to work.

We recommend making arguments an object (class instance, dict), and making the last argument a "cross_layer_props" object, that can pass along across layers.

Contributing

Running Unit Tests

poetry run pytest --cov=. --cov-report=term-missing --cov-report=html -q

Auto-Cleaning / Checking Tools

./bin/lint.sh

Publishing

./bin/deploy.sh

Models and Persistence Backends

Overview

  • Models are standard Pydantic classes decorated with @model(domain=..., plural_name=...).
  • When a domain’s services layer is loaded, the framework discovers the domain’s models and exposes them as SimpleModel wrappers under:
    • context.models.<domain>.get_models() -> Box, keyed by the model’s plural name
    • Example access: context.models.mydomain.get_models().MyModels
  • Each entry in this mapping is a SimpleModel wrapper with:
    • instance(data | **kwargs) to wrap raw data
    • create(data | **kwargs) to persist through a backend
    • retrieve(id), update(id, **kwargs), delete(id), search(query)
    • get_model_definition(), get_primary_key_name(), get_primary_key(data)
  • A SimpleModel instance supports zero-arg getters for its data via instance.get.<field>().

Important: Persistence uses a backend returned by a model backend provider living in the services of the domain named by in_layers_core.models.model_backend. If none is provided, a core fallback uses a no-op backend (CRUD operations will raise NotImplemented).

Declaring a Model

from pydantic import BaseModel, Field
from in_layers.core.models.libs import model

@model(domain="pipeline", plural_name="PipelineJobs")
class PipelineJob(BaseModel):
    id: str = Field(...)
    name: str = Field(...)

Providing a Model Backend (via a Domain’s Services)

You must provide, in the configured domain’s services layer, a method that returns a backend for each model. The service needs a method:

  • get_model_backend(model_definition) -> BackendProtocol

For example:

# services.py

class MyDomainServices:
    def __init__(self, ctx):
        self._ctx = ctx

    def get_model_backend(self, model_definition):
        # you can check the domain, the name of the model (if it is model/domain specific)
        # return your BackendProtocol implementation (e.g., Mongo, SQL, etc.)
        return MyConcreteBackend(...)


Then tell the framework which backend provider to use via config:
```python
config = Box(
    system_name="test",
    environment="test",
    in_layers_core=Box(
        logging=Box(...),
        layer_order=["services", "features"],
        domains=[...],
        models=Box(
            # Choose your model backend by telling the framework which domain it lives in.
            # The framework will call mydomain.services.get_model_backend(model_definition)
            model_backend="mydomain",
            # Optional: surface CRUD wrappers in services/features
            model_services_cruds=True,
            model_features_cruds=False,
        ),
    ),
)

Notes:

  • Ensure the configured domain’s services are loaded before domains whose models you want to wrap (via domain ordering and layer_order).
  • If not provided, the framework falls back to a core default provider, which uses a no-op backend (CRUD is not implemented).
  • If model_features_cruds is true, model_services_cruds is implicitly treated as true and both layers expose cruds.<Plural> wrappers.

Using Models in Services

from pydantic import BaseModel
from in_layers.core.models.libs import model
# ./mydomain/models.py
@model(domain="mydomain", plural_name="MyModels")
class MyModel(BaseModel):
    id: str
    name: str
# ./mydomain/services.py
from types import SimpleNamespace

class MyServices:
    def __init__(self, ctx):
        self._ctx = ctx

    def return_a_model_instance(self):
        models = self._ctx.models.mydomain.get_models()
        MyModels = models.MyModels
        # Create a non-persisted instance via kwargs (or Mapping)
        inst = MyModels.instance(id="123", name="John Doe")
        # Access fields
        assert inst.get.id() == "123"
        assert inst.get.name() == "John Doe"
        return inst
# ./mydomain/__init__.py
from . import services, models
name = "mydomain"
__all__ = ['name', 'services', 'models']

Backends

Backends implement BackendProtocol:

  • create(model, data) -> Mapping
  • retrieve(model, id) -> Mapping | None
  • update(model, id, data) -> Mapping
  • delete(model, id) -> None
  • search(model, query) -> ModelSearchResult

Your persistence factory decides which backend to return per model class (e.g., route different models to different datastores).

Instance Creation Options

  • Mapping:
    • MyModels.instance({"id": "123", "name": "John"})
    • MyModels.create({"id": "123", "name": "John"})
  • Keywords:
    • MyModels.instance(id="123", name="John")
    • MyModels.create(id="123", name="John")

When both are provided, keyword arguments override keys in the mapping.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

in_layers_core-0.4.0.tar.gz (31.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

in_layers_core-0.4.0-py3-none-any.whl (36.2 kB view details)

Uploaded Python 3

File details

Details for the file in_layers_core-0.4.0.tar.gz.

File metadata

  • Download URL: in_layers_core-0.4.0.tar.gz
  • Upload date:
  • Size: 31.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for in_layers_core-0.4.0.tar.gz
Algorithm Hash digest
SHA256 0b7eb68ba5063e249e8dc3d19ac475bf9ff1c3525a654c0af0b493db5c1be9cb
MD5 57c479e26dc8b49bfb16901021ce5eec
BLAKE2b-256 3a7e30e3372c45bd98018b3357ba3db395507fb26042aefd01e1f0a1584c97d0

See more details on using hashes here.

File details

Details for the file in_layers_core-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: in_layers_core-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 36.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for in_layers_core-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 94d4b48b41c64a3c5397fce682fba93275cc87a8fe211006acb983f7390a8025
MD5 b1930e06a1f78d3e88e361cbbebc127f
BLAKE2b-256 a3b3708072ba84f9052dfa37c03f8377703e80c92eae3d359c56e6e3bdb3e9f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page