GithubHelp home page GithubHelp logo

python-versioneer's Introduction

The Versioneer

This is a tool for managing a recorded version number in setuptools-based python projects. The goal is to remove the tedious and error-prone "update the embedded version string" step from your release process. Making a new release should be as easy as recording a new tag in your version-control system, and maybe making new tarballs.

Quick Install

Versioneer provides two installation modes. The "classic" vendored mode installs a copy of versioneer into your repository. The experimental build-time dependency mode is intended to allow you to skip this step and simplify the process of upgrading.

Vendored mode

  • pip install versioneer to somewhere in your $PATH
    • A conda-forge recipe is available, so you can also use conda install -c conda-forge versioneer
  • add a [tool.versioneer] section to your pyproject.toml or a [versioneer] section to your setup.cfg (see Install)
    • Note that you will need to add tomli; python_version < "3.11" to your build-time dependencies if you use pyproject.toml
  • run versioneer install --vendor in your source tree, commit the results
  • verify version information with python setup.py version

Build-time dependency mode

  • pip install versioneer to somewhere in your $PATH
    • A conda-forge recipe is available, so you can also use conda install -c conda-forge versioneer
  • add a [tool.versioneer] section to your pyproject.toml or a [versioneer] section to your setup.cfg (see Install)
  • add versioneer (with [toml] extra, if configuring in pyproject.toml) to the requires key of the build-system table in pyproject.toml:
    [build-system]
    requires = ["setuptools", "versioneer[toml]"]
    build-backend = "setuptools.build_meta"
  • run versioneer install --no-vendor in your source tree, commit the results
  • verify version information with python setup.py version

Version Identifiers

Source trees come from a variety of places:

  • a version-control system checkout (mostly used by developers)
  • a nightly tarball, produced by build automation
  • a snapshot tarball, produced by a web-based VCS browser, like github's "tarball from tag" feature
  • a release tarball, produced by "setup.py sdist", distributed through PyPI

Within each source tree, the version identifier (either a string or a number, this tool is format-agnostic) can come from a variety of places:

  • ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows about recent "tags" and an absolute revision-id
  • the name of the directory into which the tarball was unpacked
  • an expanded VCS keyword ($Id$, etc)
  • a _version.py created by some earlier build step

For released software, the version identifier is closely related to a VCS tag. Some projects use tag names that include more than just the version string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool needs to strip the tag prefix to extract the version identifier. For unreleased software (between tags), the version identifier should provide enough information to help developers recreate the same tree, while also giving them an idea of roughly how old the tree is (after version 1.2, before version 1.3). Many VCS systems can report a description that captures this, for example git describe --tags --dirty --always reports things like "0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the 0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has uncommitted changes).

The version identifier is used for multiple purposes:

  • to allow the module to self-identify its version: myproject.__version__
  • to choose a name and prefix for a 'setup.py sdist' tarball

Theory of Operation

Versioneer works by adding a special _version.py file into your source tree, where your __init__.py can import it. This _version.py knows how to dynamically ask the VCS tool for version information at import time.

_version.py also contains $Revision$ markers, and the installation process marks _version.py to have this marker rewritten with a tag name during the git archive command. As a result, generated tarballs will contain enough information to get the proper version.

To allow setup.py to compute a version too, a versioneer.py is added to the top level of your source tree, next to setup.py and the setup.cfg that configures it. This overrides several distutils/setuptools commands to compute the version when invoked, and changes setup.py build and setup.py sdist to replace _version.py with a small static file that contains just the generated version data.

Installation

See INSTALL.md for detailed installation instructions.

Version-String Flavors

Code which uses Versioneer can learn about its version string at runtime by importing _version from your main __init__.py file and running the get_versions() function. From the "outside" (e.g. in setup.py), you can import the top-level versioneer.py and run get_versions().

Both functions return a dictionary with different flavors of version information:

  • ['version']: A condensed version string, rendered using the selected style. This is the most commonly used value for the project's version string. The default "pep440" style yields strings like 0.11, 0.11+2.g1076c97, or 0.11+2.g1076c97.dirty. See the "Styles" section below for alternative styles.

  • ['full-revisionid']: detailed revision identifier. For Git, this is the full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac".

  • ['date']: Date and time of the latest HEAD commit. For Git, it is the commit date in ISO 8601 format. This will be None if the date is not available.

  • ['dirty']: a boolean, True if the tree has uncommitted changes. Note that this is only accurate if run in a VCS checkout, otherwise it is likely to be False or None

  • ['error']: if the version string could not be computed, this will be set to a string describing the problem, otherwise it will be None. It may be useful to throw an exception in setup.py if this is set, to avoid e.g. creating tarballs with a version string of "unknown".

Some variants are more useful than others. Including full-revisionid in a bug report should allow developers to reconstruct the exact code being tested (or indicate the presence of local changes that should be shared with the developers). version is suitable for display in an "about" box or a CLI --version output: it can be easily compared against release notes and lists of bugs fixed in various releases.

