Skip to content

Commit

Permalink
Start using versioneer for version numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
jborg committed Aug 5, 2013
1 parent 781a955 commit 3b4875c
Show file tree
Hide file tree
Showing 7 changed files with 873 additions and 12 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include README.rst LICENSE CHANGES MANIFEST.in
include README.rst LICENSE CHANGES MANIFEST.in versioneer.py
recursive-include docs *
recursive-exclude docs *.pyc
recursive-exclude docs *.pyo
Expand Down
5 changes: 3 additions & 2 deletions attic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# This is a python package

__version__ = '0.7'
__release__ = __version__ # + '.dev'
from ._version import get_versions
__version__ = get_versions()['version']
del get_versions
197 changes: 197 additions & 0 deletions attic/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@

IN_LONG_VERSION_PY = True
# This file helps to compute a version number in source trees obtained from
# git-archive tarball (such as those provided by githubs download-from-tag
# feature). Distribution tarballs (build by setup.py sdist) and build
# directories (produced by setup.py build) will contain a much shorter file
# that just contains the computed version number.

# This file is released into the public domain. Generated by
# versioneer-0.7+ (https://github.com/warner/python-versioneer)

# these strings will be replaced by git during git-archive
git_refnames = "$Format:%d$"
git_full = "$Format:%H$"


import subprocess
import sys

def run_command(args, cwd=None, verbose=False):
try:
# remember shell=False, so use git.cmd on windows, not just git
p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)
except EnvironmentError:
e = sys.exc_info()[1]
if verbose:
print("unable to run %s" % args[0])
print(e)
return None
stdout = p.communicate()[0].strip()
if sys.version >= '3':
stdout = stdout.decode()
if p.returncode != 0:
if verbose:
print("unable to run %s (error)" % args[0])
return None
return stdout


import sys
import re
import os.path

def get_expanded_variables(versionfile_source):
# the code embedded in _version.py can just fetch the value of these
# variables. When used from setup.py, we don't want to import
# _version.py, so we do it with a regexp instead. This function is not
# used from _version.py.
variables = {}
try:
for line in open(versionfile_source,"r").readlines():
if line.strip().startswith("git_refnames ="):
mo = re.search(r'=\s*"(.*)"', line)
if mo:
variables["refnames"] = mo.group(1)
if line.strip().startswith("git_full ="):
mo = re.search(r'=\s*"(.*)"', line)
if mo:
variables["full"] = mo.group(1)
except EnvironmentError:
pass
return variables

def versions_from_expanded_variables(variables, tag_prefix, verbose=False):
refnames = variables["refnames"].strip()
if refnames.startswith("$Format"):
if verbose:
print("variables are unexpanded, not using")
return {} # unexpanded, so not in an unpacked git-archive tarball
refs = set([r.strip() for r in refnames.strip("()").split(",")])
for ref in list(refs):
if not re.search(r'\d', ref):
if verbose:
print("discarding '%s', no digits" % ref)
refs.discard(ref)
# Assume all version tags have a digit. git's %d expansion
# behaves like git log --decorate=short and strips out the
# refs/heads/ and refs/tags/ prefixes that would let us
# distinguish between branches and tags. By ignoring refnames
# without digits, we filter out many common branch names like
# "release" and "stabilization", as well as "HEAD" and "master".
if verbose:
print("remaining refs: %s" % ",".join(sorted(refs)))
for ref in sorted(refs):
# sorting will prefer e.g. "2.0" over "2.0rc1"
if ref.startswith(tag_prefix):
r = ref[len(tag_prefix):]
if verbose:
print("picking %s" % r)
return { "version": r,
"full": variables["full"].strip() }
# no suitable tags, so we use the full revision id
if verbose:
print("no suitable tags, using full revision id")
return { "version": variables["full"].strip(),
"full": variables["full"].strip() }

def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):
# this runs 'git' from the root of the source tree. That either means
# someone ran a setup.py command (and this code is in versioneer.py, so
# IN_LONG_VERSION_PY=False, thus the containing directory is the root of
# the source tree), or someone ran a project-specific entry point (and
# this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the
# containing directory is somewhere deeper in the source tree). This only
# gets called if the git-archive 'subst' variables were *not* expanded,
# and _version.py hasn't already been rewritten with a short version
# string, meaning we're inside a checked out source tree.

try:
here = os.path.abspath(__file__)
except NameError:
# some py2exe/bbfreeze/non-CPython implementations don't do __file__
return {} # not always correct

# 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__.
root = here
if IN_LONG_VERSION_PY:
for i in range(len(versionfile_source.split("/"))):
root = os.path.dirname(root)
else:
root = os.path.dirname(here)
if not os.path.exists(os.path.join(root, ".git")):
if verbose:
print("no .git in %s" % root)
return {}

GIT = "git"
if sys.platform == "win32":
GIT = "git.cmd"
stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],
cwd=root)
if stdout is None:
return {}
if not stdout.startswith(tag_prefix):
if verbose:
print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix))
return {}
tag = stdout[len(tag_prefix):]
stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root)
if stdout is None:
return {}
full = stdout.strip()
if tag.endswith("-dirty"):
full += "-dirty"
return {"version": tag, "full": full}


def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False):
if IN_LONG_VERSION_PY:
# We're running from _version.py. If it's from a source tree
# (execute-in-place), we can work upwards to find the root of the
# tree, and then check the parent directory for a version string. If
# it's in an installed application, there's no hope.
try:
here = os.path.abspath(__file__)
except NameError:
# py2exe/bbfreeze/non-CPython don't have __file__
return {} # without __file__, we have no hope
# versionfile_source is the relative path from the top of the source
# tree to _version.py. Invert this to find the root from __file__.
root = here
for i in range(len(versionfile_source.split("/"))):
root = os.path.dirname(root)
else:
# we're running from versioneer.py, which means we're running from
# the setup.py in a source tree. sys.argv[0] is setup.py in the root.
here = os.path.abspath(sys.argv[0])
root = os.path.dirname(here)

# Source tarballs conventionally unpack into a directory that includes
# both the project name and a version string.
dirname = os.path.basename(root)
if not dirname.startswith(parentdir_prefix):
if verbose:
print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" %
(root, dirname, parentdir_prefix))
return None
return {"version": dirname[len(parentdir_prefix):], "full": ""}

tag_prefix = ""
parentdir_prefix = "Attic-"
versionfile_source = "attic/_version.py"

def get_versions(default={"version": "unknown", "full": ""}, verbose=False):
variables = { "refnames": git_refnames, "full": git_full }
ver = versions_from_expanded_variables(variables, tag_prefix, verbose)
if not ver:
ver = versions_from_vcs(tag_prefix, versionfile_source, verbose)
if not ver:
ver = versions_from_parentdir(parentdir_prefix, versionfile_source,
verbose)
if not ver:
ver = default
return ver

4 changes: 2 additions & 2 deletions attic/archiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import stat
import sys

from attic import __release__
from attic import __version__
from attic.archive import Archive
from attic.repository import Repository
from attic.cache import Cache
Expand Down Expand Up @@ -343,7 +343,7 @@ def run(self, args=None):
default=False,
help='verbose output')

parser = argparse.ArgumentParser(description='Attic %s - Deduplicated Backups' % __release__)
parser = argparse.ArgumentParser(description='Attic %s - Deduplicated Backups' % __version__)
subparsers = parser.add_subparsers(title='Available commands')

subparser = subparsers.add_parser('serve', parents=[common_parser])
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
# The short X.Y version.
version = attic.__version__
# The full version, including alpha/beta/rc tags.
release = attic.__release__
release = attic.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
21 changes: 15 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
from glob import glob
import attic

import versioneer
versioneer.versionfile_source = 'attic/_version.py'
versioneer.versionfile_build = 'attic/_version.py'
versioneer.tag_prefix = ''
versioneer.parentdir_prefix = 'Attic-' # dirname like 'myproject-1.2.0'


min_python = (3, 2)
if sys.version_info < min_python:
print("Attic requires Python %d.%d or later" % min_python)
Expand All @@ -13,7 +20,6 @@
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup, Extension
from distutils.command.sdist import sdist

chunker_source = 'attic/chunker.pyx'
hashindex_source = 'attic/hashindex.pyx'
Expand All @@ -22,19 +28,19 @@
from Cython.Distutils import build_ext
import Cython.Compiler.Main as cython_compiler

class Sdist(sdist):
class Sdist(versioneer.cmd_sdist):
def __init__(self, *args, **kwargs):
for src in glob('attic/*.pyx'):
cython_compiler.compile(glob('attic/*.pyx'),
cython_compiler.default_options)
sdist.__init__(self, *args, **kwargs)
versioneer.cmd_sdist.__init__(self, *args, **kwargs)

def make_distribution(self):
self.filelist.extend(['attic/chunker.c', 'attic/_chunker.c', 'attic/hashindex.c', 'attic/_hashindex.c'])
super(Sdist, self).make_distribution()

except ImportError:
class Sdist(sdist):
class Sdist(versioneer.cmd_sdist):
def __init__(self, *args, **kwargs):
raise Exception('Cython is required to run sdist')

Expand All @@ -47,9 +53,12 @@ def __init__(self, *args, **kwargs):
with open('README.rst', 'r') as fd:
long_description = fd.read()

cmdclass = versioneer.get_cmdclass()
cmdclass.update({'build_ext': build_ext, 'sdist': Sdist})

setup(
name='Attic',
version=attic.__release__,
version=versioneer.get_version(),
author='Jonas Borgström',
author_email='[email protected]',
url='https://pythonhosted.org/Attic/',
Expand All @@ -71,7 +80,7 @@ def __init__(self, *args, **kwargs):
],
packages=['attic', 'attic.testsuite'],
scripts=['scripts/attic'],
cmdclass={'build_ext': build_ext, 'sdist': Sdist},
cmdclass=cmdclass,
ext_modules=[
Extension('attic.chunker', [chunker_source]),
Extension('attic.hashindex', [hashindex_source])
Expand Down
Loading

0 comments on commit 3b4875c

Please sign in to comment.