Package: confactory
confactoryconfactoryallows building Python objects directly from configuration files.
Under the hood, it usesattrswhich makes annotating class properties dead simple.
These annotations are inspected at runtime to enable instantiating Python classes that derive fromconfactory.Catalog.
This can be used as a safer alternative to pickling, since you get to control what can be instantiated viaconfactory.Catalog.Simply load a configuration file with a (possibly nested) definition of an object, andconfactorywill build the associated Python object.confactoryalso includes support forJsonnet, allowing for compact representation of Python objects with re-usable components.NOTE:confactoryis 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 ExampleLet's say you have the json filevehicles.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:fromenumimportauto,StrEnumfromtypingimportList,OptionalfromconfactoryimportCatalog,configurableclassFuel(StrEnum):combustion=auto()electric=auto()hybrid=auto()classMaterial(StrEnum):alloy=auto()steel=auto()@configurableclassVehicle(Catalog):name:strseats:intdefdrive(self):pass@configurableclassFeature(Catalog):pass@configurableclassSunroof(Feature):automatic:bool@configurableclassRims(Feature):material:Material@configurableclassAutomobile(Vehicle):fuel:Fuelfeatures:List[Feature]=[]defdrive(self):print(f"{self.name}goes Vroom!\n")@configurableclassBicycle(Vehicle):frame:Materialdefdrive(self):print(f"{self.name}uses Pedal Power!\n")vehicles=Vehicle.from_config("vehicles.json",allow_multiple=True)forvehicleinvehicles: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,StrEnumis 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:fromenumimportEnumclassFuel(Enum):combustion="combustion"electric="electric"hybrid="hybrid"classMaterial(Enum):alloy="alloy"steel="steel"An Alternative to PickleIn Python the standardpicklemodule is well-known to be unsafe, as it can execute arbitrary code during de-serialization of Python objects.
To makepicklesafer, the module includes theUnpickler.find_classmethod to allowrestricting globals.
This is a strictlyopt-outapproach; any Python object can be de-serializedimplicitly--- developers then must decide what to filter out.Withconfactory.Catalogyouexplicltydecide 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 thatyoudecide what risk is acceptable with anopt-inapproach.What about cattrs?Whilecattrsis 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).cattrsfocuses 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 ofcattrs.strategies.include_subclassesandcattrs.strategies.use_class_methodswhich 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 packagecressetwhich allows defining Pytorchtorch.nn.Modules through configuration files.
Many of the design decisions made for this package were specifically created withcressetin mind.LicenseThis repository is licensed under the MIT license.SPDX-License-Identifer: MIT