Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commands - dot-prefixed, stdlib only #367

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

brenns10
Copy link
Contributor

@brenns10 brenns10 commented Nov 9, 2023

This is another stab at the idea of commands in drgn.

Rather than using IPython like the first PR I shared, this one is based on the stdlib only. It integrates into the existing drgn CLI via a subclass of InteractiveConsole. Commands are simply a callable, since I didn't want to get too opinionated with a class-based solution, or even requiring the use of argparse.

Commands in the CLI are prefixed by a . . No valid Python statement starts with that, so this shouldn't break any existing behavior. They are able to return results just like normal expressions, and they can access the locals too.

The drgn.cli module has a registry of commands, which can be added to using the @command("name") decorator. In order to ensure that user/third-party commands can be loaded, I've added a drgn.command.v1 entry point. Drgn automatically loads modules from that entry point prior to starting the CLI. (This is notable from a security standpoint. However, if you're running drgn as root, you would have needed to install that entry point as root as well.)

Just like globals and everything else, commands can be customized in run_interactive().

I've added three useful commands into the default set. These aren't the only ones I would be interested in adding, just a demonstration.

  • .x - execute a script (basically a wrapper on execscript)
  • .let - set a variable equal to a drgn object
  • .contrib - run a contrib script by name

I also added support to the ptdrgn.py script because I use that constantly.

In any case, I hope that the code is pretty straightforward. I think this is the most simple and direct way I could come up with to do commands in drgn.

brenns10 added a commit to brenns10/drgn-tools that referenced this pull request Nov 9, 2023
This implements a "cl" command using the drgn CLI command framework
proposed in osandov/drgn#367. The "cl" command invokes Corelens modules
as sub-commands.

Signed-off-by: Stephen Brennan <[email protected]>
@brenns10
Copy link
Contributor Author

I fixed the Python 3.6-3.9 mypy failure which was present in the previous revision, but of course since I updated the pre-commit, you won't actually get a mypy run on 3.6 now. I don't have permissions to add the label to the PR, but it would be good to have CI run against all Python versions, just to verify. Of course I tested it locally on 3.9 and it worked.

I also rebased this on main. I even checked this in combination with the #364 changes and they don't conflict. With those changes, it's pretty impressive how much less verbose the command line is!

@osandov osandov added the test-all-python-versions Run tests on all supported Python versions label Nov 30, 2023
@brenns10
Copy link
Contributor Author

brenns10 commented Dec 1, 2023

Very funny, I didn't realize that when Github Actions runs, it does an automatic merge with the main branch and then runs tests. I got failures from #364 because I guess the run happened in the brief window between the merge and the fix :)

As a debugger library, drgn can be a little bit verbose when running
interactively. There has been general concensus that it would be nice to
have a way to define shorthands for common operations, or define user
scripts that are easy to run within the shell. However we have yet to
settle upon a solution. Implementing a full CLI seems out of the scope
of drgn. Leveraging the power of a system like IPython's "magic"
commands is promising, but adds heavy dependencies.

One example CLI to draw inspiration from is SQLite, which uses the "."
prefix to define meta-commands that aren't part of the SQL language.
Python, like SQL, cannot begin a statement with a ".", so it could be
used to unambiguously signal that a command is being entered, and thanks
to the code.InteractiveConsole, we can easily extend the console to
handle these commands.

So, let's add a command system. Commands are simply callables which take
a program, the full line of text (which may contain arguments), and the
CLI locals. They may return a Python object which is stored to
`builtins._`. They can be registered using a decorator, and an API is
available to retrieve the default list. The run_interactive() function
allows the user to customize the available command list.

Signed-off-by: Stephen Brennan <[email protected]>
The execscript() helper is intended to run from the CLI and access the
calling scope's global variables. However, for CLI commands to use it,
execscript() must optionally accept a global variable dict directly.

Signed-off-by: Stephen Brennan <[email protected]>
This helper allows users to evaluate very basic C expressions of the
form "(type)int_literal". This enables users to more easily copy and
paste printed results back into the console to get the corresponding
object.

Signed-off-by: Stephen Brennan <[email protected]>
The "x" command is short for "eXecute" or execscript(). The "contrib"
command is similar to "x", but runs scripts from the "contrib/"
directory if it is present. The "let" command sets variables based on
the typed expression.

Signed-off-by: Stephen Brennan <[email protected]>
@brenns10
Copy link
Contributor Author

brenns10 commented Oct 25, 2024

Since you mentioned it Wednesday, I thought I'd update the command pull request here. If you wanted to just assemble your own system from the idea (overriding InteractiveInterpreter.runsource()) that's absolutely fine, but if you wanted these patches in some form, now they're at least rebased. I also updated the completer code so that it would tab-complete the command names, which is pretty convenient.

The actual commands themselves from this pull request aren't too important. Though I do really like the let command, I would be using it constantly.

I do think the major missing piece here is that, if we were going to have a command system, there should be a separate page in the documentation with a description of each command. Ideally it could be somehow generated from the docstring of each command function, but presented as something else. I'm not sure how that would work yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
test-all-python-versions Run tests on all supported Python versions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants