views:

244

answers:

5

How could I get the version defined in setup.py from my package? (for --version, or other purposes)

thanks!

A: 

Your question is a little vague, but I think what you are asking is how to specify it.

You need to define __version__ like so:

__version__ = '1.4.4'

And then you can confirm that setup.py knows about the version you just specified:

% ./setup.py --version
1.4.4
jathanism
I am not sure I understand either your answer :)
elmarco
I'm sorry, I will clarify.
jathanism
+5  A: 

The best technique is to define __version__ in your product code, then import it into setup.py from there. This gives you a value you can read in your running module, and have only one place to define it.

The values in setup.py are not installed, and setup.py doesn't stick around after installation.

What I did (for example) in coverage.py:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

setup(
    name = 'coverage',
    version = __version__,
    ...
    )
Ned Batchelder
that could be a solution I believe
elmarco
+1 That's what I use. I'm not sure why @pjeby is so against this method. If you use "from module import version" and version is specified at the top of the source it should only pull that value from the source not matter how broken (or how many dependencies are missing from) the source file.
Evan Plaice
@Evan: I'm not sure what you are getting at about "only pull that value from the source". If the file with `__version__` in it is broken in some way, then your import will also be broken. Python doesn't know how to only interpret the statements you want. @pjeby is right: if your module needs to import other modules, they may not be installed yet, and it will be chaotic. This technique works if you are careful that the import doesn't caues a long chain of other imports.
Ned Batchelder
@Ned Batchelder I'm pretty sure that, if you put the version before any imports in the source file and only to a 'from module import version' it won't lex any more of the source file than it needs to. Besides, who is going to release broken code? If the package needs dependencies use setuptools or wait for the release of distutils2 later on in the year.
Evan Plaice
@Evan, I'm sorry, but you are wrong about importing partial files. Try putting a print statement at the end of a long module, and import the first variable defined in the file. The print statement will execute.
Ned Batchelder
@Ned Batchelder Ok, I'll try it out and get back to you with the results.
Evan Plaice
+6  A: 

To retrieve the version from inside your package at runtime (what your question appears to actually be asking), you can use:

version = pkg_resources.require("MyProject")[0].version

If you want to go the other way 'round (which appears to be what other answer authors here appear to have thought you were asking), make a version.py in your package with a __version__ line, then read it from setup.py using execfile('mypackage/version.py'), so that it sets __version__ in the setup.py namespace.

By the way, DO NOT import your package from your setup.py as suggested in another answer here: it will seem to work for you (because you already have your package's dependencies installed), but it will wreak havoc upon new users of your package, as they will not be able to install your package without manually installing the dependencies first.

pjeby
A: 

To avoid importing a file (and thus executing its code) one could parse it and recover the version attribute from the syntax tree:

# assuming 'path' holds the path to the file

import ast

with open(path, 'rU') as file:
    t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST)
    for node in (n for n in t.body if isinstance(n, ast.Assign)):
        if len(node.targets) == 1:
            name = node.targets[0]
            if isinstance(name, ast.Name) and \
                    name.id in ('__version__', '__version_info__', 'VERSION'):
                v = node.value
                if isinstance(v, ast.Str):
                    version = v.s
                    break
                if isinstance(v, ast.Tuple):
                    r = []
                    for e in v.elts:
                        if isinstance(e, ast.Str):
                            r.append(e.s)
                        elif isinstance(e, ast.Num):
                            r.append(str(e.n))
                    version = '.'.join(r)
                    break

This code tries to find the __version__ or VERSION assignment at the top level of the module return is string value. The right side can be either a string or a tuple.

Mikhail Edoshin
A: 

Create a file in your source tree, e.g. in yourbasedir/yourpackage/_version.py . Let that file contain only a single line of code, like this:

__version__ = "1.1.0-r4704"

Then in your setup.py, open that file and parse out the version number like this:

verstr = "unknown"
try:
    verstrline = open('yourpackage/_version.py', "rt").read()
except EnvironmentError:
    pass # Okay, there is no version file.
else:
    VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
    mo = re.search(VSRE, verstrline, re.M)
    if mo:
        verstr = mo.group(1)
    else:
        raise RuntimeError("unable to find version in yourpackage/_version.py")

Finally, in yourbasedir/yourpackage/__init__.py import _version like this:

__version__ = "unknown"
try:
    from _version import __version__
except ImportError:
    # We're running in a tree that doesn't have a _version.py, so we don't know what our version is.
    pass

An example of code that does this is the "pyutil" package that I maintain. (See PyPI or google search -- stackoverflow is disallowing me from including a hyperlink to it in this answer.)

@pjeby is right that you shouldn't import your package from its own setup.py. That will work when you test it by creating a new Python interpreter and executing setup.py in it first thing: python setup.py, but there are cases when it won't work. That's because import youpackage doesn't mean to read the current working directory for a directory named "yourpackage", it means to look in the current sys.modules for a key "yourpackage" and then to do various things if it isn't there. So it always works when you do python setup.py because you have a fresh, empty sys.modules, but this doesn't work in general.

For example, what if py2exe is executing your setup.py as part of the process of packaging up an application? I've seen a case like this where py2exe would put the wrong version number on a package because the package was getting its version number from import myownthing in its setup.py, but a different version of that package had previously been imported during the py2exe run. Likewise, what if setuptools, easy_install, distribute, or distutils2 is trying to build your package as part of a process of installing a different package that depends on yours? Then whether your package is importable at the time that its setup.py is being evaluated, or whether there is already a version of your package that has been imported during this Python interpreter's life, or whether importing your package requires other packages to be installed first, or has side-effects, can change the results. I've had several struggles with trying to re-use Python packages which caused problems for tools like py2exe and setuptools because their setup.py imports the package itself in order to find its version number.

By the way, this technique plays nicely with tools to automatically create the yourpackage/_version.py file for you, for example by reading your revision control history and writing out a version number based on the most recent tag in revision control history. Here is a tool that does that for darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst and here is a code snippet which does the same thing for git: http://github.com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34

Zooko