The installer adds the following text to your __init__.py to place a basic version in YOURPROJECT.__version__:

from ._version import get_versions
__version__ = get_versions()['version']
del get_versions

Styles

The setup.cfg style= configuration controls how the VCS information is rendered into a version string.

The default style, "pep440", produces a PEP440-compliant string, equal to the un-prefixed tag name for actual releases, and containing an additional "local version" section with more detail for in-between builds. For Git, this is TAG[+DISTANCE.gHEX[.dirty]] , using information from git describe --tags --dirty --always. For example "0.11+2.g1076c97.dirty" indicates that the tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and that this commit is two revisions ("+2") beyond the "0.11" tag. For released software (exactly equal to a known tag), the identifier will only contain the stripped tag, e.g. "0.11".

Other styles are available. See details.md in the Versioneer source tree for descriptions.

Debugging

Versioneer tries to avoid fatal errors: if something goes wrong, it will tend to return a version of "0+unknown". To investigate the problem, run setup.py version, which will run the version-lookup code in a verbose mode, and will display the full contents of get_versions() (including the error string, which may help identify what went wrong).

Known Limitations

Some situations are known to cause problems for Versioneer. This details the most significant ones. More can be found on Github issues page.

Subprojects

Versioneer has limited support for source trees in which setup.py is not in the root directory (e.g. setup.py and .git/ are not siblings). The are two common reasons why setup.py might not be in the root:

  • Source trees which contain multiple subprojects, such as Buildbot, which contains both "master" and "slave" subprojects, each with their own setup.py, setup.cfg, and tox.ini. Projects like these produce multiple PyPI distributions (and upload multiple independently-installable tarballs).
  • Source trees whose main purpose is to contain a C library, but which also provide bindings to Python (and perhaps other languages) in subdirectories.

Versioneer will look for .git in parent directories, and most operations should get the right version string. However pip and setuptools have bugs and implementation details which frequently cause pip install . from a subproject directory to fail to find a correct version string (so it usually defaults to 0+unknown).

pip install --editable . should work correctly. setup.py install might work too.

Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in some later version.

Bug #38 is tracking this issue. The discussion in PR #61 describes the issue from the Versioneer side in more detail. pip PR#3176 and pip PR#3615 contain work to improve pip to let Versioneer work correctly.

Versioneer-0.16 and earlier only looked for a .git directory next to the setup.cfg, so subprojects were completely unsupported with those releases.

Editable installs with setuptools <= 18.5

setup.py develop and pip install --editable . allow you to install a project into a virtualenv once, then continue editing the source code (and test) without re-installing after every change.

"Entry-point scripts" (setup(entry_points={"console_scripts": ..})) are a convenient way to specify executable scripts that should be installed along with the python package.

These both work as expected when using modern setuptools. When using setuptools-18.5 or earlier, however, certain operations will cause pkg_resources.DistributionNotFound errors when running the entrypoint script, which must be resolved by re-installing the package. This happens when the install happens with one version, then the egg_info data is regenerated while a different version is checked out. Many setup.py commands cause egg_info to be rebuilt (including sdist, wheel, and installing into a different virtualenv), so this can be surprising.

Bug #83 describes this one, but upgrading to a newer version of setuptools should probably resolve it.

Updating Versioneer

To upgrade your project to a new release of Versioneer, do the following:

  • install the new Versioneer (pip install -U versioneer or equivalent)
  • edit setup.cfg and pyproject.toml, if necessary, to include any new configuration settings indicated by the release notes. See UPGRADING for details.
  • re-run versioneer install --[no-]vendor in your source tree, to replace SRC/_version.py
  • commit any changed files

Future Directions

This tool is designed to make it easily extended to other version-control systems: all VCS-specific components are in separate directories like src/git/ . The top-level versioneer.py script is assembled from these components by running make-versioneer.py . In the future, make-versioneer.py will take a VCS name as an argument, and will construct a version of versioneer.py that is specific to the given VCS. It might also take the configuration arguments that are currently provided manually during installation by editing setup.py . Alternatively, it might go the other direction and include code from all supported VCS systems, reducing the number of intermediate scripts.

Similar projects

License

To make Versioneer easier to embed, all its code is dedicated to the public domain. The _version.py that it creates is also in the public domain. Specifically, both are released under the "Unlicense", as described in https://unlicense.org/.

python-versioneer's People

Contributors

alimcmaster1 avatar bastianzim avatar beeftornado avatar bfroehle avatar callek avatar cclauss avatar delirious-lettuce avatar dependabot[bot] avatar dimitripapadopoulos avatar dsoprea avatar effigies avatar foxtran avatar glennmatthews avatar jayvdb avatar jwodder avatar marcelhuberfoo avatar mathijsvdv avatar mcbridejc avatar mgorny avatar moggers87 avatar muggenhor avatar mwtoews avatar pdecat avatar pelson avatar pkittenis avatar rhattersley avatar simaoafonso-pwt avatar warner avatar xoviat avatar yarikoptic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-versioneer's Issues

