bump2version: pyproject.toml (PEP 518) support

Hi, I was wondering if there were plans for pyproject.toml support.

see: peritus/bumpversion#192, PEP 518

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 65
  • Comments: 29 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I like @clbarnes’s suggestion – support for TOML (in general) with .bumpversion.cfg being deprecated for .bumpversion.toml (eventually) and support for pyproject.toml.

This is the case for Python projects.

However, pojects done in another language can also benefit from bumpversion, and there it does not make sense to have either setup.cfg or pyproject.toml.

To clarify, has pyproject.toml support been incorporated? If so, what’s the syntax for writing your configuration settings in the pyproject.toml file? (bump2version docs still only explain .cfg file setup)

Did a bit of hacking on this, it’s a little awkward to fit in with the current scheme of looking in multiple places, having to use separate parsers for different configs etc… Found that TOML is a much better fit than INI in general, because it natively supports booleans, arrays, nested tables and so on, rather than having to hack in equivalents. For this reason, I would recommend supporting a .bumpversion.toml (we basically get it for free once the machinery is in there for parsing the pyproject.toml) and encouraging users to use that instead of INI format - that works regardless of the project language. INI can stick around for legacy purposes, of course, but if/when people move away from setup.cfg, consider throwing a PendingDeprecationWarning on INI usage. There’s just much less code involved when working with TOML, and the interface is much more pythonic (INI is obviously in stdlib but it’s older than today’s notions of pythonicness).

That said, we need to be cautious about writing back to pyproject.toml because it may contain a lot of information about a lot of other tools; comments and ideally formatting should be persisted. There are a few different TOML libraries, but tomlkit seems to be the best option for that persistence. Unfortunately, its API is not exactly the load/dump we’re used to from json.

Having thought about it, I don’t think this is a good idea under bump2version’s current mode of operation. It rewrites the config file every time, and when you have a lot of tools which depend on the same config, that’s not necessarily desirable. TOML is much more flexible in layout than INI, and supports comments - you’ll want to keep that if you have a big complex pyproject file. The tomlkit library can persist formatting and comments but is a bit awkward for everything else.

In an ideal world, a tool like bumpversion could store the current version in an external file, and only store config (which arguably the current version is not) in the config file. But that’s possibly too major a departure.

maybe by default it looks for bumpversion.toml, but there’s also a --config option that one could use to do --config=pyproject.toml if they so wished.

