Schema Class
The Schema class in pydoover.config is the base class for all application configuration definitions. You subclass it, declare typed elements as class attributes, and assign it to your application's config_cls.
Defining a Schema
Declare elements as class-level attributes. The order you define them in is the order they appear in the UI form.
from pydoover.config import Schema, Integer, Number, String, Boolean
class PumpConfig(Schema):
pump_pin = Integer(
"Digital Output Number",
description="The digital output pin to drive the pump",
)
pump_on_time = Number(
"Pump On Time",
default=5.2,
description="The time in seconds to run the pump",
)
site_name = String(
"Site Name",
default="Unnamed",
)
auto_mode = Boolean(
"Automatic Mode",
default=True,
)
In this example, pump_pin has no default, making it a required field. The operator must provide a value when deploying the application. The other three elements have defaults and are optional.
The name Parameter
Every element has an internal _name that is used as the key in JSON Schema output and deployment config dictionaries. By default, this name is auto-generated from the display_name by converting it to lowercase and replacing spaces with underscores (via sanitize_display_name).
You can set an explicit name using the name parameter.
from pydoover.config import String
# Auto-generated name: "site_name"
site = String("Site Name", default="A")
# Explicit name: "my_custom_key"
site = String("Site Name", default="A", name="my_custom_key")
Names must only contain alphanumeric characters, hyphens, underscores, and spaces. The KEY_VALIDATOR regex enforces this: ^[ a-zA-Z0-9_-]*$.
Inheritance
Schema subclasses inherit elements from their parent classes through the MRO (method resolution order). This lets you create base configurations that are extended by more specific schemas.
from pydoover.config import Schema, String, Number
class BaseConfig(Schema):
site_name = String("Site Name", default="Unnamed")
poll_interval = Number("Poll Interval", default=30.0)
class ExtendedConfig(BaseConfig):
# Inherits site_name and poll_interval from BaseConfig
alert_threshold = Number("Alert Threshold", default=40.0)
ExtendedConfig has three elements: site_name and poll_interval (inherited) plus alert_threshold (its own).
Overriding Inherited Elements
If a subclass declares an element with the same _name as an inherited element, the subclass version replaces it. The position of the overridden element is preserved from the parent.
class CustomConfig(BaseConfig):
# Override the inherited poll_interval with a different default
poll_interval = Number("Poll Interval", default=60.0)
This preserves the element's position in the schema output while changing its configuration.
Required Elements
An element is required by default if it has no default value (i.e., the default is NotSet). You can override this behaviour with the required parameter.
from pydoover.config import String, NotSet
# Required (no default)
name = String("Name")
# Optional (has a default)
nickname = String("Nickname", default="")
# Explicitly required even though a default exists
important = String("Important", default="fallback", required=True)
# Explicitly optional (must provide a default)
optional = String("Optional", default=None, required=False)
Passing required=False without a default raises a ValueError. An optional element needs a fallback value for when the operator does not provide one.
When deployment config is loaded, missing required elements raise a ValueError. Missing optional elements are set to their default value.
Accessing Values
After configuration is loaded (during the application's setup phase), access element values via the .value property.
class MyProcessor(Application):
config_cls = PumpConfig
async def setup(self):
pin = self.config.pump_pin.value # int
time = self.config.pump_on_time.value # float
name = self.config.site_name.value # str
auto = self.config.auto_mode.value # bool
If you access .value before configuration has been loaded, a ValueError is raised with a message indicating which element is not set.
JSON Schema Generation
Call to_schema() on the Schema class (not an instance) to generate the JSON Schema representation.
import json schema = PumpConfig.to_schema() print(json.dumps(schema, indent=2))
This produces a JSON Schema draft 2020-12 document with:
- A
propertiesobject containing each element's schema fragment - A
requiredarray listing elements that have no default - Custom extensions like
x-name,x-hidden,x-position,x-required, andx-advancedfor the Doover UI
The output can be used for validation, documentation, or form generation outside of the Doover platform.
Schema Name
The name class parameter (passed via __init_subclass__) sets the schema's title in the JSON Schema output. It defaults to "$default".
class MyConfig(Schema, name="Pump Controller Configuration"):
pump_pin = Integer("Pump Pin")
The name appears as the title field in the generated JSON Schema.
Exporting to doover_config.json
The export() class method writes the schema to a doover_config.json file, which is included in the application package for deployment.
from pathlib import Path
from pydoover.config import Schema, Number
class Config(Schema):
threshold = Number("Threshold", default=40.0)
# Export the schema to the project's config file
Config.export(Path("doover_config.json"), "my_app_name")
This writes (or updates) the file with the schema under the given app name key:
{
"my_app_name": {
"config_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "",
"title": "$default",
"type": "object",
"properties": { ... },
"additionalElements": true,
"required": []
}
}
}
If the file already exists, export() merges the schema into it rather than overwriting.
In most cases, you do not call export() manually. The Doover CLI command doover config-schema export handles this for you as part of the application build process. You can also validate schemas with doover config-schema validate and generate sample configs with doover config-schema generate.
Runtime Configuration Loading
When the application starts (or a processor is invoked), the framework calls _inject_deployment_config(config) with the deployment configuration dictionary. This method:
- For each key in the config dict, finds the matching element by name and calls
load_data(value)on it - For unknown keys (not declared in the schema), creates a dynamic
ConfigElementand attaches it to the instance - For declared elements missing from the config dict, checks if they are required (raises
ValueError) or optional (sets them to their default)
You do not call _inject_deployment_config yourself. The framework handles it during setup.
Related Pages
- Configuration Overview -- high-level introduction to the configuration system
- Configuration Elements -- all element types with examples
- Advanced Configuration -- nested objects, variable references, and deep overrides