I am looking for some advice to avoid having to instantiate a class twice; this is more of a design pattern question. I am creating an application using the Python Click library.
I have a Settings
class that first loads all initial default settings into a dictionary (hard-coded into the application), then loads all settings overrides (if specified) from a TOML file on the user's computer into a dictionary, and then finally merges the two and makes them available as attributes of the class instance (settings.<something>
).
For most of these settings, I also want to be able to specify a command-line flag. The priority then becomes:
- Command-line flag. If not specified, then fallback to...
- User setting in TOML file. If not specified, then finally fallback to...
- Application default
In order to achieve this result, I am finding that, when using Click's decorators, I have to do something like this:
import click
from myapp import Settings
settings = Settings()
pass_settings = click.make_pass_decorator(Settings, ensure=True)
@click.command()
@click.help_option('-h', '--help')
@click.option(
'-s', '--disk-size',
default=settings.instance_disk_size,
help="Disk size",
show_default=True,
type=int
)
@click.option(
'-t', '--disk-type',
default=settings.instance_disk_type,
help="Disk type",
show_default=True,
type=click.Choice(['pd-standard', 'pd-ssd'])
)
@pass_settings
def create(settings, disk_size, disk_type):
print(disk_size)
print(disk_type)
Why twice?
- The
settings = Settings()
line is needed to provide the@click.option
decorators with thedefault
value. Thedefault
value could either come from the user override TOML file (if present), or from the application default. - The click.make_pass_decorator seems to be the recommended way for interleaved commands; it's even mentioned in their documentation. Inside of the function, in addition to the CLI parameters passed, I also sometimes needs to reference other attributes in the
Settings
class.
My question is, which is better? Is there a way to use the pass_settings
decorator in the other click.option
decorators? Or should I ditch using click.make_pass_decorator
entirely?