Thanks for the feedback 
Let’s start with the “Why do we need a standard ?”: interoperability as you highlighted, is the key. I would go further and say user experience. And I can give some example:
- as a newcomer on a project, I want a well-known and quick way to discover possible actions on a project (dev tasks, sample script, data science operations, whatever…)
- as a developer, I want my IDE being able to discover and use the same tasks
- as a DevOps, I want to be able to build a Python pipeline without having to handle a diversity of package managers and script runners
- as a maintainer, I don’t want to rewrite all tasks definitions just because I change the package manager
- as a library developer, I would like to be able to reference existing development tasks to be run in
tox
against multiple python versions without duplicating the task definition itself (same goes for nox
)
And I would take the npm.scripts
case as an example of what we can expect from it:
- whether you use
npm
, yarn
or pnpm
, scripts will just work the same from the same definitions
- migrating from one of those tool to another is almost free
- whatever IDE I use, tasks are being autodetected and easily runnable
- most reusable CI actions can launch scripts as hooks
Note that my proposal goes way further as I used it a lot and I find npm.scripts
useful but way too limited.
I think that the goal is not to provide an interoperable execution environment (the spec does not explain how you install dependencies, or these kind of things), but an interoperable way of:
- discovering tasks
- execute them in a preexisting environment
Setting the environment is another topic to me, there can be so many ways.
This is the reason why I did not include the notion of dependencies there.
It is possible to add a dependencies
property having the same syntax as a dependency-group
so it can reuse existing dependency groups or specific dependencies, but I think this deserves another discussion/PEP.
possibly a little too close to PDM’s design for some people
Actually I also used a lot poe
as reference as it is I think the declarative-based tool which is bundling the most features.
But no wonder it is close to pdm
: I contributed to this specific part, and pdm
allowed me to already try and implement some of the ideas of this document. So it goes a bit both ways, it’s close to pdm
but pdm
is also close from what I propose (and I actually proposed to 2 days ago an evolution going in the same direction: Proposal: scripts and hooks improvements · pdm-project/pdm · Discussion #3344 · GitHub).
In order to use a workflow tool, you have to buy into all of the decisions that tool makes. You can’t like the task running capabilities but hate how it manages environments.
I don’t think it is about like or hate, but actually the hole point of lots of PEPs:
- PEP 621 gives project metadata specification without explaining how to process them in a package
- PEP 621 and PEP 631 giving dependencies specification without saying how to install them
- PEP 735 not saying how to install dependency groups but giving guidelines and format specification
- …
Each time, I think it is sane for those specifications not to step on these topics as it is either another PEP focus on that, either a responsibility delegated to the tools using this PEP.
But in each one, they tried to handle most if not all known cases.
But this is also for that I tried and compared many tools in the document, to have the wider vision possible of what is needed, what is used and what is working (even if I know I don’t have all the tools neither all their usage relying on details nor specific features, and I saw at least 2 tools in this thread that I didn’t test and I am going to).
I want to say spec is not the limit, but at least it provides a common ground to work on.
PEP 621 defined the tool.*
usage, maybe there should be a similar convention for tasks like “tools can extend, but they should prefix their keys with <tool>-
(poe-*
, pdm-*
, …)”
For example, a workflow tool that emphasises security would almost certainly have a big problem with allowing arbitrary shell commands as task definitions. And an environment manager that has ways of associatng environment variables with a given Python environment could quite reasonably be unwilling to support .env
files for tasks as well.
In my opinion, what you describe there is the environment of execution and not the purpose of this discussion (at least not in my vision).
The specs should provide a common ground for describing tasks, and it’s up to each tool to chose what it uses.
Ex: You think that shell
, call
, env
or envfile
are not secure enough for the scope of your tool, just ignore them. The spec doesn’t say that runners should use all the properties, documenting that for security purpose only cmd
tasks will be executed or env
will be ignored by a tool doesn’t invalidate their possibility in a standard. Users complies with the tool they use, while tools follow available standards when possible.
It doesn’t mean that the spec should ignore security, actually it should warn of the known possible security issues (spawning a shell script, sourcing environment variable will always be a security somehow, it doesn’t mean it should never be used, but user should be warn and aware of the risks)
I think we need to understand the use cases, if only to get a clear understanding of why the “named script” approach isn’t acceptable.
I see at least 4 reasons:
- lack of interoperability with non-python tooling (each one need to have a parser for this format, while
toml
is well known) (ex: I rely a lot on being able to query toml
from scripts without extra dependencies)
- fragmentation: it’s not possible to a have a list with details of the possible operations without opening each file to read the descriptions, while I can scroll the same
pyproject.toml
to have all known tasks
- overhead:
pyproject.toml
will be most of the time already loaded for multiple reason and the reading process just have to perform what’s in the task definition to run a task, while the name script approach impose de-facto to run a wrapping python process. A listing of tasks has to list directory content or glob it, and parse each one
- entry cost: while this kind of script is great for complex tasks, if I just want to create 2 tasks
test = "pytest"
and doc = "mkdocs build"
, we already have 2 files to create, with some boilerplate to write (importing and using subprocess
is already development, I can’t say to a translator or a designer not really knowing Python “Just add you script calling your tools”, I’ll need to do it for them if I want it to be able to pass arguments which involves passing through sys.argv
)
The self-contained scripts approach is great (PEP 722 or any other from), but it’s for single-file scripts as opposed as pyproject.toml
which is a project descriptor. So I think that from the start both specs are going in different directions.
But I think a goal of this standard is to be able to reference these scripts.
Maybe this standard needs a bridge toward PEP 722 as sound as it handle dependencies, like a script
property being able to extract metadata from it, but again, I voluntary exclude the dependencies topic.
Remember - the goal here is interoperability, not “a friendly UI”.
I think one implies the other. We don’t do interoperability for itself but for tooling being able to benefit from it, and one of the first and direct consequences of interoperability is end-user being able to use whatever tool they want (the one they think as “a friendly UI”) while having this same tool being able to better integrate (and so have this “friendly UI” more functional so more friendly
)
This is why I wanted to talk about user experience at the start of this post, and this is what drove me initially to this topic years ago.