-
-
Notifications
You must be signed in to change notification settings - Fork 63
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
Initial implementation of Poetry plugin #32
Conversation
Should we notify if a command tries to/could've override Poetry default commands? like diff --git a/poethepoet/plugin.py b/poethepoet/plugin.py
index b6228d5..86a26eb 100644
--- a/poethepoet/plugin.py
+++ b/poethepoet/plugin.py
@@ -12,13 +12,9 @@ from . import config
def get_poe_task_names() -> Iterator[str]:
- return (
- name
- for name in tomlkit.loads(
- Path(config.PoeConfig().find_pyproject_toml()).read_text()
- )["tool"]["poe"]["tasks"].keys()
- if name not in app.COMMANDS
- )
+ yield from tomlkit.loads(
+ Path(config.PoeConfig().find_pyproject_toml()).read_text()
+ )["tool"]["poe"]["tasks"].keys()
def run_poe_task(name: str) -> int:
@@ -27,7 +23,13 @@ def run_poe_task(name: str) -> int:
class PoePlugin(ApplicationPlugin):
def activate(self, application: app.Application) -> None:
+ io = application.create_io()
for task_name in get_poe_task_names(): # Assume this function exists somewhere
+ if task_name in app.COMMANDS:
+ io.write_line(
+ f"<info>Skipped command creation for task '{task_name}'</info>"
+ )
+ continue
application.command_loader.register_factory(
task_name,
type( |
Interesting. Thanks for making this PoC. I thought it would be more complex to get something working (probably the plugin API I looked at is no longer current). I'll need to do quite a bit of testing before merging/releasing this (and I'm afraid I have other stuff to test first; currently I don't have a lot of time for this :( ), but I'm glad to see it's not too complicated to get working. I'm of two minds about whether it's better to namespace poe tasks (i.e. I'd prefer to keep poetry as an extra dependency (i.e. installed via The functionality you've demonstrated here might be enough for a first release that adds value, but ultimately some thought needs to go into how to best integrate poe CLI functionality with the poetry CLI, for consistently displaying help/errors etc. This is were I imagined most of the work/refactoring will be required, because poe doesn't currently use cleo. |
I realized that we probably can move this plugin functionality to a different PyPi package. I'm pretty sure when someone does
Check out my latest commit. I did more than just edit the
I see. Can't we just redirect subprocess's stdout and stderr some how? |
I did that in the latest commit (not so sure if I should use |
@nat-n can you review this? Also, I had a thought about separating plug-in (then I wouldn’t need to rely on some big hacks to make poetry an optional dependency) |
Hi @ThatXliner, Sorry for the delay. I will come back to this, it's an important feature. I'm afraid I don't expect to have time to give it enough focused attention for at least a couple more weeks. I'm afraid another project has been taking all my time for side projects. |
I guess I could make a plugin separately, as I said before… |
Hi @ThatXliner, sorry again for the delay. If you're dying to ship this then indeed it might be best to build it as a separate project. I absolutely want to have poetry plugin support built into poethepoet. However by current my assessment it's not a quick win; I need to take some time to get a deep understanding of the plugin architecture and conventions in poetry, and think about how to integrate it properly. Meanwhile my highest priority for this project at the moment is to stabilise the major new features that are currently in pre-release on the development branch so that they can be released as 0.11.0, and this might still take a while. That said it might be useful, when I'm ready to work on this, to have an example to base it on that's already been battle tested. |
Also if you maintain this PR then please target the development branch, as per the contributing guidelines |
ee1a1d7
to
5b9f257
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've finally gotten around to looking into this. Sorry for the delay. I anticipate having some time over the next few weeks to get it done (it's the highest priority major feature now after #45).
To start with I'd like your help to understand what you've done so far, to see what's there and what's missing.
io.error_output.fileno = sys.stdout.fileno | ||
stderr = io.error_output | ||
|
||
return subprocess.run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A subprocess incurs a performance penalty, and adds some uncertainty due to going via the PATH, complexity around passing args to tasks, etc. Any specific reason not to import and invoke the PoeThePoet class directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do that? I've never knew.
My thought was that poetry X
was just a simple shorthand for poe X
. To run the class, there's the IO stuff if we wanted tighter integration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Maybe the optimal solution would be attempt to use that undocumented API, and fallback to loading the config again upon failure.
|
||
|
||
def run_poe_task(name: str, io) -> int: | ||
io.fileno = sys.stdin.fileno # Monkey patches io because subprocess needs it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to redirect the io here instead of just letting the subprocess inherit sys.stdout/in/err etc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I said something here
The functionality you've demonstrated here might be enough for a first release that adds value, but ultimately some thought needs to go into how to best integrate poe CLI functionality with the poetry CLI, for consistently displaying help/errors etc. This is were I imagined most of the work/refactoring will be required, because poe doesn't currently use cleo.
I see. Can't we just redirect subprocess's stdout and stderr some how?
sys.exit(1) | ||
io = application.create_io() | ||
# Create seperate commands per task | ||
for task_name in get_poe_task_names(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is clever, but I wonder what happens if a task name happens to conflict with a native poetry command? Does it fail? does it silently override the poetry command for this project? Have you tried it?
It might be that this is a nice to have, but shouldn't be the default behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Does it fail?" yes. I have tried it.
It also says here:
A plugin must not remove or modify in any way the core commands of Poetry.
poethepoet/plugin.py
Outdated
def activate(self, application: app.Application) -> None: | ||
if app.Application is None: | ||
print( | ||
"Poetry not found. Did you install this plugin via `poetry plugin add poethepoet[poetry_plugin]`?" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When can this happen? Is there a known scenario that something other than poetry would attempt to activate this plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ( | ||
name | ||
for name in tomlkit.loads( | ||
Path(config.PoeConfig().find_pyproject_toml()).read_text() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't tried it but looking through the code it looks like there's a way to get the pyproject.toml content loaded by poetry without loading it again. Something like app.config.config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. But I wouldn't use anything undocumented. Made a discussion here
poethepoet/plugin.py
Outdated
# please mypy, | ||
# and please Poetry's plugin system (simulate the ApplicationPlugin object) | ||
app = type("application", (), {"Application": None}) # pylint: disable=C0103 | ||
ApplicationPlugin = type( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this try/except is just about making this module importable other than by poetry. Do you have a use case in mind for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. This module shouldn't be imported unless as a poetry plugin. Unless there is some wanted flexibility in the future? (But I guess we can always add it back)
Co-authored-by: nat <n@natn.me>
I had a quick play around with this implementation and made a few changes in a new PR. #46 If you'd like to continue to experiment and share ideas on this topic then we can keep this PR open, but id's suggest rebasing it onto the other way and updating it to target the branch of the other one. Otherwise we should close this one now. Or it might be easier to create a new PR of that nature since the rebase (which includes everything from the development branch) is non-trivial. Up to you @ThatXliner Thanks again for your work on this. |
…at-n#30) For example setting: [tool.poe.env] PORT.default = "9000" Will set the PORT variable on the environment, only if it isn't already set. Also replace dependency on more lightweight a complete toml parser: tomli
Rebased... I think |
…ence (nat-n#40) Allow two addition option value for `ingore_fail` option: - `return_zero`: works like true - `return_non_zero`: run all the subtasks regardless theirs exit status, but returns non-zero exit status if any of subtasks failed
…at-n#36) - Parse script task content as python using ast - Add support for arg types, including special behaviour for boolean type - Add documentation for named arguments - Improve error handling when parsing CLI arguments Testing & quality - improve organisation of test fixtures - reorganise tests by - add extensive tests for named arguments on script tasks - improve use of type anotations like Mapping Co-authored-by: NEXTHINK\mjaquier <michael.jaquier@nexthink.com> Co-authored-by: Nat Noordanus <n@natn.me>
Argh I dislike git |
72a2ccc
to
a8b8f94
Compare
I'm going to close this:
|
Closes #14