Skip to content

Generator

The base class for every grace generate subcommand, plus the discovery function that registers them. See Writing Generators for a full guide.

from grace.generator import Generator


class MyGenerator(Generator):
    NAME = "my_generator"

    def generate(self, *args, **kwargs):
        ...


def generator() -> Generator:
    return MyGenerator()

grace.generator.Generator

Generator()

Bases: Command

Base class for implementing a generator command.

This class provides a foundation for defining generator commands that use Cookiecutter to generate project templates. It includes methods for template generation, validation, and argument handling.

Attributes: - NAME: The name of the generator command (must be defined by subclasses). - OPTIONS: A dictionary of additional Click options for the command.

Ensures that the NAME attribute is defined by the subclass. Registers the command with Click using the specified name and options.

RAISES DESCRIPTION
GeneratorError

If the NAME attribute is not defined.

Source code in grace/generator.py
83
84
85
86
87
88
89
90
91
92
93
94
def __init__(self):
    """Ensures that the `NAME` attribute is defined by the subclass.
    Registers the command with Click using the specified name and options.

    :raises GeneratorError: If the `NAME` attribute is not defined.
    """
    self.app: Application | None = None

    if not self.NAME:
        raise GeneratorError("Generator name must be defined.")

    super().__init__(self.NAME, callback=self._generate, **self.OPTIONS)

NAME class-attribute instance-attribute

NAME = None

OPTIONS class-attribute instance-attribute

OPTIONS = {}

app instance-attribute

app = None

templates_path property

templates_path

invoke

invoke(ctx)
Source code in grace/generator.py
100
101
102
def invoke(self, ctx):
    self.app = ctx.obj.get("app")
    return super().invoke(ctx)

generate

generate(*args, **kwargs)

Generates template.

PARAMETER DESCRIPTION
args

The positional arguments passed to the command.

DEFAULT: ()

kwargs

The keyword arguments passed to the command

DEFAULT: {}

RAISES DESCRIPTION
NotImplementedError

If the method is not implemented by the subclass.

Source code in grace/generator.py
104
105
106
107
108
109
110
111
112
def generate(self, *args, **kwargs):
    """Generates template.

    :param args: The positional arguments passed to the command.
    :param kwargs: The keyword arguments passed to the command

    :raises NotImplementedError: If the method is not implemented by the subclass.
    """
    raise NotImplementedError

validate

validate(*args, **kwargs)

Validates the arguments passed to the command.

Source code in grace/generator.py
119
120
121
def validate(self, *args, **kwargs):
    """Validates the arguments passed to the command."""
    return True

generate_template

generate_template(template_dir, variables={}, output_dir='')

Generate a template using Cookiecutter.

Renders template_dir (a subdirectory of templates_path) with the given variables, writing the result into output_dir (defaults to the current working directory). Returns the path to the generated project directory.

Example
self.generate_template(
    "project",
    variables={"project_name": "my-bot"},
    output_dir="my-bot",
)
Source code in grace/generator.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def generate_template(
    self, template_dir: str, variables: dict[str, Any] = {}, output_dir: str = ""
):
    """Generate a template using Cookiecutter.

    Renders `template_dir` (a subdirectory of `templates_path`) with the given
    variables, writing the result into `output_dir` (defaults to the current
    working directory). Returns the path to the generated project directory.

    ## Example

    ```python
    self.generate_template(
        "project",
        variables={"project_name": "my-bot"},
        output_dir="my-bot",
    )
    ```
    """
    template = str(self.templates_path / template_dir)
    return cookiecutter(
        template, extra_context=variables, no_input=True, output_dir=output_dir
    )

generate_file

generate_file(template_dir, variables={}, output_dir='')

Generate a module using jinja2 template.

PARAMETER DESCRIPTION
template_dir

The name of the template to generate.

TYPE: str

variables

The variables to pass to the template. (default is {})

TYPE: dict[str, Any] DEFAULT: {}

output_dir

The output directory for the generated template. (default is None)

TYPE: str DEFAULT: ''

Source code in grace/generator.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def generate_file(
    self, template_dir: str, variables: dict[str, Any] = {}, output_dir: str = ""
):
    """Generate a module using jinja2 template.

    :param template_dir: The name of the template to generate.
    :type template_dir: str

    :param variables: The variables to pass to the template.
                      (default is {})
    :type variables: dict[str, Any]

    :param output_dir: The output directory for the generated template.
                       (default is None)
    :type output_dir: str
    """
    env = Environment(
        loader=PackageLoader("grace", str(self.templates_path / template_dir)),
        extensions=["jinja2_strcase.StrcaseExtension"],
    )

    env.filters["camel_case_to_space"] = _camel_case_to_space
    env.filters["pluralize"] = lambda w: inflect.engine().plural(w)

    if not env.list_templates():
        raise NoTemplateError(f"No templates found in {template_dir}")

    template_file = env.list_templates()[0]
    template = env.get_template(template_file)

    rendered_filename = env.from_string(template_file).render(variables)
    rendered_content = template.render(variables)

    with open(f"{output_dir}/{rendered_filename}", "w") as file:
        file.write(rendered_content)

grace.generator.register_generators

register_generators(command_group)

Registers generator commands to the given Click command group.

This function dynamically imports all modules in the grace.generators package and registers each module's generator command to the provided command_group.

PARAMETER DESCRIPTION
command_group

The Click command group to register the generators to.

TYPE: Group

Source code in grace/generator.py
39
40
41
42
43
44
45
46
47
48
49
50
51
def register_generators(command_group: Group):
    """Registers generator commands to the given Click command group.

    This function dynamically imports all modules in the `grace.generators` package
    and registers each module's `generator` command to the provided `command_group`.

    :param command_group: The Click command group to register the generators to.
    :type command_group: Group
    """
    from grace import generators

    for module in import_package_modules(generators, shallow=False):
        command_group.add_command(module.generator())