Coverage for src/footman/__init__.py: 100%
44 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-24 13:38 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-24 13:38 +0000
1"""footman — a task runner with typed commands and instant completion.
3Typed function signatures become real flags and positionals, modules become
4nested command groups, and shell completion answers from a cached manifest
5without importing your code.
7The console-script entry lives here and is deliberately thin: completion must
8dispatch to the stdlib-only hot path before importing the framework or the
9user's tasks, so `main` checks `--complete` first and everything else is
10imported lazily. A bare `import footman` pays for nothing but this module.
11"""
13from __future__ import annotations
15from typing import TYPE_CHECKING
17if TYPE_CHECKING:
18 # Give type-checkers the real types for the lazily re-exported names below;
19 # at runtime these are served by `__getattr__` without importing registry
20 # on a bare `import footman` (the completion hot path).
21 from footman import tools as tools
22 from footman._fetch import FetchError as FetchError
23 from footman._fetch import fetch as fetch
24 from footman.app import App as App
25 from footman.app import Brand as Brand
26 from footman.compose import include as include
27 from footman.compose import plugin as plugin
28 from footman.context import Context as Context
29 from footman.context import Failed as Failed
30 from footman.context import Result as Result
31 from footman.context import RunFailed as RunFailed
32 from footman.context import confirm as confirm
33 from footman.context import fail as fail
34 from footman.context import inherited as inherited
35 from footman.context import parallel as parallel
36 from footman.context import passthrough as passthrough
37 from footman.context import progress as progress
38 from footman.context import prompt as prompt
39 from footman.context import run as run
40 from footman.context import select as select
41 from footman.context import track as track
42 from footman.context import use_context as use_context
43 from footman.params import Exists as Exists
44 from footman.params import Forward as Forward
45 from footman.params import IsDir as IsDir
46 from footman.params import IsFile as IsFile
47 from footman.params import Many as Many
48 from footman.params import NoSplit as NoSplit
49 from footman.params import ask as ask
50 from footman.params import between as between
51 from footman.params import check as check
52 from footman.params import doc as doc
53 from footman.params import env as env
54 from footman.params import exists as exists
55 from footman.params import forward as forward
56 from footman.params import isdir as isdir
57 from footman.params import isfile as isfile
58 from footman.params import nosplit as nosplit
59 from footman.params import suggest as suggest
60 from footman.registry import Group as Group
61 from footman.registry import Tasks as Tasks
62 from footman.registry import TaskView as TaskView
63 from footman.registry import capture as capture
64 from footman.registry import finalize as finalize
65 from footman.registry import group as group
66 from footman.registry import requires as requires
67 from footman.registry import requires_dep as requires_dep
68 from footman.registry import requires_env as requires_env
69 from footman.registry import requires_tool as requires_tool
70 from footman.registry import task as task
71 from footman.testing import Runner as Runner
72 from footman.testing import recording as recording
74__version__ = "0.19.0"
75__all__ = [
76 "App",
77 "Brand",
78 "Context",
79 "Exists",
80 "Failed",
81 "FetchError",
82 "Forward",
83 "Group",
84 "IsDir",
85 "IsFile",
86 "Many",
87 "NoSplit",
88 "Result",
89 "RunFailed",
90 "Runner",
91 "TaskView",
92 "Tasks",
93 "__version__",
94 "ask",
95 "between",
96 "capture",
97 "check",
98 "confirm",
99 "doc",
100 "docstrings",
101 "env",
102 "exists",
103 "fail",
104 "fetch",
105 "finalize",
106 "forward",
107 "group",
108 "include",
109 "inherited",
110 "isdir",
111 "isfile",
112 "main",
113 "markdown",
114 "nosplit",
115 "parallel",
116 "passthrough",
117 "plugin",
118 "progress",
119 "prompt",
120 "recording",
121 "requires",
122 "requires_dep",
123 "requires_env",
124 "requires_tool",
125 "run",
126 "select",
127 "suggest",
128 "task",
129 "tools",
130 "track",
131 "use_context",
132]
135def main() -> None:
136 """Console-script entry for `footman` and `fm`."""
137 import sys
139 argv = sys.argv[1:]
140 if argv and argv[0] == "--complete":
141 from footman._complete import complete_cli
143 raise SystemExit(complete_cli(argv[1:]))
144 from footman.app import App
146 raise SystemExit(App().run(argv))
149def __getattr__(name: str) -> object:
150 # Lazy re-export: `from footman import task, group` works without paying the
151 # registry import on a bare `import footman` (the completion hot path).
152 if name in (
153 "task",
154 "group",
155 "Group",
156 "capture",
157 "finalize",
158 "Tasks",
159 "TaskView",
160 "requires",
161 "requires_dep",
162 "requires_env",
163 "requires_tool",
164 ):
165 from footman import registry
167 return getattr(registry, name)
168 if name in ("Runner", "recording"):
169 from footman import testing
171 return getattr(testing, name)
172 if name == "tools":
173 import footman.tools
175 return footman.tools
176 if name == "docstrings":
177 import footman.docstrings
179 return footman.docstrings
180 if name == "markdown":
181 import footman.markdown
183 return footman.markdown
184 if name in ("fetch", "FetchError"):
185 from footman import _fetch
187 return getattr(_fetch, name)
188 if name in ("include", "plugin"):
189 from footman import compose
191 return getattr(compose, name)
192 if name in (
193 "suggest",
194 "Many",
195 "nosplit",
196 "exists",
197 "isfile",
198 "isdir",
199 "between",
200 "env",
201 "check",
202 "doc",
203 "ask",
204 "forward",
205 "Forward",
206 "NoSplit",
207 "Exists",
208 "IsFile",
209 "IsDir",
210 ):
211 from footman import params
213 return getattr(params, name)
214 if name in (
215 "run",
216 "parallel",
217 "Context",
218 "Result",
219 "inherited",
220 "passthrough",
221 "progress",
222 "prompt",
223 "confirm",
224 "select",
225 "track",
226 "RunFailed",
227 "Failed",
228 "fail",
229 "use_context",
230 ):
231 from footman import context
233 return getattr(context, name)
234 if name in ("App", "Brand"):
235 from footman import app
237 return getattr(app, name)
238 raise AttributeError(f"module 'footman' has no attribute {name!r}")