Simplify unicode handling in setup.py

I noticed that setup.py has a few part of the code where is has to mess with the difference of dealing with unicode in Python 2 and 3. Here are some pointers that might help you in simplifying that, assuming Python 2.6 is the oldest version you want to support:

The io module offers an open() function that under Python 3 is just an alias for open() while under Python 2 provides an open() that does exactly the same as open under Python 3. So the following code works with Python 2 and 3 and builds a list of unicode strings:

import io
with io.open('some.txt', 'r', encoding='utf-8') as text_file:
    text_lines = list(text_file)

Also there is io.StringIO which under Python 2 provides a StringIO that internally uses unicode (unlike StringIO.StringIO). So io.StringIO.write() accepts unicode strings and io.StringIO.getvalue() returns a unicode string - just like under Python 3.

Finally, there is

from ___future__ import unicode_literal

which does nothing under Python 3 but changes the way string literal in the source code are interpreted under Python 2. For example,

some_text = 'this is some text'

would be the same as

some_text = u'this is some text'

If you really need a byte string, just use the 'b' prefix, which is available for Python 2.6 and Python 3, for example:

some_bytes = b'abcd'

python setup.py build fails

Using the latest head version of versioneer, I am running into the following error when doing "python setup.py build"

running build
running build_py 
UPDATING build/lib/_version.py
error: [Errno 2] No such file or directory: 'build/lib/_version.py'

The build/lib actually have the _version.py present - is there anything I missed?

Thanks

_version.py should conform to PEP8

Currently versioneer creates a _version.py with a few minor PEP8 violations. This is slightly annoying for projects where the build process includes a PEP8 validation. It can easily be fixed using

$ autopep8 -i _version.py

Nevertheless it could be nice if versioneer builds a clean _version.py out of the box.

Simple alternative to versioneer

Brian,

Thanks for releasing versioneer; it's been really helpful! We were using versioneer in some of our projects at http://ioam.github.io, but ended up making our own solution that turned out to be simpler in most ways. I wanted to let you have a look at it to see if there are any ideas that you wanted to take back into versioneer, since we so happily took ideas from versioneer. :-)

Our approach (written by Jean-Luc Stevens, with help from Marco Elver and several other people) uses one short public domain file https://github.com/ioam/param/blob/master/param/version.py that we are happy for anyone to reuse in any Python project.

To use our approach, just add version.py into a Python package, then edit the package's __init__.py file to include code like:

from .version import Version
__version__ = Version(release=(1,2,0), fpath=__file__)

Now if someone imports the package and uses __version__ anywhere a string is expected, they will get a string version similar to those created by versioneer (e.g. "1.2.0" for a release or "1.2.0-18-gb62d4d9-dirty" for a modified git version). They can also easily get direct access to the information going into that string by accessing properties of the __version__ object. I.e., this approach basically acts like a string version, but then gives more information when requested, e.g. allowing numeric comparison of different versions. The approach uses lazy evaluation so that the version control system is only accessed when version information is actually requested, and does not require the version control system to be installed for releases.

Compared to versioneer, one shortcoming is that version.py supports only git, though just a few lines are specific to git and it can easily be edited to support a different version control system. The main disadvantage of our approach is that version.py must be included with the release (e.g. on PyPI), not just development versions. But the advantage is that doing so removes the need for self-modifying code, and makes the total amount of code involved much lower. We'd be happy to hear what you think, and hope you might find anything in our approach useful!

New release (0.14)?

Even though there was a release 0.13 not so long ago (and some dev releases since), it seems like a lot of really cool and useful features were added right afterwards (most notably the PEP440 improvements). IMHO these seem worthy of a version up tick. Thoughts?

skip 'git describe' call unless .git exists

https://bugzilla.mozilla.org/show_bug.cgi?id=718115 (which is about a copy of Versioneer that I put into Jetpack) is about a leaked error message that occurs when the version-determining code tries to run git describe in a tree that has no .git directory. (In jetpack's case, this is because we have a git-to-mercurial bridge to support some legacy test automation, so they're running code that comes from an hg checkout, which of course has no .git directory).

The version-checking code correctly resolves version="unknown", but emits a scary "fatal: Not a git repository" message to stderr in the process.

The fix is to do an extra os.path.isdir(".git") check before calling git describe, and return None if .git is not present.

Optionally add the branch to the package name

