Why isn't there a version-read-from=...?

(From a users perspective). So I realize packaging is almost like a side-hustle for pyproject.toml. But it also seems partly a regression over setup.py, since most 517 implementations hinge on static declarations.¹

Specifically there’s seemingly no way to do version lookups anymore. You know, all the fiddly little ways (import/regex/perl) people have been using to extract a version from modules. → Which seems
odd, since it’s such a cheap feature to standardize. And it wouldn’t even have to be stellar, a 80/20 solution would do just fine.

  • As in: version-extract-from = "src/init.py" simply reads a script, and regexes the first semver/pep440/float out of it.
  • Didn’t even have to be a context-sensitive lookup. Because for fringe cases, a static/handicrafted pyproject.toml was still an option.
  • Obviously pep621 is frozen now, but what are the chances of any such support appearing in the build backends? ²

There’s nothing wrong with a bit of meta data duplication. There’s also no difference between setup.py and pyproject.toml when it comes to name= or description= etc. Those are rarely, if ever updated. While significant changes like new dependencies do warrant a commit artifact.
But like readme=, the version= field is clearly different. Might not matter to applications, but for e.g. smaller libraries disjoining the version field from the source seems a clutch (really just conveniences
build tools).

Or are there any plans for a _meta_data_update API hook / build chain plugins? - Like, somewhat of a healthy middle ground between sticking with setuptools vs. building a whole new build tool. ³

footnotes

¹ TBH I haven’t looked into prepare_metadata_for_build_wheel much. Seems intend for all-or-nothing rather than partial meta population though. And setuptools-scm doesn’t seem to use any such hooks either.

² Just noticed flit does the sensible thing and honors __version__ from the init module at least. (This is one of those features that might warrant a tabular overview in the packaging user guide.)

³ Of course I can just keep my Makefile rules with the current backends, but that sort of the defeats the purpose of pep518 really.

I guess there are plugins that do that (for setuptools at least).

On that topic, I think I’d rather have the build back-end (or a plugin) write all that project metadata on each build into a module (maybe _meta.py) in the top level package(s). Seems to me like it would be better than the other way around, where the build back-end reads the metadata from the source code.

Also there is importlib.metadata.version('MyProjectName'), for lots of use cases that is more than good enough in my opinion.

There isn’t anything in PEP 517 that precludes grabbing the version dynamically and it remains possible to do with setuptools, by using the attr directive in setup.cfg, from git with setuptools-scm, or even the old-fashioned way in setup.py. Should setuptools allow specifying its package metadata in pyproject.toml eventually, I expect that it’ll continue to support all of these methods - PEP 621 has a provision for dynamic fields. The attr method is probably gonna end up looking something like this:

[project]
name = "my_package"
dynamic = ["version"]

[tool.setuptools]
version = { attr = "my_package.version" }
1 Like

Flit is also working on PEP 621 support, so it’s proof dynamical version generation can be done. (It should be; that was actually an explicit design goal.)

Setuptools_scm is not vendored with setuptools or strongly recommended?

setuptools-scm · PyPI :

the blessed package to manage your versions by scm tags

setuptools_scm handles managing your Python package versions in SCM metadata instead of declaring them as the version argument or in a SCM managed file.

Additionally setuptools_scm provides setuptools with a list of files that are managed by the SCM (i.e. it automatically adds all of the SCM-managed files to the sdist). Unwanted files must be excluded by discarding them via MANIFEST.in.

# pyproject.toml

[tool.setuptools_scm]
write_to = "pkg/version.py"

bump2version (bump2version) is another way to accomplish updating version strings in the package [source] files. bump2version · PyPI :

Version-bump your software with a single command!

A small command line tool to simplify releasing software by updating all version strings in your source code by the correct increment. Also creates commits and tags:

  • version formats are highly configurable
  • works without any VCS, but happily reads tag
  • informationn from and writes commits and tags to Git and Mercurial if available
  • just handles text files, so it’s not specific to any programming language
  • supports Python 3 and PyPy3

For example, cookiecutter-pypackage has this in the (Jinja2) template setup.cfg: cookiecutter-pypackage/setup.cfg at master · audreyfeldroy/cookiecutter-pypackage · GitHub :

[bumpversion]
current_version = {{ cookiecutter.version }}
commit = True
tag = True

[bumpversion:file:setup.py]
search = version='{current_version}'
replace = version='{new_version}'

[bumpversion:file:{{ cookiecutter.project_slug }}/__init__.py]
search = __version__ = '{current_version}'
replace = __version__ = '{new_version}'

FWIU, including a varying commit hash in the build isn’t a complete barrier to having verifiable reproducible builds as long as the commit hash in the version string is the same.

bump2version has a sign_tags feature presumably for e.g. git GPG support that may be most appropriate for an actual release workflow that GPG signs commit tags containing the version string (presumably at version_string update time)

@layday Thanks; I was completely oblivious to the attr: scheme in setuptools. But that’s exactly what this topic was about.
And if this transfers to pyproject.toml someday, that’s perfectly sufficent for most projects I’d say. The version readout is likely the primary use case for dynamic fields. Especially if there’s support in one of the major backends.

Just noticed that PEP621 mentions general meta data hooks being rejected. Which is unfortunate. But I suppose one could always wrap setuptools´ future pep517 api backend, much like you can wrap setuptools.setup() now.

@Wes Kinda did have a tool like that already. Albeit less configurable, more convenient and language-agnostic: version read init.py bump write setup.cfg
bump2version obviously has the better bindings here (→no Makefile necessary). Though those approaches don’t really avert the duplication/post packaging commit.

GitFlow and HubFlow merge branches and tag releases consistently when you git hf release start v0.0.1, update the version with whichever tools on the release/v0.0.1 branch, and then git hf release finish v0.0.1 to merge to master (and develop) and then tag it on master iirc. It prompts for commit messages a couple times and one of those is the commit message for the tag (which you can promote to a Release with release notes and attached build artifacts on e.g. GitHub).

I copied the GitFlow and HubFlow branching diagrams into my notes:
Tools — westurner/tools .

@Wes Pretty nice docs.
But I’m not actually using git. (Very very tempted to go on a rant though;)
Setuptools-scm is certainly useful to a lot of devs, but somewhat coarse when it comes to scm support.

Anyway, I feel like capturing the version number is the more crucial part in this question. Albeit I was originally wondering about feeding in more meta data, or if there’s currently a trivial way to port pluginconf.setup into a pep517 hook. (More about feature management, but it lends itself to abuse for wheel building, as well as deb/rpm/arch packages from the same source.)