Skip to content

Settings and Configuration

Sayer provides a built-in way to configure your CLI application using centralized settings, powered by the SAYER_SETTINGS_MODULE environment variable.

This mechanism lets you:

  • Keep config logic out of command code
  • Load settings dynamically
  • Easily override settings in dev, test, and prod environments

โš™๏ธ Default Settings Class

Sayer includes a built-in Settings dataclass that defines the default configuration used when no SAYER_SETTINGS_MODULE is set, or when internal tools need immediate settings access.

This class covers:

  • Debug mode and logging
  • Output color systems
  • Version tagging
  • Serialization utilities

๐Ÿ“ฆ Location

from sayer.conf.settings import Settings

This is not to be confused with your application-level settings โ€” it is used internally and as a base for custom configurations.


๐Ÿงพ Default Fields

Field Type Description
debug bool Enables debug mode (more logging, errors)
logging_level str Logging threshold (DEBUG, INFO, etc.)
version str Version of the Sayer library (auto-filled)
is_logging_setup bool Tracks if logging has already been configured
force_terminal bool | None Force terminal output regardless of environment
color_system "auto" | "standard" | "256" | ... Controls terminal color profile
display_full_help bool Flag indicating the the display of each command must be diplayed in full
display_help_length int The length of the help if display_full_help is set to False. Defaults to 99
formatter_class RichHelpFormatter The formatter used to render the Sayer Rich UI. This can be overridden by any custom renderer.

๐ŸŽ›๏ธ Field Examples

settings.debug = True
settings.logging_level = "DEBUG"
settings.force_terminal = True
settings.color_system = "256"

๐Ÿง  logging_config Property

This is a dynamic property that returns a LoggingConfig object based on the current logging level.

config = settings.logging_config

Sayer uses this internally to configure rich/standard logging output. You can override it:

from sayer.logging import CustomLoggingConfig

settings.logging_config = CustomLoggingConfig(level="DEBUG")

๐Ÿ”„ Converting to Dict or Tuples

The Settings class provides two helpers to introspect or export the config:

dict(...)

settings.dict(exclude_none=True, upper=True)

Args:

  • exclude_none: Omit fields with None values
  • upper: Transform keys to uppercase (useful for env-like exports)

Returns:

{
  "DEBUG": True,
  "LOGGING_LEVEL": "INFO",
  ...
}

tuple(...)

settings.tuple(exclude_none=True, upper=False)

Returns:

[("debug", True), ("logging_level", "INFO"), ...]

Useful for exporting to .env files or logging config introspection.


๐Ÿงฐ Internal Use

The Settings class is primarily used:

  • As the fallback configuration if SAYER_SETTINGS_MODULE is not defined
  • By components like sayer.logging and rich terminal integration
  • To bootstrap internal state when settings aren't user-defined

๐Ÿงช Customizing in Tests

You can use it in test environments or override fields dynamically:

from sayer.conf.global_settings import Settings

s = Settings(debug=True, logging_level="DEBUG")
print(s.dict())

๐Ÿงฌ Summary

Feature Supported
Built-in debug mode โœ…
Color and terminal control โœ…
Logging abstraction โœ…
Dict export โœ…
Tuple export โœ…
Used in fallback mode โœ…

This class is the core config contract Sayer uses internally โ€” you can use it as a reference to build more advanced or environment-specific config systems.


๐Ÿ“ฆ How It Works

At runtime, Sayer looks for the environment variable:

SAYER_SETTINGS_MODULE=your_project.settings.Dev

It will:

  1. Import the specified module
  2. Access the specified class (e.g., Dev)
  3. Instantiate and expose it as settings anywhere you need

๐Ÿง  Anatomy of a Settings Module

# your_project/settings.py
from sayer.conf.global_settings import BaseSettings

class Base(BaseSettings):
    debug = False
    retries = 3

class Dev(Base):
    debug = True
    retries = 10

๐Ÿš€ Setting the Module

You must set the environment variable before launching the CLI:

export SAYER_SETTINGS_MODULE=your_project.settings.Dev
python app.py my-command

You can also set it inline:

SAYER_SETTINGS_MODULE=your_project.settings.Dev python app.py my-command

๐Ÿงช Accessing Settings in Commands

from sayer.conf import settings

@command()
def show():
    print("Debug mode:", settings.debug)

No need to pass it explicitly โ€” it's global, and lazily loaded on first access.


๐Ÿงฐ Settings Best Practices

1. Split environments:

# settings.py
from dataclasses import dataclass
from sayer.conf.global_settings import BaseSettings

class Base(BaseSettings):
    retries = 3

class Dev(Base):
    debug = True

class Prod(Base):
    debug = False
    retries = 1

Use different envs like:

SAYER_SETTINGS_MODULE=myapp.settings.Dev

2. Type Your Settings

Sayer doesn't enforce typing but it's recommended:

class Dev:
    debug: bool = True
    retries: int = 3

You can combine this with Pydantic, attrs, or msgspec for stricter validation if desired.


๐Ÿงช Overriding Settings in Tests

Tests that rely on settings should override the environment variable or monkey-patch sayer.conf.settings.

Example:

import os
from sayer.conf import settings

def test_behavior(monkeypatch):
    monkeypatch.setenv("SAYER_SETTINGS_MODULE", "tests.fake_settings.Mocked")
    assert settings.debug is True

Or if settings is already imported:

settings.debug = True  # override directly for testing

๐Ÿง  Under the Hood

If SAYER_SETTINGS_MODULE is unset, it will use the internal by default or if invalid, Sayer will raise a helpful exception.


๐Ÿงฐ Recap

Feature Description
SAYER_SETTINGS_MODULE Points to your settings class
settings Auto-loaded global settings object
Lazy loading Only instantiated when accessed
Nested inheritance Use Base, Dev, Prod hierarchies
Test override Patch env var or mutate settings in test

โœ… Example Project Structure

myapp/
โ”œโ”€โ”€ settings.py
โ”‚   โ”œโ”€โ”€ class Base
โ”‚   โ”œโ”€โ”€ class Dev(Base)
โ”‚   โ””โ”€โ”€ class Prod(Base)
โ”œโ”€โ”€ commands.py
โ””โ”€โ”€ main.py

Set it:

export SAYER_SETTINGS_MODULE=myapp.settings.Dev

Access it:

from sayer.conf import settings
print(settings.retries)

๐Ÿ‘‰ Next: Commands