Just a minor point: pyproject.toml would need to be treated differently to bumpversion.toml because all of the bumpversion-specific config would be buried in a sub-table rather than at the root. So it wouldn’t be enough to just include a --config= argument which is generic over bumpversion-specific files and pyproject files, unless that argument also allowed you to specify the table within the file to look for (e.g. --config=pyproject.toml:tool.bumpversion. You could hardcode special behaviour for pyproject files but that makes it a bit less transparent; although it would also allow you to hardcode additional special behaviour for other language-specific config files with space for arbitrary tools (e.g. rust’s Cargo.toml) in the same entry point.

In short, the following sequence of searching / reading configuration should be used:

I’d suggest a minor tweak to the resolution order above: bumpversion-specific files should always be sought before generic files, because plenty of projects could have a setup.cfg without bumpversion config in it, and then a separate bumpversion config file. Additionally, TOML is strictly a better format than INI, so I’d be in favour of an order like bumpversion.toml -> bumpversion.ini -> pyproject.toml:tool.bumpversion -> setup.cfg.

I think we shouldn’t mix things up.

Having had a couple of years to mull over this issue, I think I too am personally coming down on the side of a specific config file rather than writing back to the pyproject.toml, especially as all of bumpversion’s config would be nested pretty deep into the pyproject, and deep nesting is not handled very ergonomically by TOML. Certainly that would be the easiest first pass, and it helps establish bumpversion as a project-agnostic tool (albeit one with a particularly strong relationship with python). But there seems to be a fair amount of appetite for building it into a shared config file and it may not be all that difficult.

Using an ini-based, toml-based, yaml-based or json-based configuration

I do have an opinion on this: moving from INI to TOML is strictly an upgrade which drastically improves the complexity and maintainability of the tool (once INI support is actually deprecated, of course). INI was a mistake (globally, that is; not one made specifically by bumpversion). That there will be a transition period where both forms is supported is IMO merely an unfortunate hangover from those dark days; not a design goal. I think that supporting multiple config formats as a feature would complicate the lives of contributors, maintainers, and users for very little benefit: sure, there are YAML enthusiasts out there and javascripters inexplicably love hand-writing JSON, but I don’t think supporting them all really makes bumpversion any more attractive a solution. “There should be one-- and preferably only one --obvious way to do it.”

I think we shouldn’t mix things up.

Though mentioned otherwise, adding support for pyproject.toml doesn’t mean that it should be the only or single source of truth. It just means, that python developers who wish to use pyproject.toml because of PEP518 as central configuration for their development are free to do so.

If you do not (wish to) use pyproject.toml or use bump2version for the development with other programming languages, you should still be free to do so.

Although, there might be a tendency to use a toml-based configurattion file over setup.cfg or .bump2version.cfg, it does not mean, that everything should be done in pyproject.toml.

So I think we should consider adding support for pyproject.toml just to be compliant to PEP518. What happens afterward, is a different story. This part of the road(map) is simply the job of the authors / maintainers like @c4urself .

Using an ini-based, toml-based, yaml-based or json-based configuration will result in python always in dict instances, so the impact in fact should be minimal, if it is done right.

So, if we just imagine, the support for pyproject.toml is here, which sequence should be used when loading the configuration? Should we use pyproject.toml in favor of all other files or should it be the fallback?

What I’ve seen so far is that implementations vary. Some prefer the usage of pyproject.toml over their own configuration, some prefer their own configuration.

My gut instincts tend to tell my the letter should be the preferred way.

In short, the following sequence of searching / reading configuration should be used:

  1. Search for .bump2version.cfg
  2. Search for setup.cfg
  3. Search for .bump2version.toml
  4. Search for pyproject.toml

In order to make things more easy for the user, the configuration files 1-3 should be written back. If a pyproject.toml is used. the configuration entries for modifying the version numbers should be added automatically unless the user configures it differently. This will help, keeping the PEP621 entries PEP440 compliant.

This should be easy to implement, if I understand the existing implementation correctly.

I’d advise against adding pyproject.toml support but moving to a bumpversion specific TOML file (.bumpversion.toml) instead for reasons, many of which, already mentioned in this thread:

  • pyproject.toml is an important file used by countless tools so it’d be safer to treat it as read-only to avoid style changes and possible content changes resulting from TOML parser/writer bugs. A shared configuration file that many tools read is great. A shared file that some tools also write to … not so great. My understanding is that setup.cfg support already received some complaints regarding this.
  • bump2version’s usefulness is in no way limited to Python projects so using a Python specific conf file seems like bad taste (IMO). Also, supporting only one configuration file has the advantage that there’s only one possible place to look for config if you want to know if the tool is being used or not.
  • People will configure search-and-replace patterns for pyproject.toml. It feels risky to do a parser/writer passthrough (possibly doing minor style changes) (also changing version) and a following search-and-replace in the same file.
  • A bumpversion specific config can be made flatter and easier to read. It could look something like
    commit = true
    tag = true
    current_version = "0.5.2"
    
    [[files]]
    path="src/main.py"
    search = '__VERSION__ = "{current_version}"'
    replace = '__VERSION__ = "{new_version}"'
    
    [[files]]
    path="pyproject.toml"
    search = 'version = "{current_version}"'
    replace = 'version = "{new_version}"'
    

@kdeldycke poetry v1.2 supports plugins and several people have already made “bumpversion” plugins for custom script support:

  1. poetry-bumpversion
  2. poetry-version-plugin
  3. poetry-dynamic-versioning

Unfortunately (I say this because I have loved bump2version), I don’t think it would be worth anyone’s time to write a custom script for bump2version; they would soon pyproject.toml front-end equivalent anyway.

Had bump2version got on board and decided to adopt the pyproject.toml file, one could have put these setup.cfg/.bumpversion.cfg ini-file style options into the pyproject.toml. Many other 3rd-party packages already have done this, though I respect the bump2version developers’ decision points against adopting pyproject.toml wholesale.

If any work is to be done, I recommend it be toward using a plugin or creating one that suites your needs.

Sadly, there’s not much of a selling point for me to use bump2version any more:

  • plugins are easy to write
  • I don’t have to rely on a 3rd party package
  • I can remove additional .cfg and .ini files, getting closer to a single source of truth

I might’ve kept bump2version had pyproject.toml been adopted, but it’s too late now. Metadata reading being incorporated into the python 3.8+ stdlib too…this is a no brainer for me.

Here is the awesome-pyproject list. It would be helpful if and when bump2version supports pyproject.toml to notify the list maintainer or submitting a PR to update the list indicating that bump2version supports the file now. Just FYI. 😃

While I have nothing against a project specific toml file, much of the appeal of pyproject.toml support is to avoid having a separate config file for each tool in a project.

Perhaps a compromise would be to (by default) support a specifically named toml file, and then provide a command-line option to override. Ex: maybe by default it looks for bumpversion.toml, but there’s also a --config option that one could use to do --config=pyproject.toml if they so wished.

FYI, bump2version is no longer maintained but a new project is resurrecting it and is called Bump My Version.

Good new, the last bump-my-version 0.4.0 supports pyproject.toml out of the box.

For more information, see: https://github.com/c4urself/bump2version/issues/268

  • It seems now that poetry supports the command poetry version <rule> where one can bump the version listed in the pyproject.toml by major/minor/patch (see the poetry docs.).

  • In python3.8, one can use the builtin module importlib.metadata to query for package versions dynamically in source code (e.g. my_version = metadata.version('my_pkg'))

With these two updates, bump2version now seems obsolete:

  • I no longer have to add locations I want version strings updated in a .bumpversion.cfg or setup.cfg. I can do this programmatically with python and I can delete my cfg files
  • The “single source of truth” is in the pyproject.toml, which resolves the myriad of solutions offered by the PyPA
  • I can remove bump2version, eliminating an extra dependency in my project
  • In the advent of PEP 517 and 518, setuptools has deprecated setup.py install (see this) and appears to be transitioning from being a front + backend to simply a backend. Hence, with the growing popularity of pyproject.toml, it seems highly likely adding a pyproject.toml to one’s project when packaging will soon become a requirement anyway. Hence, I can achieve everything with one toml file instead of mutliple toml plus config files.

much of the appeal of pyproject.toml support is to avoid having a separate config file for each tool in a project.

IMO this appeal only applies to, as the name pyproject suggests, Python project specific tools. I would not find it appealing at all if suddenly my .gitignore, .editorconfig, .pre-commit-conf.yaml and .codecov.yaml were crammed into pyproject.toml as these files are being read by programming language agnostic tools. I think bump2version falls in the same category.