Skip to content

FloatingRockStudio/fr_config

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fr_config - Floating Rock Cascading Config Resolver

fr_config is a hierarchical cascading config resolver that resolves configs based on hierarchy structure and schema.

This allows for a high degree of control over how configurations are loaded based on where they are in the filesystem.

Configs will load in the order of variant/default, parent.variant/parent.default until they either hit the root paths or a $parent redirect. A redirect allows for configs to jump or reference a shared directory.
For example a package config may have a manifests directory that can be referenced at any level of the hierarchy.

A Cascade is how the configs are applied via the schema. eg: given a list value, how is that resolved across configs? Do we append? prepend? replace?
If that list contains objects do we replace those? just where they conflict? or perhaps recursively update.
This cascade allows us to use the same config layer to apply to many data types while maintaining type coherence.

fr_config was designed to work with fr_env_resolver, Floating Rocks rez-based environment resolver for loading context dependent package lists.

Context Path Resolve

Contexts are .frconfigs that live in any directory, When a path is loaded, the variables "cascade" backwards up the tree for a final resolve.

This allows for per shot/asset variations.

It's important to note this is not restricted to production dirs, any directory can be used to cascade.

The process here is the same for Environment Contexts (packages, variables) and Tool Contexts (The subenvironment used by tools in the launcher)

Context Path Resolve

Variants

Inside the config resolve is something called "variants", By default, there is a "default" variant associated with each level in the hierarchy, but additional variants can be added to produce variations such as per department.

These variations are used in anything deriving from fr_config. Meaning you can have a variant for a context, manifest, workflow or tool.

If a variant is specified to a loader, it will attempt to load that variant any time it is available before loading the default variant. You cannot skip loading the default, if that is required then leave it blank and only use variants to provide details.

Context Path Resolve

Features

  • Hierarchical Configuration: Configs follow a filesystem path for resolving
  • Schema Validation: JSON Schema-based validation with custom type system
  • Version Management: Built-in versioning with tags (published, latest, deprecated, etc.)
  • Data Merging: Configurable cascade modes (replace, update, append, prepend)
  • Multi-Variant Support: Support for different configuration variants (departments, tasks, etc)

Quick Start

Important Note Any code inside the _internal/ folder is not gaurenteed to persist across versions and should not be used directly outside of debugging.

Installation

Local Installation

If you have the source code locally, navigate to the project directory and install it:

cd /path/to/fr_config
pip install .

or with rez:

cd /path/to/fr_config
rez-pip install .

Remote Installation

To install directly from this Git repository:

pip install git+https://github.com/FloatingRockStudio/fr_config.git

or with rez:

rez-pip install git+https://github.com/FloatingRockStudio/fr_config.git

Loading Configuration

from fr_config import ConfigLoader

# Load config from a specific path
config = ConfigLoader("simple", "/path/to/project")

# Access configuration data
fps = config.value("fps")
resolution = config.value("resolution")
tools = config.value("tools", [])  # With default value if not specified in schema

# Get all configuration keys
keys = config.keys()
key_paths = config.key_paths()  # Returns nested paths like "/playblast_settings/overscan"

Loading Specific Variants/Tags

# Load development variant
config = ConfigLoader("simple", "/path", variant="dev")

# Load with specific tags, first one in list that matches is used (if no wip, then latest is used)
config = ConfigLoader("simple", "/path", tags=["wip", "latest"])

# Load multiple variants, this will load /path/prod.fr_config then /path/default.fr_config.
config = ConfigLoader("simple", "/path", variant=["prod", "default"])
# If a string is passed then it will automatically change to [variant, "default"]
config = ConfigLoader("simple", "/path", variant="prod")

Writing Configuration

from fr_config import ConfigWriter

# Create or modify a config
writer = ConfigWriter("simple", "/path/to/project")
writer.set_data("fps", 30.0)
writer.set_data("resolution", [1920, 1080])

writer.commit("Updated resolution and fps")  # Save changes to disk

Schema Definition

fr_config uses json5 files to store schema, this allows for comments to be added in complex schemas

{
    __version__: 1,
    fps: {type: "float", default: 24.0},
    resolution: {
        type: "array",
        length: 2,
        items: {type: "int"}
    },
    publish_defaults: {
        type: "object",
        publish_type: {type: "string", default: "Publish"},
        handles: {
            type: "array",
            items: {type: "int"},
            default: [0, 0]
        }
    },
    tools: {
        type: "array",
        cascade: "append",
        items: {type: "string"}
    }
}

Data Flow

fr_config follows a hierarchical resolution pattern:

  1. Path Resolution: Starting from the specified path, walks up the directory tree
  2. Config Discovery: Looks for .fr_config/<name>/<variant>/ directories
  3. Version Selection: Chooses the appropriate version based on tags (published > latest)
  4. Schema Loading: Loads and validates against the schema file
  5. Data Cascading: Merges configurations based on cascade and schema rules
  6. Value Resolution: Resolves environment variables and applies type structure

Directory Structure Example

project/
├── .fr_config/
│   └── simple/
│       └── default/
│           ├── v1.fr_config
│           ├── v2.fr_config
│           ├── v3.fr_config
│           └── .v2.published
├── shots/
│   └── seq001/
│       └── shot010/
│           └── .fr_config/
│               └── simple/
│                   ├── default/
│                   │   └── v1.fr_config
│                   └── animation/
│                       └── v1.fr_config
└── schema/
    └── simple.fr_schema

Data Cascade Example

Given the directory structure above, loading the animation variant config from shots/seq001/shot010 will:

  1. Load shots/seq001/shot010/.fr_config/simple/variant/v1.fr_config
  2. Load shots/seq001/shot010/.fr_config/simple/default/v1.fr_config
  3. Load project/.fr_config/simple/default/v2.fr_config (published version)
  4. Merge them according to cascade rules

Configuration File Format

fr_config files use JSON5 format with special keys:

{
    // Reference parent config for inheritance
    "$parent": "${FR_PROJECTS_DIR}/../shared_config",

    // Regular configuration data
    fps: 30.0,
    resolution: [1920, 1080],

    // Complex cascading example
    environment_variables: {
        PATH: {
            cascade: "append",
            separator: "%pathsep%",  // Platform-specific path separator
            value: "/additional/bin/path"
        }
    },

    // Array with uniqueness constraints
    packages: {
        cascade: "append",
        unique: true,
        value: ["new-package-1.0+", "another-package-2.1+"]
    }
}

Schema System

Data Types

  • string: Text values
  • int: Integer numbers
  • float: Floating-point numbers
  • bool: Boolean true/false
  • array: Lists of values
  • object: Nested structures

Cascade Modes

  • replace: Replace entire value (default for primitives)
  • update: Merge object keys (default for objects)
  • append: Add to end of array/string
  • prepend: Add to beginning of array/string

Custom Types

Define reusable types in schema:

{
    __types__: {
        package: {
            type: "string",
            compare: "^([_a-zA-Z0-9]+)-.*"  // Regex for uniqueness comparison
        },
        environ: {
            type: "string",
            separator: "%pathsep%"
        }
    },

    packages: {
        type: "array",
        cascade: "append",
        unique: true,
        items: {type: "package"}
    },

    PATH: {type: "environ"}
}

Environment Setup

Set environment variables for schema and root path discovery:

# Path to schema files
export FR_CONFIG_SCHEMA_PATH="/path/to/schemas:/another/schema/path"

# Root paths that terminate config hierarchy traversal
export FR_CONFIG_ROOT_PATHS="/projects/root:/shared/configs"

About

Cascading Configurations for VFX Pipelines

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages