pypa / setuptools

Official project repository for the Setuptools build system
https://pypi.org/project/setuptools/
MIT License
2.35k stars 1.15k forks source link

Discussion: support for pyproject.toml configuration #1688

Closed pganssle closed 2 years ago

pganssle commented 5 years ago

Over the last few months, I've been using setup.cfg more, and I find the syntax and types and such to be super counter-intuitive. I don't find TOML to be a breeze per se, but it's at least a lot clearer to me how to specify a string, a list, etc. We also hear a lot of cries to move towards supporting pyproject.toml as the "one file" for setup configuration, in favor of setup.cfg. I'm not terribly opposed to this, but I want to open up the discussion for a few options:

  1. Add pyproject.toml as the one true way to do declarative builds, moving all setuptools configuration over there and deprecating setup.cfg (partially what is discussed in #1160, and if we decide on that option we can move over to that issue).
  2. Add pyproject.toml as a second supported way to do a declarative configuration, alongside setup.cfg.
  3. Stay the course with setup.cfg indefinitely.

I am deeply concerned with the large amount of churn that we're introducing into the Python packaging space, so I don't want to make a lot of changes for the sake of changes, but I do think that at least having the option to use a pyproject.toml would be a real benefit, so I'm basically in favor of option 2.

I think the biggest downside of option 2 is that it means maintaining, testing and documenting three separate locations for all of our options - how to do it in setup.cfg, how to do it in pyproject.toml, and how to do it in setup.py. I think the hard part is going from 1 way to do everything to 2 ways. Our documentation is a mess anyway, but my beautiful vision for the future is that all our examples have "tabs" that allow you to toggle them between setup.cfg and setup.py. Adding a pyproject.toml in there would probably not be the worst thing.

jaraco commented 5 years ago

I'd prefer option 1. I imagine during the deprecation period that the setup.cfg documentation could be removed, but a permalink to the old documentation made available for projects still relying on that technique.

One way to ease the transition could be for the setup.cfg code to generate a .toml file and consume then have the pyproject.toml consumer just load both files. That way, a project seeking to transition would only need to do one build, manually merge the two .toml files, and delete setup.cfg.

My second choice would be option 3. I'd really like to avoid supporting multiple formats that do essentially the same thing.

Another consideration I'd like to mention is the proposal I only recently started fleshing out in pypa/packaging-problems#248. Such an approach would be yet another declarative format. However, this proposed approach would enable project developers to supply metadata decoratively in the canonical format (or in a format suitable for the requisite build steps). If such an approach is deemed viable and comes to fruition, the value in migrating to pyproject.toml may prove small. I suggest postponing this transition at least until that more ambitious proposal is disposed or reaches fruition.

njsmith commented 5 years ago

I imagine during the deprecation period that the setup.cfg documentation could be removed, but a permalink to the old documentation made available for projects still relying on that technique.

I'm pretty sure the deprecation period would be literally forever. I bet there are projects on PyPI that (a) are dead and will never see another release, (b) are still used, and (c) depend on setup.cfg to install.

I'm not opposed to adding features to setuptools and better ways to do things, but I think it's about 100x less important than not breaking stuff.

pradyunsg commented 4 years ago

I'm in favour of 1, with deprecated forever status (ideally keep the heading in documentation and just remove all the content, replacing it with a paragraph saying use pyproject.toml)

qlixed commented 4 years ago

There is an ETA for this? Or there is still some discussion pending?.I think that Option 1 is the best, but here is not stated for sure if this will be done.

pganssle commented 4 years ago

@qlixed There is no ETA.

I think we probably have a rough consensus for Option 1 with an indefinite deprecation period (we don't have to specify the length of the deprecation period anyway). I won't have time to do this any time soon, but I think we can accept a contribution that does.

I think we'll need to:

  1. Vendor the toml library
  2. Build out a tool that converts setup.cfg to pyproject.toml
  3. Make it the default way that setup.cfg is processed.

I think we can say that if options are specified in both setup.cfg and pyproject.toml (under tool:setuptools), that we throw an error and refuse to build (use one or the other, not both). I don't think we should warn if people use setup.cfg even when pyproject.toml is available, we should wait for pyproject.toml configuration to be stable for at least a year before actively pushing it like that.

We'll also need to decide how to handle the little ad-hoc "types" we've implemented like file:, attr:, etc. Are we parsing these as strings (I really hate how much string parsing we do in setup.cfg as it is)? Using a dict? Is there some facility in the TOML language that would make this easy for us?

layday commented 4 years ago

Is there some facility in the TOML language that would make this easy for us?

Yes, TOML supports both nested and inline tables. For example, the version could be hardcoded as a string:

version = "0.0.0"

... or it could be a table specifying one of attr or file:

version = { attr = "setuptools.version" }

The same 'trick' is used by Poetry to separate dependency markers from version specifiers.

brainwane commented 3 years ago

In the fundable packaging improvements list I summarized this as follows (if it's inaccurate, I'd appreciate a correction, especially as a pull request):

Add support for pyproject.toml as a way to configure setuptools

setuptools does not yet allow project creators to use the new pyproject.toml standard configuration file to configure setuptools behavior. This distracts and confuses package creators, and prevents platforms and tools from depending on the presence of standard pyproject.toml metadata in packages. We'd like to implement pyproject.toml configuration support in setuptools. This requires backend development work, technical writing, and coordination and publicity work among Python users.

I'd also like to better work out what this entails and how long it would take. To dig into @pganssle's proposed sequence:

I think we'll need to:

1. Vendor the `toml` library

2. Build out a tool that converts `setup.cfg` to `pyproject.toml`

Would this live within setuptools or be part of some other codebase?

3. Make it the default way that `setup.cfg` is processed.

One subtask being: decide and implement how to deal with it if people use setup.cfg even when pyproject.toml is available.

Also: should this task include implementing a deprecation notices to output when the user's supplied a setup.cfg file, advising them to switch to pyproject.toml in the future?

So that's the backend development work. I'd add in:

  1. technical writing, and coordination and publicity work among Python users:

What have I missed or gotten wrong? Is this mostly right?

jaraco commented 3 years ago

Thanks @brainwane for pushing this forward. Your outline looks good to me. And nicely done making docs and the user experience first-class considerations.

Would this live within setuptools or be part of some other codebase?

My instinct is within, as it has little value outside the context of setuptools. I would expect it to be largely uncoupled with most of the functionality in setuptools except config-related handling.

pganssle commented 3 years ago

One thing to note: There is an as-yet-unreleased PEP that is in its final stages that intends to standardize the form of all the build metadata in pyproject.toml. I believe that should be released for public comment soonish. (Hopefully I'm not spilling the beans too much by mentioning it here).

It's fine to start work on pyproject.toml support, but we should be aware that any ad-hoc configuration we come up with in the tool:setuptools table may become Yet Another Location for build metadata if the PEP is accepted. If someone is itching to get started on this and doesn't have access to the PEP, they can let me know and I'll see about arranging to get them a copy. Otherwise I'll link to it here when it is made public. Even before it's accepted I think coding assuming it will be accepted is reasonable; we can always rename the tables later, and actually trying to implement the thing will probably give the authors good feedback.

KOLANICH commented 3 years ago

If someone is itching to get started on this and doesn't have access to the PEP

Are there any deep reasons to prepare it non-publicly?

pganssle commented 3 years ago

Are there any deep reasons to prepare it non-publicly?

It was not my choice, but PEPs are usually prepared privately, by a single individual or a small group. This one has a decent number of authors on it (as you'll see), so it's a bit unusual in that respect, but I think it was probably a smart strategy to get an initial draft that is agreeable to a bunch of the stakeholders before bringing it forward for wider comment. It's super easy to get bogged down in random details when there are "too many cooks in the kitchen".

Honestly, it's not some secretive packaging cabal, just a project that people have been putting a lot of effort into that I happen to know about. I'm fairly certain the number of goats sacrificed was in the single digits.

eli-schwartz commented 3 years ago

Nice! I've been wondering for a long time why every tool inefficiently reimplements the exact same metadata verbatim in a non-introspectable manner. Good to hear this mistake is being rectified. :)

pradyunsg commented 3 years ago

the number of goats sacrificed was in the single digits

WAIT. Who sacrificed goats!? :o

pradyunsg commented 3 years ago

tool.setuptools table may become Yet Another Location for build metadata if the PEP is accepted

I mean, to an extent. The tool.setuptools will still be relevant since there's a lot of stuff that's just not performed/treated the same way (eg: C extensions, including/excluding specific files, package data etc), so there would still be setuptools specific options/flags, which it'd be nice to be able to declare in pyproject.toml.

brainwane commented 3 years ago

PEP 621 ("Storing project metadata in pyproject.toml") is now published in draft form and available for discussion.

brainwane commented 3 years ago

I talked with a few maintainers and here is a rough estimate of how much time this would take, assuming that the Python developer(s), tech writer(s), coordinator(s), etc. involved already know some things about Python packaging but haven't previously worked on the setuptools codebase.

Here's a very rough idea (always assuming people are working about 35 hrs/week, and including testing and iterating in response to code review):

  1. Ramp-up time for everyone who doesn't already know the setuptools codebase: ~2 weeks
  2. Vendor the toml library: ~1 week (should be fairly straightforward except for review coordination)
  3. Build out a tool, to live within setuptools, that converts setup.cfg to pyproject.toml: ~4 weeks (including a few weeks of finding sample setup.cfg files, testing those, finding bugs, making design decisions about validity)
  4. Make it the default way that setup.cfg is processed (including: dealing with simultaneous setup.cfg & pyproject.toml presence, and implementing deprecation notices): ~4-6 weeks

Simultaneous with a bunch of that: technical writing, and coordination and publicity work among Python users:

KOLANICH commented 3 years ago

Some comments:

  1. Since we don't directly define console_scripts entry points anymore, I'd like to make a step further and also standardize an icon per a script. An icon is given as a relative path to a graphic file of PNG format (further other formats may be added). The action of it is following: metadata dir is given a subdir icons where icons are placed using last components of their paths as names (so they must be unique), and icons names are placed into entry points metadata (not yet standardized, I gonna probably write a pep, basically it is entry_point_name @ {"icon":"a.png"}). Different scripts can have the same icon, in this case only 1 file is kept. pip or other tools may use this info for creating shortcuts.
  2. instead of dynamic I propose the following scheme: the metadata may be set by build plugins, like setuptools_scm, so instead of mentioning the name in dynamic we just mention it within the key that it is set using the plugin somehow. When a plugin is installed, it contains in its entry point metadata the list of pyproject.toml metadata keys it can (and must, if mentioned) set, so everything can be verified statically without invoking the plugin.
eli-schwartz commented 3 years ago

What do icons have to do with console scripts which are only relevant on the console, which doesn't have the concept of graphics?

How does this relate to consolidating existing metadata into pyproject.toml?

KOLANICH commented 3 years ago

What do icons have to do with console scripts which are only relevant on the console, which doesn't have the concept of graphics?

  1. CLI apps may have icons. In Windows.
  2. shortcuts / .desktop files to cli apps may have icons.

How does this relate to consolidating existing metadata into pyproject.toml?

In no way to consolidating, but this is also a good moment to introduce something new

merwok commented 3 years ago

This issue is focused on defining a new way of specifying existing information. Please create a new thread on https://discuss.python.org/c/packaging/14 to propose changes to the metadata spec. Thanks!

nschloe commented 3 years ago

FWIW, PEP 621 for Storing project metadata in pyproject.toml has been provisionally accepted.

abitrolly commented 3 years ago

@brainwane how to apply to https://github.com/psf/fundable-packaging-improvements/blob/master/FUNDABLES.md#add-support-for-pyprojecttoml-as-a-way-to-configure-setuptools ? I can start on vendoring toml and code that reads values from it for setup.cfg override. Logging hours on UpWork, working through gitcoin.co contract are all fine with me.

di commented 3 years ago

Hi @abitrolly, that list is projects that need funding, not projects that we already have funding for. We'd need to find funding for this first.

berislavlopac commented 3 years ago

FWIW, as announced in November, PEP 621 was declared Final on 1st March.

FelixBenning commented 3 years ago

I am a bit confused by the current state of things #1160 was closed "in favour of" this merge request and states that setup.py will not be deprecated while setup.cfg would be deprecated indefinitely. Now the documentation (e.g. Quickstart) has tabs for setup.cfg and setup.py but the default tab is setup.cfg which kind of implies that this is the recommended way of doing things and the Quickstart documentation also includes a section called: Transitioning from setup.py to setup.cfg

Lastly I wonder how a declarative format would allow things like loading the README.md from file into a variable and using that as the long description of the package, which is common I think. In other words: how are declarative configurations doing these dynamic setup logic types of things.

I assume that the above is the reason setup.py will not be deprecated but I still wonder what belongs into the pyproject.toml and setup.py then, and how they interoperate. And the current documentation only references pyproject.toml shortly in the beginning for defining the setuptools and wheels requirement.

FelixBenning commented 3 years ago

Lastly I wonder how a declarative format would allow things like loading the README.md from file into a variable and using that as the long description of the package, which is common I think

It appears the pyproject.toml will have this functionality included by default: https://www.python.org/dev/peps/pep-0621/#readme

JulienPalard commented 3 years ago

Lastly I wonder how a declarative format would allow things like loading the README.md from file into a variable and using that as the long description of the package, which is common I think. In other words: how are declarative configurations doing these dynamic setup logic types of things.

This kind of thing is common yes, and handled using attr:, file:, ... prefixes.

See for example my https://github.com/JulienPalard/oeis/ project that dropped setup.py on jan 2021 (it was previously a 2 line file: just import setuptools and setuptools.setup(), I sometime even used __import__("setuptools").setup() for the fun of oneliners).

Since PEP 621 has been accepted, the future as I personally see it, is to drop setup.cfg too in favor of pyproject.toml.

KOLANICH commented 3 years ago

It can be possible to create a bot searching GitHub for setup.cfgs with full metadata, converting them into PEP 621-compliant pyproject.toml and sending PRs.

wodny commented 3 years ago

I am a bit confused by the current state of things #1160 was closed "in favour of" this merge request and states that setup.py will not be deprecated while setup.cfg would be deprecated indefinitely. Now the documentation (e.g. Quickstart) has tabs for setup.cfg and setup.py but the default tab is setup.cfg which kind of implies that this is the recommended way of doing things and the Quickstart documentation also includes a section called: Transitioning from setup.py to setup.cfg

Documentation in various places seems to be out of sync, imprecise, confusing and sometimes it omits information if it's about PEP-517 or legacy mode.

As I understand it - the build tools environment currently undergoes both a rapid development and unification. Unification comes mainly from PEP-517, PEP-518, PEP-621 and PEP-631.

With setuptools as the build backend

When using setuptools, two modes are available:

In PEP-517 mode setup.cfg will be useful until all the things from setup.cfg can be expressed in pyproject.toml. pyproject.toml + setup.cfg without setup.py is mentioned in setuptools changelog (v40.9.0). pyproject.toml is not explicitly mentioned there but is implied by the PEP-517 mode.

In legacy mode setup.cfg gives an option to express metadata in a declarative manner with setup.py containing just the minimal code (from setuptools import setup; setup()) . For me it's more elegant than specifying static data using code.

A legacy mode with just setup.cfg but without setup.py is possible but discouraged. (setuptools provides minimal setup.py on-the-fly in both modes). See setuptools implementation.

With something else as the build backend

When not using setuptools I suppose pyproject.toml is already sufficient as the only metadata file because tools like flit or poetry support PEP-517 etc. and started using pyproject.toml instead of tool-specific config files a long time ago. setup.py is not required (but sometimes used?) and setup.cfg is just a setuptools-specific thing (probably).

Remarks

Various tools are capable of reading different things from pyproject.toml: basic metadata like author, dependencies, tool-specific settings. Note the unified [project] section as opposed to [tool.*] tool-specific sections.

Other capabilities important in this whole process but depending on a tool used:

Please correct me if I'm wrong. Those are just parts of my own notes based on issues, PRs and docs of pip, setuptools, PyPA's build, flit and poetry.

Wenzil commented 2 years ago

@wodny In PEP-517 mode, it seems setup.py is not optional if you want to install the package in editable mode, e.g. with pip install -e .

ERROR: File "setup.py" not found. Directory cannot be installed in editable mode: /Users/Wenzil/my_repo
(A "pyproject.toml" file was found, but editable mode currently requires a setup.py based build.)

I resolved it by adding a dummy setup.py file (alongside setup.cfg):

#!/usr/bin/env python
import setuptools

if __name__ == "__main__":
    setuptools.setup()

Posting in case it can help others.

wodny commented 2 years ago

In PEP-517 mode, it seems setup.py is not optional if you want to install the package in editable mode, e.g. with pip install -e .

I think you may be using an old version of pip (< 21.1). See the commit that changed that. What's your version of pip?

Wenzil commented 2 years ago

You are right!

I was on pip 20.1. I have upgraded to 21.1 and it now works as expected: setup.py is not needed anymore.

Thanks!

abravalheri commented 2 years ago

Hello all,

@brainwane and @pganssle previously mentioned the idea of building a tool that converts setup.cfg into pyproject.toml as one of the steps required to support PEP621.

I recently started working in a tool that might relevant here: ini2toml (it was literally created with the same objective in mind).

It is still a work in progress but I would be super happy to collaborate with setuptools!

There are a few examples that show how the translation could work out.

I had to make some assumptions because there is information/keywords in setup.cfg not covered by PEP 621, but that is pretty much provisional and can be changed according to what setuptools decide to adopt. (I also took some suggestions from this thread).


Thank you very much all the maintainers of setuptools! It is an amazing tool that deserves loads of love in the community

CAM-Gerlach commented 2 years ago

I wish I had the time in the short term to dedicate this, given my at least basic background/involvement in the packaging ecosystem, extensive technical writing experience and enthusiasm for getting this done, but maybe if we can break this up into many smaller incremental subtasks, multiple collaborators could have a shot at incrementally completing this?

As a bit of a sidenote, tomli now seems to be the gold standard for a vendored toml library as (unlike uiri/toml) its minimal, well-maintained, TOML 1.0.0-conformant, and essentially purpose-built for this role, so integrating it would be almost trivial. Pip (pypa/pip#10035 ), setuptools_scm and others in the ecosystem have already adopted it as well. So at least one aspect of this should be very straightforward.

Ambro17 commented 2 years ago

What's the best way to help supporting pep 621 into setuptools?

cowlinator commented 2 years ago

@CAM-Gerlach , what kind of smaller incremental subtasks should we break this up into? What are the first steps?

CAM-Gerlach commented 2 years ago

@cowlinator Well, that's what I was asking :slightly_smiling_face: The high-level tasks are outlined here, but if the first steps could be broken down further into more atomic subtasks, perhaps it would be easier to get the ball rolling without committing the much larger time and effort to doing everything.

abravalheri commented 2 years ago

@CAM-Gerlach, @cowlinator would like to review https://github.com/pypa/setuptools/pull/2970?

Although it might be a bit dense (also because of the vendored dependencies), but I tried to break down to what would be equivalent to a few PRs (github does not offer native support to stacked PRs), and there are links and a diagram in the description showing so. (I did do a later rebase fixing some merge-conflicts but nothing too serious that invalidate my previous separation).

Also notice that the PR is adding 2 vendored dependencies, so you can skip the files in setuptools/_vendor...

abitrolly commented 2 years ago

github does not offer native support to stacked PRs

Is there a forge that supports stacked PRs or MRs?

abravalheri commented 2 years ago

github does not offer native support to stacked PRs

Is there a forge that supports stacked PRs or MRs?

@abitrolly according to this article launchpad seems to support it (if I understood your question).

CAM-Gerlach commented 2 years ago

Wow, thanks for the work, @abravalheri ! Unfortunately, I'm far less knowledgeable about Setuptools' internals than you, let alone a core developer or maintainer, so I'm afraid I'm not much more help on the review, but good on you for taking that on! However, I can potentially help with some of the technical writing work, since that's closer to my expertise, though I'm already championing at least one major packaging PEP and helping out with several others, never mind many other tasks.

abravalheri commented 2 years ago

If anyone would like to help testing the feature (to see if anything breaks in existing packages), I posted some information on the Python Discourse.

ssbarnea commented 2 years ago

So PEP-631 is not implemented by setuptools yet... :(

abravalheri commented 2 years ago

Hi @ssbarnea, we are planning to merge the experimental branch very soon. Meanwhile, I am just making sure we don't break existing projects. If you want to try it out, please check the instructions in the Python Discourse. Any feedback is appreciated.

ssbarnea commented 2 years ago

Super. I recently discovered that dependabot supports looking at requirements from pyproject.toml but not setup,cfg and I doubt they will add them (bug is more than two years old). So for the moment I am bit stuck and unable to automate bumping of test dependencies.

ofek commented 2 years ago

dependabot supports looking at requirements from pyproject.toml but not setup,cfg

I think you meant the opposite: https://github.com/dependabot/dependabot-core/issues/3290

CAM-Gerlach commented 2 years ago

I thought it only supported requirements.txt and some custom thing with poetry; are you sure it supports setup.cfg? I've haven't ever seen any Dependabot PRs for the latter across the many projects I maintain that use both, just for GH actions versions.

abravalheri commented 2 years ago

The v61.*.* series of setuptools includes some initial support for pyproject.toml in an experimental fashion.

The experimental aspect of it allow us to think about how we would like the [tool.setuptools] table to look like (e.g. #3191). Currently it is mostly reflecting everything what already exists in setup.cfg, but it is pretty much open for debate.

The recently improved auto-discovery should allow the simple projects to completely skip the [tool.setuptools] table.

Note that the existing implementation started by taking approach 2, as discussed in the first post. However the plan is to eventually deprecate setup.cfg (and therefore adopt what is described in approach 1). But that will come with time.

I will open a new issue to track the deprecation.

P.S.: There is already an initial implementation of how to generate pyproject.toml from setup.cfg in ini2toml.

EpicWink commented 2 years ago

The v61.*.* series of setuptools includes some initial support for pyproject.toml in an experimental fashion.

you say "some" support, but I don't see what isn't supported

abravalheri commented 2 years ago

Hi @EpicWink, from the top of my head: