Skip to content

Commit b46b178

Browse files
authored
Add introspection command (#228)
Partially addresses #226
2 parents a2becb3 + d969ed4 commit b46b178

4 files changed

Lines changed: 86 additions & 11 deletions

File tree

README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,23 +86,23 @@ python -m spin
8686
Available as `spin.cmds.meson.*`.
8787

8888
```
89-
build 🔧 Build package with Meson/ninja
90-
ipython 💻 Launch IPython shell with PYTHONPATH set
91-
python 🐍 Launch Python shell with PYTHONPATH set
92-
shell 💻 Launch shell with PYTHONPATH set
93-
test 🔧 Run pytest
94-
run 🏁 Run a shell command with PYTHONPATH set
95-
docs 📖 Build Sphinx documentation
96-
gdb 👾 Execute a Python snippet with GDB
97-
lldb 👾 Execute a Python snippet with LLDB
89+
build 🔧 Build package with Meson/ninja
90+
ipython 💻 Launch IPython shell with PYTHONPATH set
91+
python 🐍 Launch Python shell with PYTHONPATH set
92+
shell 💻 Launch shell with PYTHONPATH set
93+
test 🔧 Run pytest
94+
run 🏁 Run a shell command with PYTHONPATH set
95+
docs 📖 Build Sphinx documentation
96+
gdb 👾 Execute a Python snippet with GDB
97+
lldb 👾 Execute a Python snippet with LLDB
9898
```
9999

100100
### [Build](https://pypa-build.readthedocs.io/en/stable/) (PEP 517 builder)
101101

102102
Available as `spin.cmds.build.*`:
103103

104104
```
105-
sdist 📦 Build a source distribution in `dist/`
105+
sdist 📦 Build a source distribution in `dist/`
106106
```
107107

108108
### [pip](https://pip.pypa.io) (Package Installer for Python)
@@ -113,7 +113,15 @@ development workflow.
113113
Available as `spin.cmds.pip.*`:
114114

115115
```
116-
install 💽 Build and install package using pip.
116+
install 💽 Build and install package using pip.
117+
```
118+
119+
### Meta (commands that operate on commands)
120+
121+
Available as `spin.cmds.meta.*`:
122+
123+
```
124+
introspect 🔍 Print a command's location and source code
117125
```
118126

119127
## 🧪 Custom commands

example_pkg/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ package = 'example_pkg'
4747
"Pip" = [
4848
"spin.cmds.pip.install"
4949
]
50+
"Meta" = [
51+
"spin.cmds.meta.introspect"
52+
]
5053

5154
[tool.spin.kwargs]
5255
".spin/cmds.py:example" = {"test" = "default override", "default_kwd" = 3}

spin/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,14 @@ def group(ctx):
132132

133133
try:
134134
cmd_func = getattr(mod, func)
135+
cmd_func.module = mod # metadata for use by `introspect` command
135136
except AttributeError:
136137
print(f"!! Could not load command `{func}` from file `{path}`.\n")
137138
continue
138139

140+
# Save command definition for use in `introspect`
141+
cmd_func.spec = cmd
142+
139143
default_kwargs = cmd_default_kwargs.get(cmd)
140144
import functools
141145

spin/cmds/meta.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import inspect
2+
3+
import click
4+
5+
from .util import get_commands, get_config
6+
7+
8+
def _highlight(src):
9+
from pygments import highlight
10+
from pygments.formatters import TerminalFormatter
11+
from pygments.lexers import PythonLexer
12+
13+
return highlight(src, PythonLexer(), TerminalFormatter())
14+
15+
16+
@click.command()
17+
@click.argument("cmd", nargs=1)
18+
def introspect(*, cmd):
19+
"""🔍 Print a command's location and source code."""
20+
cmds_by_section = get_commands()
21+
overrides = get_config().get("tool.spin.kwargs")
22+
23+
cmds = {}
24+
for section in cmds_by_section:
25+
cmds.update({cmd.name: cmd for cmd in cmds_by_section[section]})
26+
27+
if cmd not in cmds:
28+
raise SystemExit(f"Command `{cmd}` not found. Exiting.")
29+
30+
cmd_func = cmds[cmd]
31+
try:
32+
code = inspect.getsource(cmd_func.callback)
33+
except TypeError:
34+
# Perhaps a partial, try again
35+
code = inspect.getsource(cmd_func.callback.func)
36+
37+
try:
38+
code = _highlight(code)
39+
except ImportError:
40+
pass
41+
42+
print(code)
43+
44+
click.secho(
45+
f"The `{cmd}` command is defined as `{cmd_func.spec}`.",
46+
bold=True,
47+
fg="magenta",
48+
)
49+
50+
click.secho(
51+
f"The code is in `{cmd_func.module.__file__}`.", bold=True, fg="magenta"
52+
)
53+
54+
if cmd_func.spec in overrides:
55+
click.secho(
56+
"\nThe function has the following keyword overrides defined:\n",
57+
bold=True,
58+
fg="magenta",
59+
)
60+
print(" ", overrides[cmd_func.spec], "\n")

0 commit comments

Comments
 (0)