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

Initial implementation of Poetry plugin #32

Closed
wants to merge 15 commits into from

Conversation

ThatXliner
Copy link
Contributor

Closes #14

@ThatXliner ThatXliner marked this pull request as draft July 3, 2021 22:10
@ThatXliner ThatXliner marked this pull request as ready for review July 3, 2021 22:10
@ThatXliner
Copy link
Contributor Author

ThatXliner commented Jul 3, 2021

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(

@nat-n
Copy link
Owner

nat-n commented Jul 4, 2021

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. poetry poe test vs poetry test). Though maybe offering a plugin for each and letting the user decide would be reasonable. At least notifying the user if they've configured colliding task name would be in keeping with current design philosophy.

I'd prefer to keep poetry as an extra dependency (i.e. installed via pip install poethepoet[poetry_plugin] or so), because I've found poe is actually useful in contexts without poetry.

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.

@ThatXliner
Copy link
Contributor Author

ThatXliner commented Jul 4, 2021

I'm of two minds about whether it's better to namespace poe tasks (i.e. poetry poe test vs poetry test). Though maybe offering a plugin for each and letting the user decide would be reasonable. At least notifying the user if they've configured colliding task name would be in keeping with current design philosophy.

I realized that we probably can move this plugin functionality to a different PyPi package. I'm pretty sure when someone does pip install poethepoet, poethepoet is available as a library (due to Poetry's auto detection I think).

I'd prefer to keep poetry as an extra dependency (i.e. installed via pip install poethepoet[poetry_plugin] or so), because I've found poe is actually useful in contexts without poetry.

Check out my latest commit. I did more than just edit the pyproject.toml file.

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?

@ThatXliner
Copy link
Contributor Author

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 copy.copy or that the variable already copies the value)

@ThatXliner
Copy link
Contributor Author

ThatXliner commented Aug 21, 2021

@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)

@nat-n
Copy link
Owner

nat-n commented Aug 21, 2021

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.

@ThatXliner
Copy link
Contributor Author

I guess I could make a plugin separately, as I said before…

@nat-n
Copy link
Owner

nat-n commented Sep 26, 2021

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.

@nat-n
Copy link
Owner

nat-n commented Sep 26, 2021

Also if you maintain this PR then please target the development branch, as per the contributing guidelines

@ThatXliner ThatXliner changed the base branch from main to development September 26, 2021 21:32
@nat-n nat-n force-pushed the development branch 7 times, most recently from ee1a1d7 to 5b9f257 Compare November 14, 2021 20:18
pyproject.toml Outdated Show resolved Hide resolved
Copy link
Owner

@nat-n nat-n left a 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(
Copy link
Owner

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?

Copy link
Contributor Author

@ThatXliner ThatXliner Dec 23, 2021

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

Copy link
Owner

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
Copy link
Owner

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?

Copy link
Contributor Author

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():
Copy link
Owner

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.

Copy link
Contributor Author

@ThatXliner ThatXliner Dec 23, 2021

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.

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]`?"
Copy link
Owner

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.

Copy link
Contributor Author

@ThatXliner ThatXliner Dec 23, 2021

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()
Copy link
Owner

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

Copy link
Contributor Author

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

# please mypy,
# and please Poetry's plugin system (simulate the ApplicationPlugin object)
app = type("application", (), {"Application": None}) # pylint: disable=C0103
ApplicationPlugin = type(
Copy link
Owner

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?

Copy link
Contributor Author

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>
@nat-n nat-n mentioned this pull request Dec 23, 2021
@nat-n
Copy link
Owner

nat-n commented Dec 23, 2021

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
@ThatXliner
Copy link
Contributor Author

Rebased... I think

ThatXliner and others added 5 commits December 23, 2021 21:22
…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>
@ThatXliner
Copy link
Contributor Author

Argh I dislike git

@ThatXliner
Copy link
Contributor Author

ThatXliner commented Dec 24, 2021

I'm going to close this:

  1. I hate git.
  2. I like your progress on the other branch

@ThatXliner ThatXliner closed this Dec 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Poetry plugin
4 participants