Hi, I'm building my first plugin now which needs t...
# plugins
h
Hi, I'm building my first plugin now which needs to create a digest for its own path I can technically use
__file__
to do that, but that's not very elegant Is there a builtin way of doing that? Or maybe at least access
GLOBAL.pythonpath
from
pants.toml
?
c
Hi, you can access global options from you plugin (but I don't follow how
GLOBAL.pythonpath
would be able to substitute for
__file__
..?) See https://www.pantsbuild.org/2.21/docs/writing-plugins/the-rules-api/options-and-subsystems and for example on using global options: https://github.com/pantsbuild/pants/blob/71749c04c7cf3cd9c22aea3e99fded57d5f41ebc/src/python/pants/core/util_rules/distdir.py#L29-L31 It is a good idea to have a rule, as in the case above, that extracts the option you care about, and depend on that value only from your other rules that require it. The reason being that if you depend on
GlobalOptions
directly, your rule will be invalidated on any change to any global option, even if it doesn't affect your rule at all. Isolating the option extraction like this, will still invalidate the option extraction rule, but if the option you care about is unchanged, then it will not invalidate further.
h
Thanks for pointing me in the right direction It actually didn't occur to me that Pants was also using the same API internally, so reading the source code has actually been an eye-opener for me... It's a shame that
GlobalOptions
and
BuildRoot
are not mentioned anywhere in the docs, though...
👍 1
Considering my options based on your comment and reading the source code, here's the solution I came up with
Copy code
@dataclass(frozen=True)
class OwnPath:
    """
    The directory of this plugin.
    """

    path: Path


@rule
async def own_path(buildroot: BuildRoot) -> OwnPath:
    own_root = Path(__file__).parent
    build_root = buildroot.pathlib_path
    return OwnPath(
        own_root.relative_to(build_root) if own_root.is_absolute() else own_root
    )
Does it look correct?
c
Seems reasonable to me 🙂
🙂 1
h
Going back to this solution, now trying to write tests for
own_path
rule, I'm experiencing an issue because
own_root
is located at
/tmp/pants-sandbox-...
and
build_root
is
/tmp/_BUILD_ROOT...
so
own_root.relative_to(build_root)
fails I tried to patch
buildroot.pathlib_path
but it doesn't seem to work... This rule is tested as part of a goal using
run_goal_rule()
Any idea what could be done here?
h
Yeah, that can’t work, for exactly the reason you’re seeing. The build root is the workspace, but your code is running in a sandbox.
Let’s see…
You want a path in the workspace (it is workspace files that can be digested)
Hmm, I guess you could run your code in a “workspace environment”, @fast-nail-55400’s new feature that allows ad_hoc tools to run there.
Oh wait, sorry, your processes run in a sandbox, but this is just rule code
so unclear to me why
own_root
is in a sandbox
Are you spawning a process somewhere here?
h
I'm using both
run_rule_with_mocks()
and
run_goal_rule()
in two separate tests The logic of the rule relies on
__file__
because we need to find where the plugin files are stored, and we need that path to be relative to
build_root
for obvious reasons
I ended up patching
__file__
and it worked, but I'm not happy with this solution
Copy code
patch('plugin.rule_file.__file__', f'{rule_runner.build_root}/plugin_dir')
h
Ah, so this is in the tests for your plugin. Those are indeed running in a process (the
pytest
process).
So you need to set up a fake workspace inside the sandbox.
Many of Pants’s own tests do this
h
Are you talking about this? https://www.pantsbuild.org/stable/docs/using-pants/environments Do you have any example of tests that do this and I can learn from?
h
These are two different things: environments is one, but with the clarification that we’re talking about tests, are probably less relevant. What you need is an integration test that runs a copy of Pants, with your plugin, in a subprocess. For a very simple example of setting up a source tree in a sandbox: src/python/pants/backend/project_info/peek_integration_test.py
But there are dozens all over the codebase.
Of course in your case you will have to copy the plugin code into the sandbox, so that the Pants subprocess can pick it up from there
So you won’t be using
run_*
rules in your test, you will be invoking a whole new Pants instance
That is necessary to test “plugin is loaded from a specific source location inside the sandbox”, which is not a common requirement
h
Alright, thanks! I'll take a look