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.1.tar.gz (31.2 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.1-py3-none-any.whl (36.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: in_layers_core-0.4.1.tar.gz
  • Upload date:
  • Size: 31.2 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.1.tar.gz
Algorithm Hash digest
SHA256 ce266dc40c2c1ccac77683c0f1fcc98f5be70dc5603b6ef64fa20326e618cdd5
MD5 79cecf555359dddb9880855460ef32f7
BLAKE2b-256 729f94d86ecb9c83142708b24993f863e23a4468b3a8f56b3b04cb7c01131c85

See more details on using hashes here.

File details

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

File metadata

  • Download URL: in_layers_core-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 36.4 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0bbfe5d8f3e1926144f002706c8c1a3ce8a2c7afe2a99839d83e766da7cde7ad
MD5 67180ae2849a4eba958b0361210b25ad
BLAKE2b-256 120d7c4a829b3535d149cbd937a9f3eb9060cc50f44ee348385dfd1225bbc27c

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