Assume that for your library my_lib you have a master and a develop branch and you let a continuous integration system like Jenkins build releases for each commit on these branches so that bleeding-edge developers can easily install current development builds of your library. Now just by looking at the pip freeze output it is next to impossible to see if you have installed my_lib from the current development branch or master branch. It would be useful if there was an option in versioneer so that in case of a non-master branch like develop the branch is suffixed to the package like my_lib_develop-1.1-121-gebab194. I know that the ambiguity could also be resolved by building only tagged releases on the master branch but I think my suggestion would make it more explicit.

usage of versioneer for version argument of a programm

I like your tool and use it for auto-naming packages, that works good, but I now try to use it to print out the current version of the installed tool, it did work like I wanted it in the git repos but it does not work if I try to use it with the installed tool, do you have an idea how I could use versioneer in this way. I tryed to implement it in the latest version of mmailer:

spiderbit/mmailer@81ac57e

but like I said it only works before I installed it through setup/distutils.

Git tags - support for multiple versions on same commit

I think that the current mechanism for getting tags (improved in #77) doesn't pick the biggest version if you tag the same commit multiple times for the same application but for different versions.

Basically when I bump a version number for my application without doing a commit (not that common but does happen if we update a package's dependencies) the older tag is getting picked up.

Probably easiest to illustrate just using git and the current "git describe" command:

# git init .
Initialised empty Git repository in /tmp/x/.git/
# touch x && git add x && git commit -m "test"
[master (root-commit) 217feb2] test
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 x
# git tag R_example_1.0.0
# git tag R_example_1.0.1
# git describe --tags --dirty --always --long --match "R_example*"
R_example_1.0.0-0-g217feb2

Here 1.0.0 is getting taken over 1.0.1, clearly I'd like to have 1.0.1 taken over it.

From initial googling there seems to be a way to get the current tags for a commit. I suppose you could take these all in, parse them and pick the biggest? But I guess this doesn't give you the nice things that describe does (like dirty and commits ahead).

py3.2 broken

something with the unicode string in setup.py breaks with py3.2, I think it's the "u" prefix (which was supposed in py2.x and py3.3 but not py3.2). I believe io.write() requires a unicode object. I tried using "stuff".decode("ascii") which is ok for py2.6/py2.7 but rejected in py3.x (because "stuff" is already unicode).

This might require a helper function from six.py, but I'd really like to avoid adding external dependencies. Including a copy of six.py in the tree would be better, but I'd stll prefer to find a smaller trick.

make versioneer a module for something else

Unfortunately, there are a few things that I want to do in my setup.py other than determining the version of something. versioneer seems to be gaining some traction and adoption, so I'd love to hijack that to build my own personal brand. versioneer is a great solution to one aspect of this problem โ€“ particularly the issue where we discover issues with one or another technique for discovering the version of a package, we can fix those bugs centrally instead of once in every project.

I created a quick proof-of-concept I'm calling "metasetup", over at https://gitlab.com/glyph/metasetup which might point in the direction of a more general way that things like versioneer might be implemented. It might make more sense to throw that away and evolve versioneer itself into that more-general thing, or it might make sense to polish that up, make it real, and turn versioneer into the version-calculation plugin for it.

I'd really love some feedback from users and maintainers of versioneer on this idea.

stale versions when running with setuptools

Zooko and I noticed (at Pycon, while versioneer-ifying pycryptopp) that the following sequence:

  • setup.py build
  • make some changes
  • setup.py install

resulted in installed code that had the right version string, but the copy in build/ was stale (I vaguely remember the code in build/ being updated but the version string remaining unmodified). I don't know exactly what was happening, but I remember it making me question my previous assumption that setup.py install would always execute setup.py build (and the associated versioneer hook) first.

versioneer.py should be PEP8-compliant too

(spin-off of #56, which only fixed _version.py)

It'd be nice if the generated versioneer.py were PEP8-compliant. The biggest obstacle is the overlong lines in the docstring, which are copied from the README, which contain URLs for badges (travis, coveralls) and aren't easily splittable. The fix is probably to just omit those lines when building the docstring.

bad interaction: entrypoint script vs 'setup.py develop'

I noticed a bad interaction today. In my "magic-wormhole" project (which uses versioneer), I created a new virtualenv and ran "pip install -e .". The project uses an entrypoint, and it wrote the entrypoint script into venv/bin/wormhole. The generated script looks like:

#!/Users/warner/stuff/tahoe/magic-wormhole/ve/bin/python2.7
# EASY-INSTALL-ENTRY-SCRIPT: 'magic-wormhole==0.2.0+22.g894da44.dirty','console_scripts','wormhole'
__requires__ = 'magic-wormhole==0.2.0+22.g894da44.dirty'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('magic-wormhole==0.2.0+22.g894da44.dirty', 'console_scripts', 'wormhole')()
    )

This works fine until I make a commit, which changes the version that setup.py version reports into something different than the one frozen into the entrypoint script. After making a commit, running the script gives me an error:

pkg_resources.DistributionNotFound: The 'magic-wormhole==0.2.0+22.g894da44.dirty' distribution was not found and is required by the application

That makes me sad: if I want to use the virtualenv for testing the executable, I have to re-run pip install -e . after basically every commit.

I think one workaround might be to avoid entrypoints and just use a regular scripts= argument (in setup.py), with a small executable that does a plain import-and-run. I haven't been able to come up with a better idea.

publish a release artifact of some sort

Amber recently used Versioneer for one of her projects, and noted that it wasn't particularly obvious how to actually obtain a copy of versioneer.py. She wound up doing a 'git clone' and then copying the file out of that repo.

I make release tags every once in a while, but I've never actually published any artifacts of those releases. This ticket is to decide on a suitable artifact.

Really, the only file that needs publishing is the versioneer.py script: it contains instructions (as comments), should be copied into the target project, and should be executed to perform some installation steps.

It might also be nice to include the README.rst too, since that's where anyone sane would expect instructions to be found.

The rest of the source files (tests, source code that gets assembled into versioneer.py, and the script that does the assembly) doesn't strictly need to be in a user-facing build artifact, and might even be confusing if it were present.

So, one option: produce a tarball that contains only the README and versioneer.py, and publish it through github's release-artifacts feature and/or pypi.

It's not something you can "install", more like something you unpack and then copy. But maybe it'd be easier to use if we actually distributed an "install-versioneer" executable, which could find an associated copy of versioneer.py and copy it into place.

Multiple tags result in "unknown" even if correct tag exists

We want to use versioneer to version multiple python packages in a single git repo.

I have 2 setup.cfgs with:

tag_prefix=ExampleA-

and

tag_prefix=ExampleB-

In my case I have 2 directories with versioneer installed. And I tag a single commit twice:

git tag ExampleA-1.2.3
git tag ExampleB-2.3.4

I was assuming that doing an sdist upload would result in the ExampleA being versioned as 1.2.3 but it goes up as "unknown".

Turning on verbose I get

tag 'ExampleB-2.3.4' doesn't start with prefix 'ExampleA-'

Looking at the code it looks like this is from the use of "git describe" which takes the most recent commit. So I suppose this would need to be changed to look at all the tags, not just the most recent one.

Do you envisage supporting this use case? Or am I missing something?

Git replacements may not always be applied

It looks like .gitattributes has the right stuff, but the replacements aren't happening into _version.py after the commit/tag (for me).

Any idea what could be happening?

I sent you an email, but you must've missed it.

PyPi broken?

It seems like this package isn't currently in PyPI. The package as indicated ("versioneer") isn't found, and there are no current packages with "versioneer" in the package name.

no version until first tag is added

The current code only reports a version string for revisions that include at least one tag in their ancestry. So you'll get "unknown" until you add the first tag.

The first task is to document this clearly.

The second task is to try and fix it usefully. We use git describe to find out what the revision is. If that doesn't see a tag and is unhappy, we should at least extract the raw hash. Formatting this is an open question. We usually use TAG-DISTANCE-gHASH[-dirty]. If we don't have a tag, we could just leave that blank, or replace it with some string like "notag". It'd be swell if the results sorted nicely (so "notag" or "" sorted before any real tag, noting that real tags will generally be like "1.2" since we strip any prefix first).

should versions be unicode?

So I'm working on the "style" branch, to make it possible to configure the style of version string that you want, and I've run into a question that I need help with. Should the version string exposed to your program, on python2, be bytes or unicode? How about on python3?

In earlier versions, I think we've been doing bytes on py2 and str (ie unicode) on py3. In a sense, these are the "normal" printable-string types on those platforms. Versions are frequently printed in response to a CLI command, sometimes stored in files (debug logs, sometimes as a compatibility marker for serialized data), and occasionally sent over the network (again for compatibility).

Two things are affecting my viewpoint:

  • the new branch uses JSON to store version information for the "from-file" case (where you've run setup.py build and it writes pre-rendered version data into the _version.py), and python's modern JSON module likes to return unicode keys and values on py2, even if you wrote bytes in the first place.
  • git tags can nominally be unicode, directory-name prefixes could nominally be unicode, and we derive versions from both of those. So maybe versions should be unicode too.

The switch might require applications to explicitly .encode() the version before sending it to something that really wants bytes, like a logfile or (maybe even) stdout.

Thoughts?

add SVN support

Dustin Oprea wrote:

I'll do it, or give up as a broken man.

It looks like the versioneer.py content is baked into
versioneer-installer. We'll have to be able to pass the VCS name into
it, in order to adopt a second VCS, right?

Yes on 1 and maybe on 2. I see a couple of approaches to a "polyglot" Versioneer:

  • 1: decide on the VCS at install time. You'd run versioneer-installer svn, and the resulting versioneer.py would only have SVN support. We'd need to embed multiple alternatives in versioneer-installer
  • 2: decide on the VCS at "update files" time. You'd run a bare versioneer-installer, which would create a versioneer.py that can handle all supported VCSes. Then you configure which VCS you're using in setup.py (maybe by setting versioneer.vcs = "svn"). When you run update_files, you'd get a generated SRC/_version.py which only supports SVN.
  • 3: decide at version-computation time: no config necessary, the generated SRC/_version.py would look for signs of every VCS it knows about, and computes a version from the first one that works.

Option 3 would be the easiest to use, although probably a bit tricky to implement. The versions_from_vcs() function could probably look like:

if os.path.isdir(".git"):
    versions = version_from_git()
    if versions:
        return versions
if os.path.isdir(".svn):
    versions = version_from_svn()
    if versions:
        return versions
return None

The expanded variables ($Format:%d$) need to be VCS-specific (and not all of them support variable expansion), so either we'd need to deduce the VCS and then write the strings, or write strings for all supported VCSes. It might also be useful to write the deduced VCS name next to the strings, so versions_from_expanded_variables() could only pay attention to the string that's likely to change.

A hybrid between 2 and 3 might work too: create a polyglot _version.py but interpolate in the name of the VCS being used, which then switches off some of the code paths.

One quirk: some projects are managed in multiple VCSes at the same time: we maintained Tahoe in both darcs and git for a couple of years, with a (fragile) automated two-way bridge. Sometimes that results in VCS metadata for both trees being present at the same time, which might confuse our automatic detection. We can defer supporting this case until someone complains, but eventually it may be useful to let folks configure setup.py to only try a specific VCS.

Compatibility with setuptools w.r.t the `sdist` command

Both versioneer and setuptools override the sdist class of distutils.command.sdist, hard-wiring it as their base-class.
This renders the setuptools 'sdist' command unusable when versioneer is used with setuptools, as

setup(
    ...
    cmdclass=versioneer.get_cmdclass(),
    ...
    )

overrides setuptools.command.sdist.sdist with verisoneer.cmd_sdist.

One way to work around this is:

from setuptools import setup, Extension

# Monkey-patch distutils.command.sdist so that versioneer derives its 
# command class from setuptools, not distutils
from setuptools.command.sdist import sdist as setupToolsSdistClass
import distutils.command.sdist as distutilsSdistModule
distutilsSdistModule.sdist = setupToolsSdistClass
# Now we can import verioneer
import versioneer

Of course it would be much nicer if versioneer could be told to derive its command objects from the command of another distutils extension (be that setuptools or something else). For example get_cmdclass() could take a dictionary containing command objects to derive from.

rename "update_files" command to something versioneer-specific

When developers use a versioneer-enabled project, and they run setup.py --help-commands to see what they can do, they'll see a command named "update_files", which at best is confusing. We should rename that command to something with "versioneer" in the name.

We already have a make_versioneer (for debugging/testing, to create a standalone versioneer.py), but we could probably get rid of that.

Should we call the new command setup.py install_versioneer_files? or versioneer_install_files? It's also used for updating an existing installation (to upgrade: re-run versioneer-installer, then re-run setup.py COMMAND).

Contents of "src/from_file.py" not correctly escaped.

I'm getting this error:

ValueError: unsupported format character 'v' (0x76) at index 697

Because the contents of from_file.py includes the following:

return version_string_template % versions

This will be fixed by a commit in a moment.

problems with `pip install` from a source tree

I tried to pip install .../path/to/sourcetree on a project that uses versioneer, and it failed with the following message:

error: build/lib/petmail/_version.py: No such file or directory

It's possible that I've installed versioneer into this project strangely (I need to test it with a more-established project), but I think it's also likely that something I did in versioneer is incompatible with however pip is processing setup.py. Needs investigation.

prep work for multiple-VCS support

#25 is about SVN, #6 is about bzr. It'd be easier for other folks to write these backends if the code made room for them: choose one of the proposed strategies from #25, add a second (dummy) system, make it clear what needs to be added for a real one.

Set up test suite code coverage reporting

Automatic test coverage is an important part of any software project. Happily python-versioneer already has an automated test suite and a continuous integration system to run it for every change. This is excellent. An improvement to this would be to generate coverage reports (lines/branches actually executed by the test suite) to demonstrate that python-versioneer actually has great test coverage and avoid accidentally regressing that test coverage. This information can be advertised alongside the existing information about python-versioneer's continuous integration results in the readme.

This would be of particular benefit to other projects which are tracking their own coverage level - since python-versioneer injects its own source code into those projects. They will want to be able to see that python-versioneer's code is well-tested since a copy of it ends up in their project (and they probably won't want to write their own tests for it!).

Python setup can not be located in a sub-directory of a repository

The versioneer script does not allow the creation of python modules which are sub-directories of a git repository.

ex:

repo/
   .git
   module1/
       setup.py
       versioneer.py
           module1/
          __init__.py
           stuff.py
    module2/
       setup.py
       versioneer.py
       module2/
           __init__.py
           morestuff.py

While this directory structure may not be a best practice, it can be a useful construct when building rapid prototypes.

get_versions fails on windows due to path.sep conflict

On Windows, versioneer is reporting the version for my project as unknown, although I have numerous git tags defined. I have a _version.py file installed in hexrd/_version.py. In that file I find the following:

    try:
        root = os.path.abspath(__file__)
        # versionfile_source is the relative path from the top of the source
        # tree (where the .git directory might live) to this file. Invert
        # this to find the root from __file__.
        for i in range(len(versionfile_source.split(os.sep))):
            root = os.path.dirname(root)
    except NameError:
        return default

The problem, I think, is that I ran versioneer on a unix system, and it created versionfile_source with a unix pathsep. Now on windows, it is trying to split using a windows pathsep.

I think this could be improved:

    try:
        root = __file__ # already an abspath
        # versionfile_source is the relative path from the top of the source
        # tree (where the .git directory might live) to this file. Invert
        # this to find the root from __file__.
        for i in versionfile_source.split('/'): # no need for range(len())
            root = os.path.dirname(root)
    except NameError:
        return default

and the versionfile_source variable should be formatted with unix pathseps on all platforms. However, I don't see where these changes should be made in your repo. Alternatively, you could do:

    try:
        root = __file__ # already an abspath
        # versionfile_source is the relative path from the top of the source
        # tree (where the .git directory might live) to this file. Invert
        # this to find the root from __file__.
        for i in os.path.relpath(versionfile_source).split(os.path.pathsep): # no need for range(len())
            root = os.path.dirname(root)
    except NameError:
        return default

Duplicate imports in generated files

Thanks for Versioneer - it's great!

We run pyflakes on our code; it complains about some of the generated Versioneer files:

$ pyflakes versioneer.py param/_version.py
versioneer.py:450: redefinition of unused 'sys' from line 245
versioneer.py:486: redefinition of unused 'sys' from line 450
versioneer.py:487: redefinition of unused 're' from line 245
versioneer.py:592: redefinition of unused 'os' from line 488
versioneer.py:593: redefinition of unused 'sys' from line 486
param/_version.py:53: redefinition of unused 'sys' from line 17

This is from Versioneer 0.9 (https://pypi.python.org/pypi/versioneer/0.9).

I checked with current git Versioneer (0.10-30-ge20ecd9), and find something similar:

$ pyflakes versioneer.py param/_version.py
versioneer.py:265: redefinition of unused 'sys' from line 248
versioneer.py:495: redefinition of unused 're' from line 248
versioneer.py:560: redefinition of unused 'sys' from line 265
versioneer.py:594: redefinition of unused 'os' from line 561
versioneer.py:595: redefinition of unused 'sys' from line 560
versioneer.py:675: redefinition of unused 'sys' from line 595
param/_version.py:134: redefinition of unused 'sys' from line 22

tests fail against git-1.8.3

They worked against git-1.8.2.3 (and earlier), but fail when run with git-1.8.3 (and later).

FAIL: test_full (__main__.Repo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test/test_git.py", line 115, in test_full
    self.do_checks(short, full, dirty=False, state="SA")
  File "test/test_git.py", line 167, in do_checks
    self.check_version(target, exp_short_TD, exp_long, False, state, tree="TD")
  File "test/test_git.py", line 201, in check_version
    self.compare(v, exp_short, state, tree, "RA")
  File "test/test_git.py", line 189, in compare
    % (where, got, expected))
AssertionError: SA/TD/RA: got 'bcf1f672131090156fb30bd9597799cb73d68693' != expected '1.0'

MANIFEST.in lines are sometimes (often? usually?) redundant

in my setup.py file I already do this:

setup(
    ...
    packages=["mypackage", ...],
)

This automatically includes all of the modules in mypackage. It seems likely that most setup.py files are written like this or using the setuptools equivalent (find_modules or something) - though I see how it's not necessarily the case that they are.

versioneer-install adds mypackage/_version.py to MANIFEST.in anyway. This seems to be harmless but it is redundant and a distraction at best. At worst it makes things confusing for someone trying to change how the package works (since they must now edit two things in order to change whether/how _version.py gets packaged).

It would be pretty cool not to add this file to MANIFEST.in if it is redundant to do so.

using versioneer in multiple setuptools dependant projects leads to interchanged version numbers

I know only distutils is supported. But still I wanted to report this (maybe to avoid others run into this). I will try to figure out whats going on.

Lets say A depends B (both using versioneer). B is in on pypi with a source tarball, so have a static _version.py file.

A drags in B during setup phase, and version of B gets replaced by version of A.
Can be observed here: https://travis-ci.org/markovmodel/PyEMMA/builds/40644627#L623

Maybe this also occurs if A and B are using distutils (have not tested).

dirty versions are not PEP440 compatible

Is it possible to achieve compatibility?

newest setuptools raises a warning:

setuptools-11.3.1-py2.7.egg/setuptools/dist.py:292: UserWarning: The version specified ('1.0a1-83-g89149e7') is an invalid version, this may not work as expected with newer versions of setuptools, pip, and PyPI. Please see PEP 440 for more details.

problems with setup.cfg tag_prefix = ""

When I have in my setup.cfg file:

[versioneer]
VCS = git
style = pep440
tag_prefix = ""
...

then in Python 2 I have cfg.tag_prefix == '' and in Python 3 cfg.tag_prefix == '""'. Solution is to make setup.cfg looks like:

[versioneer]
VCS = git
style = pep440
tag_prefix = 
...

Then in Python 2 and Python 3 cfg.tag_prefix == ''

PyPI import problem

PyPI can't successfully install a versioneer-enabled package because it's doing the import of visioneer, when pip won't be installing it using the package's directory as the CWD.

As a working fix, I tried adjusting the path, prior:

from os.path import dirname
sys.path.insert(0, dirname(__file__))

import versioneer

Unfortunately, I'm having other build problems that are preventing me from testing it. My current "git describe" says that I'm at 0.2.5, but setup.py built a "0.2.5-dirty" package, which subsequently got uploaded. I suspect that PIP isn't considering this as a candidate, and, therefore, won't upgrade the package. Any idea why I'm getting the "dirty" suffix?

get version from bzr

davidsarah: zooko: are you seeing the build failure notices for the pycryptopp package in oneiric?
davidsarah: * Source Package: pycryptopp
davidsarah: * Version: 0.5.29+877+11~oneiric1
davidsarah: * Architecture: i386
davidsarah: * Archive: tahoe-lafs-daily PPA
davidsarah: * Component: main
davidsarah: * State: Failed to build
davidsarah: * Duration: 12 minutes
davidsarah: * Build Log: https://launchpad.net/~tahoe-lafs/+archive/daily/+build/3653853/+files/buildlog_ubuntu-oneiric-i386.pycryptopp_0.5.29%2B877%2B11%7Eoneiric1_FAILEDTOBUILD.txt.gz
davidsarah: * Builder: https://launchpad.net/builders/protactinium
davidsarah: * Source: not available

zooko: Yes.
zooko: The copy of "versioneer" in pycryptopp doesn't know how to make a reaasonable version number when it is being run from bzr rather than from git or from a sdist.
zooko: The best fix is someone to extend versioneer to read bzr the way it reads git, I suppose.
zooko: Other workarounds should certainly be doable...

versions_from_file() doesn't appear to work

After I do an "sdist upload" of a package, a "pip3 install " fails because the version can't be recovered from _version.py, and there isn't going to be any VCS metadata to fallback on.

This is essentially the main content of my _version.py (in the package about to be downloaded and installed via pip):

versions = {'full_revisionid': '7574780f82745fcfeec579d11628cbae9721fdb8', 'describe': '0.3.10', 'dash_dirty': '', 'distance': 0, 'default': '0.3.10', 'closest_tag_or_zero': '0.3.10', 'pep440': '0.3.10', 'long': '0.3.10-0-g7574780', 'closest_tag': '0.3.10', 'dirty': False, 'short_revisionid': '7574780', 'dash_distance': ''}

def get_versions(default={}, verbose=False):
    return versions

def get_version():
    return version_string_template % versions

However, none of the following conditionals hit (none of the lines match):

            for line in f.readlines():
                mo = re.match("version_version = '([^']+)'", line)
                if mo:
                    versions["version"] = mo.group(1)
                mo = re.match("version_full = '([^']+)'", line)
                if mo:
                    versions["full"] = mo.group(1)

This bug means that PyPI-based installs will always fail. Please confirm. I think this predates my changes.

add version components, string_template

This ticket is to add raw components to the dictionary returned by get_versions(), and to provide some sort of versioneer.string_template= config setting to control what the default get_version() returns. The details are in a comment in #24.

might better to double/tripple/... license that unsafe generic "public domain"

"is hereby released into the public domain" in itself doesn't e.g. provide any disclaimers for absent warranty etc, like any license has (e.g.BSD-3 or MIT/expat), so theoretically (IANAL) you might be left responsible for every harmful use of your code ;) So I wondered why didn't you just release it under some very open license which would not have (virtual) any conflicts? E.g. BSD-3 or MIT/Expat... or may be both of those ;)

Setting tag_prefix to the empty string

In the readme, the example setup.cfg has the line:

tag_prefix = ""

This seemed to me to be saying that the tag_prefix is the empty string, but actually I found the empty string is expressed as:

tag_prefix =

It seemed only to be a problem with Python 3.

Btw, versioneer is a fantastic bit of software, thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.