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 |
ποΈ 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 withNone
valuesupper
: 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
andrich
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:
- Import the specified module
- Access the specified class (e.g.,
Dev
) - 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