Commit c6c46864 authored by Aron Helser's avatar Aron Helser

Merge branch 'upstream-incremental' into webserver_update

* upstream-incremental:
  incremental 2017-07-25 (326b1b5f)
parents b603b773 2ca80b52
......@@ -13,7 +13,7 @@ API documentation can be found `here <https://hawkowl.github.io/incremental/docs
Quick Start
-----------
Add this to your ``setup.py``\ 's ``setup()`` call:
Add this to your ``setup.py``\ 's ``setup()`` call, removing any other versioning arguments:
.. code::
......@@ -25,24 +25,78 @@ Add this to your ``setup.py``\ 's ``setup()`` call:
}
Then in your project add a ``_version.py`` that contains:
Then run ``python -m incremental.update <projectname> --create`` (you will need ``click`` installed from PyPI).
It will create a file in your package named ``_version.py`` and look like this:
.. code::
from incremental import Version
__version__ = Version("widgetbox", 1, 2, 3)
__version__ = Version("widgetbox", 17, 1, 0)
__all__ = ["__version__"]
Then, so users of your project can find your version, in your project's ``__init__.py`` add:
Then, so users of your project can find your version, in your root package's ``__init__.py`` add:
.. code::
from ._version import __version__
Subsequent installations of your project will use incremental for versioning.
Subsequent installations of your project will then use Incremental for versioning.
Incremental Versions
--------------------
``incremental.Version`` is a class that represents a version of a given project.
It is made up of the following elements (which are given during instantiation):
- ``package`` (required), the name of the package this ``Version`` represents.
- ``major``, ``minor``, ``micro`` (all required), the X.Y.Z of your project's ``Version``.
- ``release_candidate`` (optional), set to 0 or higher to mark this ``Version`` being of a release candidate (also sometimes called a "prerelease").
- ``dev`` (optional), set to 0 or higher to mark this ``Version`` as a development release.
You can extract a PEP-440 compatible version string by using the following methods:
- ``.local()``, which returns a ``str`` containing the full version plus any Git or SVN information, if available. An example output would be ``"17.1.1rc1+r123"`` or ``"3.7.0+rb2e812003b5d5fcf08efd1dffed6afa98d44ac8c"``.
- ``.public()``, which returns a ``str`` containing the full version, without any Git or SVN information. This is the version you should provide to users, or publicly use. An example output would be ``"13.2.0"``, ``"17.1.2dev1"``, or ``"18.8.0rc2"``.
Calling ``repr()`` with a ``Version`` will give a Python-source-code representation of it, and calling ``str()`` with a ``Version`` will provide a string similar to ``'[Incremental, version 16.10.1]'``.
Updating
--------
Incremental includes a tool to automate updating your Incremental-using project's version called ``incremental.update``.
It updates the ``_version.py`` file and automatically updates some uses of Incremental versions from an indeterminate version to the current one.
It requires ``click`` from PyPI.
``python -m incremental.update <projectname>`` will perform updates on that package.
The commands that can be given after that will determine what the next version is.
- ``--newversion=<version>``, to set the project version to a fully-specified version (like 1.2.3, or 17.1.0dev1).
- ``--rc``, to set the project version to ``<year-2000>.<month>.0rc1`` if the current version is not a release candidate, or bump the release candidate number by 1 if it is.
- ``--dev``, to set the project development release number to 0 if it is not a development release, or bump the development release number by 1 if it is.
- ``--patch``, to increment the patch number of the release. This will also reset the release candidate number, pass ``--rc`` at the same time to increment the patch number and make it a release candidate.
If you give no arguments, it will strip the release candidate number, making it a "full release".
Incremental supports "indeterminate" versions, as a stand-in for the next "full" version. This can be used when the version which will be displayed to the end-user is unknown (for example "introduced in" or "deprecated in"). Incremental supports the following indeterminate versions:
- ``Version("<projectname>", "NEXT", 0, 0)``
- ``<projectname> NEXT``
When you run ``python -m incremental.update <projectname> --rc``, these will be updated to real versions (assuming the target final version is 17.1.0):
- ``Version("<projectname>", 17, 1, 0, release_candidate=1)``
- ``<projectname> 17.1.0rc1``
Once the final version is made, it will become:
- ``Version("<projectname>", 17, 1, 0)``
- ``<projectname> 17.1.0``
.. |coverage| image:: https://codecov.io/github/hawkowl/incremental/coverage.svg?branch=master
.. _coverage: https://codecov.io/github/hawkowl/incremental
......
......@@ -9,7 +9,6 @@ See L{Version}.
from __future__ import division, absolute_import
import os
import sys
import warnings
......@@ -21,33 +20,6 @@ if sys.version_info < (3, 0):
_PY3 = False
else:
_PY3 = True
unicode = str
def _nativeString(s):
"""
Convert C{bytes} or C{unicode} to the native C{str} type, using ASCII
encoding if conversion is necessary.
@raise UnicodeError: The input string is not ASCII encodable/decodable.
@raise TypeError: The input is neither C{bytes} nor C{unicode}.
"""
if not isinstance(s, (bytes, unicode)):
raise TypeError("%r is neither bytes nor unicode" % s)
if _PY3:
if isinstance(s, bytes):
return s.decode("ascii")
else:
# Ensure we're limited to ASCII subset:
s.encode("ascii")
else:
if isinstance(s, unicode):
return s.encode("ascii")
else:
# Ensure we're limited to ASCII subset:
s.decode("ascii")
return s
try:
_cmp = cmp
......@@ -146,6 +118,7 @@ class _inf(object):
return 0
return 1
_inf = _inf()
......@@ -162,8 +135,7 @@ class Version(object):
PEP-440 compatible version strings.
This class supports the standard major.minor.micro[rcN] scheme of
versioning, with support for "local versions" which may include a SVN
revision or Git SHA1 hash.
versioning.
"""
def __init__(self, package, major, minor, micro, release_candidate=None,
prerelease=None, dev=None):
......@@ -212,38 +184,6 @@ class Version(object):
DeprecationWarning, stacklevel=2),
return self.release_candidate
def short(self):
"""
Return a string in canonical short version format,
<major>.<minor>.<micro>[+rSVNVer/+gitsha1].
"""
s = self.base()
gitver = self._getGitVersion()
if not gitver:
svnver = self._getSVNVersion()
if svnver:
s += '+r' + _nativeString(svnver)
else:
s += '+' + gitver
return s
def local(self):
"""
Return a PEP440-compatible "local" representation of this L{Version}.
This includes a SVN revision or Git commit SHA1 hash, if available.
Examples:
- 14.4.0+r1223
- 1.2.3rc1+rb2e812003b5d5fcf08efd1dffed6afa98d44ac8c
- 12.10.1
- 3.4.8rc2
- 11.93.0rc1dev3
"""
return self.short()
def public(self):
"""
Return a PEP440-compatible "public" representation of this L{Version}.
......@@ -255,12 +195,6 @@ class Version(object):
- 14.2.1rc1dev9
- 16.04.0dev0
"""
return self.base()
def base(self):
"""
Like L{short}, but without the +rSVNVer or @gitsha1.
"""
if self.major == "NEXT":
return self.major
......@@ -279,16 +213,11 @@ class Version(object):
self.micro,
rc, dev)
def __repr__(self):
# Git repr
gitver = self._formatGitVersion()
if gitver:
gitver = ' #' + gitver
base = public
short = public
local = public
# SVN repr
svnver = self._formatSVNVersion()
if svnver:
svnver = ' #' + svnver
def __repr__(self):
if self.release_candidate is None:
release_candidate = ""
......@@ -301,15 +230,14 @@ class Version(object):
else:
dev = ", dev=%r" % (self.dev,)
return '%s(%r, %r, %d, %d%s%s)%s' % (
return '%s(%r, %r, %d, %d%s%s)' % (
self.__class__.__name__,
self.package,
self.major,
self.minor,
self.micro,
release_candidate,
dev,
gitver or svnver)
dev)
def __str__(self):
return '[%s, version %s]' % (
......@@ -382,131 +310,6 @@ class Version(object):
otherdev))
return x
def _parseGitDir(self, directory):
headFile = os.path.abspath(os.path.join(directory, 'HEAD'))
with open(headFile, "r") as f:
headContent = f.read().strip()
if headContent.startswith("ref: "):
with open(os.path.abspath(
os.path.join(directory,
headContent.split(" ")[1]))) as f:
commit = f.read()
return commit.strip()
return headContent
def _getGitVersion(self):
"""
Given a package directory, walk up and find the git commit sha.
"""
mod = sys.modules.get(self.package)
if mod:
basepath = os.path.dirname(mod.__file__)
upOne = os.path.abspath(os.path.join(basepath, '..'))
if ".git" in os.listdir(upOne):
return self._parseGitDir(os.path.join(upOne, '.git'))
while True:
upOneMore = os.path.abspath(os.path.join(upOne, '..'))
if upOneMore == upOne:
return None
if ".git" in os.listdir(upOneMore):
return self._parseGitDir(os.path.join(upOneMore, '.git'))
upOne = upOneMore
def _parseSVNEntries_4(self, entriesFile):
"""
Given a readable file object which represents a .svn/entries file in
format version 4, return the revision as a string. We do this by
reading first XML element in the document that has a 'revision'
attribute.
"""
from xml.dom.minidom import parse
doc = parse(entriesFile).documentElement
for node in doc.childNodes:
if hasattr(node, 'getAttribute'):
rev = node.getAttribute('revision')
if rev is not None:
return rev.encode('ascii')
def _parseSVNEntries_8(self, entriesFile):
"""
Given a readable file object which represents a .svn/entries file in
format version 8, return the revision as a string.
"""
entriesFile.readline()
entriesFile.readline()
entriesFile.readline()
return entriesFile.readline().strip()
# Add handlers for version 9 and 10 formats, which are the same as
# version 8 as far as revision information is concerned.
_parseSVNEntries_9 = _parseSVNEntries_8
_parseSVNEntriesTenPlus = _parseSVNEntries_8
def _getSVNVersion(self):
"""
Figure out the SVN revision number based on the existence of
<package>/.svn/entries, and its contents. This requires discovering the
format version from the 'format' file and parsing the entries file
accordingly.
@return: None or string containing SVN Revision number.
"""
mod = sys.modules.get(self.package)
if mod:
svn = os.path.join(os.path.dirname(mod.__file__), '.svn')
if not os.path.exists(svn):
# It's not an svn working copy
return None
formatFile = os.path.join(svn, 'format')
if os.path.exists(formatFile):
# It looks like a less-than-version-10 working copy.
with open(formatFile, 'rb') as fObj:
format = fObj.read().strip()
parser = getattr(self,
'_parseSVNEntries_' + format.decode('ascii'),
None)
else:
# It looks like a version-10-or-greater working copy, which
# has version information in the entries file.
parser = self._parseSVNEntriesTenPlus
if parser is None:
return b'Unknown'
entriesFile = os.path.join(svn, 'entries')
entries = open(entriesFile, 'rb')
try:
try:
return parser(entries)
finally:
entries.close()
except:
return b'Unknown'
def _formatSVNVersion(self):
ver = self._getSVNVersion()
if ver is None:
return ''
return ' (SVN r%s)' % (ver,)
def _formatGitVersion(self):
ver = self._getGitVersion()
if ver is None:
return ''
return ' (Git %s)' % (ver,)
def getVersionString(version):
"""
......
......@@ -7,5 +7,5 @@ Provides Incremental version information.
from incremental import Version
__version__ = Version('Incremental', 16, 10, 1)
__version__ = Version('Incremental', 17, 5, 0)
__all__ = ["__version__"]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment