Advanced Usage & Troubleshooting¶
Using Classes Directly¶
For more control, use the underlying classes:
from rconfig import ConfigStore, ConfigValidator, ConfigInstantiator
store = ConfigStore()
store.register(name="model", target=ModelConfig)
validator = ConfigValidator(store)
instantiator = ConfigInstantiator(store, validator)
config = {"_target_": "model", "hidden_size": 256}
result = validator.validate(config)
if result.valid:
model = instantiator.instantiate(config)
Custom File Loaders¶
ReausoConfig includes built-in loaders for YAML, JSON, and TOML:
YamlConfigLoader-.yaml,.ymlfilesJsonConfigLoader-.jsonfilesTomlConfigLoader-.tomlfiles (Python 3.11+)
To add support for additional file formats, create a custom loader and register it for specific extensions:
from rconfig import ConfigFileLoader, register_loader
from pathlib import Path
from typing import Any
import configparser
class IniConfigLoader(ConfigFileLoader):
def load(self, path: Path) -> dict[str, Any]:
parser = configparser.ConfigParser()
parser.read(path)
# Convert to nested dict
return {s: dict(parser[s]) for s in parser.sections()}
# Register for .ini extension
register_loader(IniConfigLoader(), ".ini")
# Now works with instantiate
model = rc.instantiate(path=Path("config.ini"))
Thread Safety¶
ReausoConfig is thread-safe for concurrent access. The following operations can be safely called from multiple threads:
rc.register()/rc.unregister()- Thread-safe class registrationrc.instantiate()- Thread-safe config loading and instantiationrc.validate()- Thread-safe validationrc.set_cache_size()/rc.clear_cache()- Thread-safe cache managementregister_loader()/unregister_loader()- Thread-safe loader registrationregister_exporter()/unregister_exporter()- Thread-safe exporter registration
Note: rc.known_targets() returns a live view of registrations. Individual read operations are thread-safe, but iteration during concurrent mutation may raise RuntimeError.
Error Handling¶
ReausoConfig provides a hierarchy of exceptions:
ConfigError (base)
├── ConfigFileError # File loading issues
├── TargetNotFoundError # Unknown _target_
├── ValidationError # Validation failures
│ ├── MissingFieldError # Required field missing
│ ├── TypeMismatchError # Wrong type provided
│ ├── AmbiguousTargetError # Cannot infer type (abstract/multiple impls)
│ ├── TargetTypeMismatchError # Explicit _target_ wrong type
│ ├── TypeInferenceError # Inferred type validation failed
│ └── RequiredValueError # _required_ value not provided
├── CompositionError # Config composition issues
│ ├── CircularRefError # Circular _ref_ detected
│ ├── RefResolutionError # Cannot resolve _ref_ path
│ ├── AmbiguousRefError # Multiple files match extension-less _ref_
│ ├── RefAtRootError # _ref_ at root level
│ ├── RefInstanceConflictError # Both _ref_ and _instance_ in same block
│ ├── CircularInstanceError # Circular _instance_ detected
│ ├── InstanceResolutionError # Cannot resolve _instance_ path
│ ├── InvalidInnerPathError # Invalid inner_path for partial instantiation
│ └── MergeError # Deep merge failed
├── InterpolationError # Interpolation issues
│ ├── InterpolationSyntaxError # Invalid ${...} syntax
│ ├── InterpolationResolutionError # Path/env not found
│ ├── CircularInterpolationError # Circular ${...} reference
│ ├── EnvironmentVariableError # Required env var not set
│ └── ResolverError # Custom resolver issues
│ ├── UnknownResolverError # Resolver path not registered
│ └── ResolverExecutionError # Resolver function raised exception
├── OverrideError # Override-related errors
│ ├── InvalidOverridePathError # Override path doesn't exist
│ └── InvalidOverrideSyntaxError # Override string malformed
└── InstantiationError # Object creation failed
Example error handling:
from rconfig import (
ConfigFileError, ValidationError, InstantiationError,
AmbiguousTargetError, TypeInferenceError,
UnknownResolverError, ResolverExecutionError
)
try:
model = rc.instantiate(path=Path("config.yaml"))
except AmbiguousTargetError as e:
print(f"Cannot infer type: {e}")
print(f"Available targets: {e.available_targets}")
except TypeInferenceError as e:
print(f"Inferred type validation failed: {e}")
except UnknownResolverError as e:
print(f"Unknown resolver: {e}")
except ResolverExecutionError as e:
print(f"Resolver failed: {e}")
except ConfigFileError as e:
print(f"Could not load file: {e}")
except ValidationError as e:
print(f"Invalid config: {e}")
except InstantiationError as e:
print(f"Could not create object: {e}")
Troubleshooting¶
Common errors and how to fix them:
TargetNotFoundError: Target 'X' is not registered¶
Cause: The _target_ name in your config doesn't match any registered class.
Fix: Ensure you called rc.register(name="X", target=MyClass) before instantiation, and that the name matches exactly (case-sensitive).
ConfigFileError: file not found¶
Cause: The config file path doesn't exist or is relative to the wrong directory.
Fix: Verify the file exists and the path is correct relative to your working directory. Use absolute paths if unsure.
RefResolutionError / AmbiguousRefError¶
Cause: A _ref_ path doesn't point to an existing file, or an extension-less ref matches multiple files (e.g., both model.yaml and model.json exist).
Fix: Check that the referenced file exists. If using extension-less refs, ensure only one file with that stem exists, or specify the extension explicitly.
CircularInterpolationError¶
Cause: Two or more ${...} expressions reference each other, creating a cycle.
Fix: Break the cycle by using a literal value for one of the expressions instead of a reference.
AmbiguousTargetError: Cannot infer type¶
Cause: A nested config dict has no _target_ and the field's type hint is abstract or has multiple registered implementations.
Fix: Add an explicit _target_ to the nested config block to specify which implementation to use.
RequiredValueError¶
Cause: Config contains _required_ markers that weren't satisfied by overrides, CLI arguments, or environment variables.
Fix: Provide values via overrides={"key": value}, CLI arguments (key=value), or environment variable interpolation (${env:KEY}).
RefAtRootError¶
Cause: A config file has _ref_ at its root level. Every config file must define an object dictionary directly.
Fix: Move the _ref_ inside a nested key, or inline the referenced content directly.
Key Strengths¶
- Minimal coupling: Only your startup code imports rconfig
- Simple API: Just
register,validate,instantiate - Lazy instantiation: Defer expensive initialization until first access
- Type validation: Catches type mismatches before instantiation
- Implicit target inference: Omit
_target_for concrete nested types - Config composition: Load and merge configs from files with
_ref_ - Instance sharing: Share objects across config with
_instance_ - Interpolation: Reference values with
${...}, env vars, and full expressions - Provenance tracking: Debug where each config value originated
- Lightweight: Focused feature set, no bloat
- Pure Python output: Instantiated objects have no framework dependency