Skip to main content

Construct Python objects directly from configuration files

Project description

confactory

confactory allows building Python objects directly from configuration files. Under the hood, it uses attrs which makes annotating class properties dead simple. These annotations are inspected at runtime to enable instantiating Python classes that derive from confactory.Catalog. This can be used as a safer alternative to pickling, since you get to control what can be instantiated via confactory.Catalog.

Simply load a configuration file with a (possibly nested) definition of an object, and confactory will build the associated Python object. confactory also includes support for Jsonnet, allowing for compact representation of Python objects with re-usable components.

NOTE: confactory is still early in development, so large API and functionality changes might occur. Future versions will likely add support for additional configuration languages, e.g. YAML and TOML.

A Simple Example

Let's say you have the json file vehicles.json, that defines a bunch of vehicle types:

[
    {
        "type": "Automobile",
        "name": "Sedan",
        "seats": 4,
        "fuel": "hybrid",
    },
    {
        "type": "Automobile",
        "name": "Convertible",
        "seats": 2,
        "fuel": "electric",
        "features": [{"type": "Rims", "material": "alloy"}, {"type": "Sunroof", "automatic": true}]
    },
    {
        "type": "Bicycle",
        "name": "Bicycle",
        "seats": 1,
        "frame": "steel"
    },
    {
        "type": "Bicycle",
        "name": "Tandem Bicycle",
        "seats": 2,
        "frame": "steel"
    },
]

you can construct the associated Python objects like so:

from enum import auto, StrEnum
from typing import List, Optional

from confactory import Catalog, configurable

class Fuel(StrEnum):
    combustion = auto()
    electric = auto()
    hybrid = auto()

class Material(StrEnum):
    alloy = auto()
    steel = auto()

@configurable
class Vehicle(Catalog):
    name: str
    seats: int

    def drive(self):
        pass

@configurable
class Feature(Catalog):
    pass

@configurable
class Sunroof(Feature):
    automatic: bool

@configurable
class Rims(Feature):
    material: Material

@configurable
class Automobile(Vehicle):
    fuel: Fuel
    features: List[Feature] = []

    def drive(self):
        print(f"{self.name} goes Vroom!\n")

@configurable
class Bicycle(Vehicle):
    frame: Material

    def drive(self):
        print(f"{self.name} uses Pedal Power!\n")

vehicles = Vehicle.from_config("vehicles.json", allow_multiple=True)
for vehicle in vehicles:
    print(vehicle)
    vehicle.drive()

Here's the output you'd see:

Automobile(name='Sedan', seats=4, fuel='hybrid', features=[])
Sedan goes Vroom!

Automobile(name='Convertible', seats=2, fuel='electric', features=[Rims(material='alloy'), Sunroof(automatic=True)])
Convertible goes Vroom!

Bicycle(name='Bicycle', seats=1, frame='steel')
Bicycle uses Pedal Power!

Bicycle(name='Tandem Bicycle', seats=2, frame='steel')
Tandem Bicycle uses Pedal Power!

Note, StrEnum is new in Python 3.11, so if you have an older version of Python, to run the code above you'll need to modify the enums like so:

from enum import Enum

class Fuel(Enum):
    combustion = "combustion"
    electric = "electric"
    hybrid = "hybrid"

class Material(Enum):
    alloy = "alloy"
    steel = "steel"

An Alternative to Pickle

In Python the standard pickle module is well-known to be unsafe, as it can execute arbitrary code during de-serialization of Python objects. To make pickle safer, the module includes the Unpickler.find_class method to allow restricting globals. This is a strictly opt-out approach; any Python object can be de-serialized implicitly --- developers then must decide what to filter out.

With confactory.Catalog you expliclty decide what types can be constructed into Python objects. This makes it less likely that you'll accidentally open the door for arbitrary code execution. Though of course, if you register a class that has unsafe side-effects, then you can certainly introduce security risks. The important point is that you decide what risk is acceptable with an opt-in approach.

What about cattrs?

While cattrs is an excellent package that also enables constructing objects from configuration files, it has a different philosophy. This is was even more true when I first developed my code back in 2020 (though I've only recently released it publicly). cattrs focuses on allowing the creation of structured Python objects from potentially unstructured data. This gives great flexibility, but means users of the package are expected to understand their data format such that they can pass the expected types in during object construction. That requirement has been relaxed a bit with the introduction of cattrs.strategies.include_subclasses and cattrs.strategies.use_class_methods which are new as of 2023.

Rather the approach taken here is a bit more opinionated, but reduces boilerplate even more. This was a very important factor, which enabled the creation of the sister package cresset which allows defining Pytorch torch.nn.Modules through configuration files. Many of the design decisions made for this package were specifically created with cresset in mind.

License

This repository is licensed under the MIT license.

SPDX-License-Identifer: MIT

Project details


Release history Release notifications | RSS feed

This version

0.1

Download files

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

Source Distribution

confactory-0.1.tar.gz (14.3 kB view details)

Uploaded Source

Built Distribution

confactory-0.1-py3-none-any.whl (18.1 kB view details)

Uploaded Python 3

File details

Details for the file confactory-0.1.tar.gz.

File metadata

  • Download URL: confactory-0.1.tar.gz
  • Upload date:
  • Size: 14.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.13

File hashes

Hashes for confactory-0.1.tar.gz
Algorithm Hash digest
SHA256 b6724ffa05059f5ddb93c4674e014dd88c3b64ab992c82182e05e41f899952f3
MD5 ce97f9772e10a8aae46328610550293f
BLAKE2b-256 fb45e67e6f7cb8f3b5e44d923c555d89b10e27cd746f35ad149a52e2532d147a

See more details on using hashes here.

File details

Details for the file confactory-0.1-py3-none-any.whl.

File metadata

  • Download URL: confactory-0.1-py3-none-any.whl
  • Upload date:
  • Size: 18.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.13

File hashes

Hashes for confactory-0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4d9e1ba9b6e82381ea549d1f4b0cabb0cab8b65e8200f67add0f0cb60ea25610
MD5 b5fc332ce06240909bffd104fd640f49
BLAKE2b-256 f4466329df32968d53ff4f6e7039e2aff246626b4bd6e1df452f7e20c5cb0403

See more details on using hashes here.

Supported by

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