import functools import pathlib import attr import click @attr.s class CommandSpec: module = attr.ib() function = attr.ib() input_files = attr.ib() output_files = attr.ib() class ScriptsCLI(click.MultiCommand): @functools.lru_cache(None) def _collect_commands(commands_path): command_dicts = toml.load(pathlib.Path(commands_path).read_text())["commands"] command_specs = [CommandSpec(d) for d in command_dicts] return {spec.name: spec for spec in command_specs} def list_commands(self, ctx): specs = self._collect_commands(ctx.obj.config_dir / "commands.toml") return sorted(specs.keys()) def get_command(self, ctx, name): specs = self._collect_commands(ctx.obj.config_dir / "commands.toml") spec = specs[name] module = importlib.import_module(spec.module) command = getattr(module, spec.function) return command @click.group() @click.pass_context def cli(ctx): ctx.obj.config_dir = 'THE_DIR' pass @cli.command(cls=ScriptsCLI) @click.pass_context def hello(ctx): pass if __name__ == "__main__": cli() # File "cli3.py", line 38, in cli # ctx.obj.config_dir = 'THE_DIR' # AttributeError: 'NoneType' object has no attribute 'config_dir'