Coverage for src/lektor_ng/devcli.py: 63%
49 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-08 12:57 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-08 12:57 +0000
1# pylint: disable=import-outside-toplevel
2import os
3import sys
5import click
7from lektor_ng.cli_utils import AliasedGroup, extraflag, pass_context
9try:
10 from IPython import embed
11 from traitlets.config.loader import Config
12except ImportError:
13 pass # fallback to normal Python InteractiveConsole
16@click.group(cls=AliasedGroup, short_help="Development commands.")
17def cli():
18 """Development commands for Lektor.
20 This provides various development support commands for Lektor. This is
21 primarily useful for Lektor plugin development but also if you want to
22 extend Lektor itself. Additional functionality can be unlocked by
23 exporting the `LEKTOR_DEV=1` environment variable.
24 """
27@cli.command("shell", short_help="Starts a python shell.")
28@extraflag
29@pass_context
30def shell_cmd(ctx, extra_flags):
31 """Starts a Python shell in the context of a Lektor project.
33 This is particularly useful for debugging plugins and to explore the
34 API. To quit the shell just use `quit()`. Within the shell various
35 utilities are available right from the get-go for you.
37 \b
38 - `project`: the loaded project as object.
39 - `env`: an environment for the loaded project.
40 - `pad`: a database pad initialized for the project and environment
41 that is ready to use.
42 """
43 ctx.load_plugins(extra_flags=extra_flags)
44 import code
46 from lektor.builder import Builder
48 from lektor_ng.db import F, Tree
50 banner = f"Python {sys.version} on {sys.platform}\nLektor Project: {ctx.get_env().root_path}"
51 ns = {}
52 startup = os.environ.get("PYTHONSTARTUP")
53 if startup and os.path.isfile(startup):
54 with open(startup, encoding="utf-8") as f:
55 eval(compile(f.read(), startup, "exec"), ns) # pylint: disable=eval-used
56 pad = ctx.get_env().new_pad()
57 ns.update(
58 project=ctx.get_project(),
59 env=ctx.get_env(),
60 pad=pad,
61 tree=Tree(pad),
62 config=ctx.get_env().load_config(),
63 make_builder=lambda: Builder(ctx.get_env().new_pad(), ctx.get_default_output_path()),
64 F=F,
65 )
66 try:
67 c = Config()
68 c.TerminalInteractiveShell.banner2 = banner
69 embed(config=c, user_ns=ns)
70 except NameError: # No IPython
71 code.interact(banner=banner, local=ns)
74@cli.command("new-plugin", short_help="Creates a new plugin")
75@click.option("--path", type=click.Path(), help="The destination path")
76@click.argument("plugin_name", required=False)
77@pass_context
78def new_plugin(ctx, **defaults):
79 """This command creates a new plugin.
81 This will present you with a very short wizard that guides you through
82 creation of a new plugin. At the end of it, it will create a plugin
83 in the packages folder of the current project or the path you defined.
85 This is the fastest way to creating a new plugin.
86 """
87 from .quickstart import plugin_quickstart
89 project = ctx.get_project(silent=True)
90 plugin_quickstart(defaults, project=project)
93@cli.command("new-theme", short_help="Creates a new theme")
94@click.option("--path", type=click.Path(), help="The destination path")
95@click.argument("theme_name", required=False)
96@pass_context
97def new_theme(ctx, **defaults):
98 """This command creates a new theme.
100 This will present you with a very short wizard that guides you through
101 creation of a new theme. At the end of it, it will create a theme
102 in the packages folder of the current project or the path you defined.
104 This is the fastest way to creating a new theme.
105 """
106 from .quickstart import theme_quickstart
108 project = ctx.get_project(silent=True)
109 theme_quickstart(defaults, project=project)