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

Uploaded Python 3

File details

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

File metadata

  • Download URL: in_layers_core-0.4.7.tar.gz
  • Upload date:
  • Size: 31.5 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.7.tar.gz
Algorithm Hash digest
SHA256 6c00cff6683c225f93a625800211f96ed0c422ea50998122c9362560c695d364
MD5 52f1205eb770d1c3f8a27049671b4a99
BLAKE2b-256 c67914031b85a610ac9b7b985ca39ab8c2c2fcca85bdd7380e94600d44f81e7c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: in_layers_core-0.4.7-py3-none-any.whl
  • Upload date:
  • Size: 36.6 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.7-py3-none-any.whl
Algorithm Hash digest
SHA256 ed9ae4df1355750a0e3a5ca6ebf938ae0a81a4546f01c8ddc1fe5b899fd66a79
MD5 2239d26d04542369d0e35e97da04134b
BLAKE2b-256 0d8708e73a1081e2ec30373a0aa740d9d8b5cd3ca85351bedb1c51c0dc8b1099

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