Commit ddc0c02a authored by Stefano Rivera's avatar Stefano Rivera

New upstream version 1.4.0

parent 6dcf67af
Copyright (c) 2015, Jeff Forcier
Copyright (c) 2017, Jeff Forcier
All rights reserved.
Redistribution and use in source and binary forms, with or without
......
Metadata-Version: 1.1
Name: releases
Version: 1.0.0
Version: 1.4.0
Summary: A Sphinx extension for changelog manipulation
Home-page: https://github.com/bitprophet/releases
Author: Jeff Forcier
Author-email: jeff@bitprophet.org
License: UNKNOWN
Description-Content-Type: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Unix
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Documentation
Classifier: Topic :: Documentation :: Sphinx
......@@ -4,9 +4,9 @@
What is Releases?
=================
Releases is a `Sphinx <http://sphinx-doc.org>`_ extension designed to help you
keep a source control friendly, merge friendly changelog file & turn it into
useful, human readable HTML output.
Releases is a Python (2.7, 3.4+) compatible `Sphinx <http://sphinx-doc.org>`_
(1.3+) extension designed to help you keep a source control friendly, merge
friendly changelog file & turn it into useful, human readable HTML output.
Specifically:
......@@ -23,10 +23,8 @@ Specifically:
Some background on why this tool was created can be found in `this blog post
<http://bitprophet.org/blog/2013/09/14/a-better-changelog/>`_.
For more documentation, including detailed installation and usage information,
please see http://releases.readthedocs.org.
For more documentation, please see http://releases.readthedocs.io.
.. note::
You can install the `development version
<https://github.com/bitprophet/releases/tarball/master#egg=releases-dev>`_
via ``pip install releases==dev``.
You can install the development version via ``pip install -e
git+https://github.com/bitprophet/releases/#egg=releases``.
# Task runner
invoke>=0.6.0
invocations>=0.4.1
invoke>=0.6.0,<2.0
invocations>=0.14,<2.0
# Tests (N.B. integration suite also uses Invoke as above)
spec>=0.11.3
spec>=0.11.3,<2.0
mock==1.0.1
# Just for tests...heh
six>=1.4.1
six>=1.4.1,<2.0
# Docs
-e .
sphinx>=1.1
sphinx_rtd_theme>=0.1.5
sphinx_rtd_theme>=0.1.5,<2.0
# Builds
wheel==0.24
twine==1.5
......@@ -2,6 +2,64 @@
Changelog
=========
* :release:`1.4.0 <2017-10-20>`
* :support:`-` Drop Python 2.6 and 3.3 support, to correspond with earlier
changes in Sphinx and most other public Python projects.
* :bug:`- major` Identified a handful of issues with our Sphinx pin &
subsequently, internal changes in Sphinx 1.6 which broke (and/or appear to
break, such as noisy warnings) our own behavior. These have (hopefully) all
been fixed.
* :release:`1.3.2 <2017-10-19>`
* :support:`68 backported` Update packaging requirements to allow for
``sphinx>=1.3,<2``. Thanks to William Minchin.
* :release:`1.3.1 <2017-05-18>`
* :bug:`60` Report extension version to Sphinx for improved Sphinx debug
output. Credit: William Minchin.
* :bug:`66` (via :issue:`67`) Deal with some Sphinx 1.6.1 brokenness causing
``AttributeError`` by leveraging ``getattr()``'s default-value argument.
Thanks to Ian Cordasco for catch & patch.
* :release:`1.3.0 <2016-12-09>`
* :feature:`-` Add ``releases.util``, exposing (among other things) a highly
useful ``parse_changelog(path)`` function that returns a user-facing dict
representing a parsed changelog. Allows users to examine their changelogs
programmatically and answer questions like "do I have any outstanding bugs in
the 1.1 release line?".
* :release:`1.2.1 <2016-07-25>`
* :support:`51 backported` Modernize release management so PyPI trove
classifiers are more accurate, wheel archives are universal instead of Python
2 only, and release artifacts are GPG signed.
* :bug:`56` Fix exceptions that occurred when no release/issue link options
were configured. Now those options are truly optional: release version and
issue number text will simply display normally instead of as hyperlinks.
Thanks to André Caron for the report.
* :bug:`36` Changelogs with no releases whatsoever should still be viable
instead of raising exceptions. This is now happily the case. All items in
such changelogs will end up in a single "unreleased features" list, just as
with regular prehistory entries. Thanks to Steve Ivy for initial report and
André Caron for additional feedback.
* :release:`1.2.0 <2016-05-20>`
* :bug:`- major` Fix formatting of release header dates; a "75% text size"
style rule has had an uncaught typo for some time.
* :bug:`55 major` Non-annotated changelog line items (which implicitly become
bugs) were incorrectly truncating their contents in some situations
(basically, any time they included non-regular-text elements like monospace,
bold etc). This has been fixed.
* :feature:`19` Add ``unstable_prehistory`` option/mode for changelogs whose
0.x release cycle is "rapid" or "unstable" and doesn't closely follow normal
semantic version-driven organization. See :ref:`unstable-prehistory`.
* :bug:`53 major` Tweak newly-updated models so bugfix items prior to an
initial release are considered 'major bugs' so they get rolled into that
initial release (instead of causing a ``ValueError``).
* :release:`1.1.0 <2016-04-28>`
* :feature:`45` Add support for major version transitions (e.g. 1.0 to 2.0).
.. note::
This adds a new install-time dependency: the `semantic_version library
<https://python-semanticversion.readthedocs.io>`_. It's pure Python, so
installation should be trivial.
* :bug:`44 major` Update one of our internal docutils-related classes for
compatibility with Sphinx 1.4.x. Thanks to Gabi Davar for catch & patch.
* :release:`1.0.0 <2015-11-05>`
* :feature:`42` For readability, issues within each release so they are
displayed in feature->bug->support order.
......
This diff is collapsed.
......@@ -31,6 +31,15 @@ Specifically:
for an example.
* You may optionally set ``releases_debug = True`` to see debug output
while building your docs.
* If your changelog includes "simple" pre-1.0 releases derived from a
single branch (i.e. without stable release lines & semantic versioning)
you may want to set ``releases_unstable_prehistory = True``.
* This is also useful if you've just imported a non-Releases changelog,
where your issues are all basic list-items and you don't want to go
through and add bug/feature/support/etc roles.
* See :ref:`the appropriate conceptual docs <unstable-prehistory>` for
details on this behavior.
* Create a Sphinx document named ``changelog.rst`` containing a bulleted list
somewhere at its topmost level.
......@@ -79,10 +88,21 @@ Specifically:
releases '1.1.1' and '1.2.0' will cause it to appear in '1.2.0'
**only**.
* ``(N.N+)`` where ``N.N`` is a valid release line, e.g. ``1.1`` or
``2.10``: Given on *bug* issues to denote minimum release line. E.g.
when actively backporting most bugs to release lines 1.2, 1.3 and
1.4, you might specify ``:bug:`55 (1.3+)``` to note that bug 55 only
applies to releases in 1.3 and above - not 1.2.
``2.10``: Given on issues (usually *bugs*) to denote minimum release
line. E.g. when actively backporting most bugs to release lines 1.2,
1.3 and 1.4, you might specify ``:bug:`55 (1.3+)``` to note that bug
55 only applies to releases in 1.3 and above - not 1.2.
* A `semantic version range spec covering minor+major version numbers
<https://python-semanticversion.readthedocs.io/en/latest/reference.html#version-specifications-the-spec-class>`_
such as ``(<2.0)`` or ``(>=1.0,<3.1)``. A more powerful version of
``(N.N+)`` allowing annotation of issues belonging to specific major
versions.
.. note::
It is possible to give *both* a regular keyword
(``backported``/``major``) *and* a spec (``(N.N+)``/``(>=1.0)``) in
the same issue. However, giving two keywords or two specs at the same
time makes no sense & is not allowed.
* Regular Sphinx content may be given after issue roles and will be preserved
as-is when rendering. For example, in ``:bug:`123` Fixed a bug, thanks
......@@ -99,7 +119,7 @@ Specifically:
Then build your docs; in the rendered output, ``changelog.html`` should show
issues grouped by release, as per the above rules. Examples: `Releases' own
rendered changelog
<http://releases.readthedocs.org/en/latest/changelog.html>`_, `Fabric's
<http://releases.readthedocs.io/en/latest/changelog.html>`_, `Fabric's
rendered changelog <http://www.fabfile.org/changelog.html>`_.
......
Metadata-Version: 1.1
Name: releases
Version: 1.0.0
Version: 1.4.0
Summary: A Sphinx extension for changelog manipulation
Home-page: https://github.com/bitprophet/releases
Author: Jeff Forcier
Author-email: jeff@bitprophet.org
License: UNKNOWN
Description-Content-Type: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Unix
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Documentation
Classifier: Topic :: Documentation :: Sphinx
......@@ -5,7 +5,6 @@ dev-requirements.txt
setup.cfg
setup.py
tasks.py
docs/.changelog.rst.swp
docs/changelog.rst
docs/concepts.rst
docs/conf.py
......@@ -13,9 +12,14 @@ docs/index.rst
docs/usage.rst
releases/__init__.py
releases/_version.py
releases/line_manager.py
releases/models.py
releases/util.py
releases.egg-info/PKG-INFO
releases.egg-info/SOURCES.txt
releases.egg-info/dependency_links.txt
releases.egg-info/requires.txt
releases.egg-info/top_level.txt
tests/changelog.py
\ No newline at end of file
tests/_util.py
tests/organization.py
tests/presentation.py
\ No newline at end of file
semantic_version<3.0
sphinx<2,>=1.3
This diff is collapsed.
__version_info__ = (1, 0, 0)
__version_info__ = (1, 4, 0)
__version__ = '.'.join(map(str, __version_info__))
# TODO: un-subclass dict in favor of something more explicit, once all regular
# dict-like access has been factored out into methods
class LineManager(dict):
"""
Manages multiple release lines/families as well as related config state.
"""
def __init__(self, app):
"""
Initialize new line manager dict.
:param app: The core Sphinx app object. Mostly used for config.
"""
super(LineManager, self).__init__()
self.app = app
@property
def config(self):
"""
Return Sphinx config object.
"""
return self.app.config
def add_family(self, major_number):
"""
Expand to a new release line with given ``major_number``.
This will flesh out mandatory buckets like ``unreleased_bugfix`` and do
other necessary bookkeeping.
"""
# Normally, we have separate buckets for bugfixes vs features
keys = ['unreleased_bugfix', 'unreleased_feature']
# But unstable prehistorical releases roll all up into just
# 'unreleased'
if major_number == 0 and self.config.releases_unstable_prehistory:
keys = ['unreleased']
# Either way, the buckets default to an empty list
self[major_number] = {key: [] for key in keys}
@property
def unstable_prehistory(self):
"""
Returns True if 'unstable prehistory' behavior should be applied.
Specifically, checks config & whether any non-0.x releases exist.
"""
return (
self.config.releases_unstable_prehistory
and not self.has_stable_releases
)
@property
def stable_families(self):
"""
Returns release family numbers which aren't 0 (i.e. prehistory).
"""
return [x for x in self if x != 0]
@property
def has_stable_releases(self):
"""
Returns whether stable (post-0.x) releases seem to exist.
"""
nonzeroes = self.stable_families
# Nothing but 0.x releases -> yup we're prehistory
if not nonzeroes:
return False
# Presumably, if there's >1 major family besides 0.x, we're at least
# one release into the 1.0 (or w/e) line.
if len(nonzeroes) > 1:
return True
# If there's only one, we may still be in the space before its N.0.0 as
# well; we can check by testing for existence of bugfix buckets
return any(
x for x in self[nonzeroes[0]] if not x.startswith('unreleased')
)
from functools import reduce
from operator import xor
from docutils import nodes
from semantic_version import Version as StrictVersion, Spec
import six
class Version(StrictVersion):
"""
Version subclass toggling ``partial=True`` by default.
"""
def __init__(self, version_string, partial=True):
super(Version, self).__init__(version_string, partial)
# Issue type list (keys) + color values
......@@ -10,10 +23,24 @@ ISSUE_TYPES = {
class Issue(nodes.Element):
# Technically, we just need number, but heck, you never know...
_cmp_keys = ('type', 'number', 'backported', 'major')
@property
def type(self):
return self['type_']
@property
def is_featurelike(self):
if self.type == 'bug':
return self.major
else:
return not self.backported
@property
def is_buglike(self):
return not self.is_featurelike
@property
def backported(self):
return self.get('backported', False)
......@@ -27,8 +54,120 @@ class Issue(nodes.Element):
return self.get('number', None)
@property
def line(self):
return self.get('line', None)
def spec(self):
return self.get('spec', None)
def __eq__(self, other):
for attr in self._cmp_keys:
if getattr(self, attr, None) != getattr(other, attr, None):
return False
return True
def __hash__(self):
return reduce(xor, [hash(getattr(self, x)) for x in self._cmp_keys])
def minor_releases(self, manager):
"""
Return all minor release line labels found in ``manager``.
"""
# TODO: yea deffo need a real object for 'manager', heh. E.g. we do a
# very similar test for "do you have any actual releases yet?"
# elsewhere. (This may be fodder for changing how we roll up
# pre-major-release features though...?)
return [
key for key, value in six.iteritems(manager)
if any(x for x in value if not x.startswith('unreleased'))
]
def default_spec(self, manager):
"""
Given the current release-lines structure, return a default Spec.
Specifics:
* For feature-like issues, only the highest major release is used, so
given a ``manager`` with top level keys of ``[1, 2]``, this would
return ``Spec(">=2")``.
* When ``releases_always_forwardport_features`` is ``True``, that
behavior is nullified, and this function always returns the empty
``Spec`` (which matches any and all versions/lines).
* For bugfix-like issues, we only consider major release families which
have actual releases already.
* Thus the core difference here is that features are 'consumed' by
upcoming major releases, and bugfixes are not.
* When the ``unstable_prehistory`` setting is ``True``, the default
spec starts at the oldest non-zero release line. (Otherwise, issues
posted after prehistory ends would try being added to the 0.x part of
the tree, which makes no sense in unstable-prehistory mode.)
"""
# TODO: I feel like this + the surrounding bits in add_to_manager()
# could be consolidated & simplified...
specstr = ""
# Make sure truly-default spec skips 0.x if prehistory was unstable.
stable_families = manager.stable_families
if manager.config.releases_unstable_prehistory and stable_families:
specstr = ">={}".format(min(stable_families))
if self.is_featurelike:
# TODO: if app->config-><releases_always_forwardport_features or
# w/e
if True:
specstr = ">={}".format(max(manager.keys()))
else:
# Can only meaningfully limit to minor release buckets if they
# actually exist yet.
buckets = self.minor_releases(manager)
if buckets:
specstr = ">={}".format(max(buckets))
return Spec(specstr) if specstr else Spec()
def add_to_manager(self, manager):
"""
Given a 'manager' structure, add self to one or more of its 'buckets'.
"""
# Derive version spec allowing us to filter against major/minor buckets
spec = self.spec or self.default_spec(manager)
# Only look in appropriate major version/family; if self is an issue
# declared as living in e.g. >=2, this means we don't even bother
# looking in the 1.x family.
families = [Version(str(x)) for x in manager]
versions = list(spec.filter(families))
for version in versions:
family = version.major
# Within each family, we further limit which bugfix lines match up
# to what self cares about (ignoring 'unreleased' until later)
candidates = [
Version(x)
for x in manager[family]
if not x.startswith('unreleased')
]
# Select matching release lines (& stringify)
buckets = []
bugfix_buckets = [str(x) for x in spec.filter(candidates)]
# Add back in unreleased_* as appropriate
# TODO: probably leverage Issue subclasses for this eventually?
if self.is_buglike:
buckets.extend(bugfix_buckets)
# Don't put into JUST unreleased_bugfix; it implies that this
# major release/family hasn't actually seen any releases yet
# and only exists for features to go into.
if bugfix_buckets:
buckets.append('unreleased_bugfix')
# Obtain list of minor releases to check for "haven't had ANY
# releases yet" corner case, in which case ALL issues get thrown in
# unreleased_feature for the first release to consume.
# NOTE: assumes first release is a minor or major one,
# but...really? why would your first release be a bugfix one??
no_releases = not self.minor_releases(manager)
if self.is_featurelike or self.backported or no_releases:
buckets.append('unreleased_feature')
# Now that we know which buckets are appropriate, add ourself to
# all of them. TODO: or just...do it above...instead...
for bucket in buckets:
manager[family][bucket].append(self)
def __repr__(self):
flag = ''
......@@ -36,10 +175,10 @@ class Issue(nodes.Element):
flag = 'backported'
elif self.major:
flag = 'major'
elif self.line:
flag = self.line + '+'
elif self.spec:
flag = self.spec
if flag:
flag = ' ({0})'.format(flag)
flag = ' ({})'.format(flag)
return '<{issue.type} #{issue.number}{flag}>'.format(issue=self,
flag=flag)
......@@ -49,5 +188,16 @@ class Release(nodes.Element):
def number(self):
return self['number']
@property
def minor(self):
# TODO: use Version
return '.'.join(self.number.split('.')[:-1])
@property
def family(self):
# TODO: use Version.major
# TODO: and probs just rename to .major, 'family' is dumb tbh
return int(self.number.split('.')[0])
def __repr__(self):
return '<release {0}>'.format(self.number)
return '<release {}>'.format(self.number)
This diff is collapsed.
[bdist_wheel]
[flake8]
exclude = docs,.git,build,dist
ignore = E124,E125,E128,E261,E301,E302,E303
max-line-length = 79
[wheel]
universal = 1
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
......@@ -16,20 +16,26 @@ setup(
author_email='jeff@bitprophet.org',
url='https://github.com/bitprophet/releases',
packages=['releases'],
install_requires=[
'semantic_version<3.0',
'sphinx>=1.3,<2',
],
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Unix',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Topic :: Software Development',
'Topic :: Software Development :: Build Tools',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Unix',
'Operating System :: POSIX',
'Operating System :: Microsoft :: Windows',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Software Development',
'Topic :: Software Development :: Documentation',
'Topic :: Documentation',
'Topic :: Documentation :: Sphinx',
],
)
from os.path import join
from invocations import docs
from invocations.testing import test
from invocations.testing import test, integration, watch_tests
from invocations.packaging import release
from invoke import Collection
from invoke import run
from invoke import task
@task(help={
'pty': "Whether to run tests under a pseudo-tty",
ns = Collection(test, integration, watch_tests, release, docs)
ns.configure({
'tests': {
'package': 'releases',
},
'packaging': {
'sign': True,
'wheel': True,
'changelog_file': join(
docs.ns.configuration()['sphinx']['source'],
'changelog.rst',
),
},
})
def integration(pty=True):
"""Runs integration tests."""
cmd = 'inv test -o --tests=integration'
run(cmd + ('' if pty else ' --no-pty'), pty=pty)
ns = Collection(test, integration, release, docs)
from docutils.nodes import (
list_item, paragraph,
)
from mock import Mock
from spec import eq_, ok_
import six
from releases import (
Issue,
issues_role,
Release,
release_role,
construct_releases,
)
from releases.util import make_app, changelog2dict
def inliner(app=None):
app = app or make_app()
return Mock(document=Mock(settings=Mock(env=Mock(app=app))))
# Obtain issue() object w/o wrapping all parse steps
def issue(type_, number, **kwargs):
text = str(number)
if kwargs.get('backported', False):
text += " backported"
if kwargs.get('major', False):
text += " major"
if kwargs.get('spec', None):
text += " (%s)" % kwargs['spec']
app = kwargs.get('app', None)
return issues_role(
name=type_,
rawtext='',
text=text,
lineno=None,
inliner=inliner(app=app),
)[0][0]
# Even shorter shorthand!
def b(number, **kwargs):
return issue('bug', str(number), **kwargs)
def f(number, **kwargs):
return issue('feature', str(number), **kwargs)
def s(number, **kwargs):
return issue('support', str(number), **kwargs)
def entry(i):
"""
Easy wrapper for issue/release objects.
Default is to give eg an issue/release object that gets wrapped in a LI->P.
May give your own (non-issue/release) object to skip auto wrapping. (Useful
since entry() is often called a few levels deep.)
"""
if not isinstance(i, (Issue, Release)):
return i
return list_item('', paragraph('', '', i))
def release(number, **kwargs):
app = kwargs.get('app', None)
nodes = release_role(
name=None,
rawtext='',
text='%s <2013-11-20>' % number,
lineno=None,
inliner=inliner(app=app),
)[0]
return list_item('', paragraph('', '', *nodes))
def release_list(*entries, **kwargs):
skip_initial = kwargs.pop('skip_initial', False)
entries = list(entries) # lol tuples
# Translate simple objs into changelog-friendly ones
for index, item in enumerate(entries):
if isinstance(item, six.string_types):
entries[index] = release(item)
else:
entries[index] = entry(item)
# Insert initial/empty 1st release to start timeline
if not skip_initial:
entries.append(release('1.0.0'))
return entries
def releases(*entries, **kwargs):
app = kwargs.pop('app', None) or make_app()
return construct_releases(release_list(*entries, **kwargs), app)[0]
def setup_issues(self):
self.f = f(12)
self.s = s(5)
self.b = b(15)
self.mb = b(200, major=True)
self.bf = f(27, backported=True)
self.bs = s(29, backported=True)
def expect_releases(entries, release_map, skip_initial=False, app=None):
kwargs = {'skip_initial': skip_initial}
# Let high level tests tickle config settings via make_app()
if app is not None:
kwargs['app'] = app