changelog-0.3.5/ 0000775 0001750 0001750 00000000000 13011374674 014542 5 ustar classic classic 0000000 0000000 changelog-0.3.5/setup.py 0000664 0001750 0001750 00000002123 13011372263 016242 0 ustar classic classic 0000000 0000000 from setuptools import setup
import os
import re
v = open(os.path.join(os.path.dirname(__file__), 'changelog', '__init__.py'))
VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v.read()).group(1)
v.close()
readme = os.path.join(os.path.dirname(__file__), 'README.rst')
setup(name='changelog',
version=VERSION,
description="Provides simple Sphinx markup to render changelog displays.",
long_description=open(readme).read(),
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Console',
'Intended Audience :: Developers',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Documentation',
],
keywords='Sphinx',
author='Mike Bayer',
author_email='mike@zzzcomputing.com',
url='http://bitbucket.org/zzzeek/changelog',
license='MIT',
packages=['changelog'],
include_package_data=True,
zip_safe=False,
)
changelog-0.3.5/changelog/ 0000775 0001750 0001750 00000000000 13011374674 016471 5 ustar classic classic 0000000 0000000 changelog-0.3.5/changelog/changelog.css 0000664 0001750 0001750 00000000141 13011372263 021116 0 ustar classic classic 0000000 0000000 a.changeset-link {
visibility: hidden;
}
li:hover a.changeset-link {
visibility: visible;
}
changelog-0.3.5/changelog/changelog.py 0000664 0001750 0001750 00000032735 13011372300 020764 0 ustar classic classic 0000000 0000000 #! coding: utf-8
import re
from sphinx.util.compat import Directive
from docutils.statemachine import StringList
from docutils import nodes
from sphinx.util.console import bold
import os
from sphinx.util.osutil import copyfile
import textwrap
import itertools
import collections
import sys
py2k = sys.version_info < (3, 0)
if py2k:
import md5
else:
import hashlib as md5
def _is_html(app):
return app.builder.name in ('html', 'readthedocs') # 'readthedocs', classy
def _comma_list(text):
return re.split(r"\s*,\s*", text.strip())
def _parse_content(content):
d = {}
d['text'] = []
idx = 0
for line in content:
idx += 1
m = re.match(r' *\:(.+?)\:(?: +(.+))?', line)
if m:
attrname, value = m.group(1, 2)
d[attrname] = value or ''
elif idx == 1 and line:
# accomodate a unique value on the edge of .. change::
continue
else:
break
d["text"] = content[idx:]
return d
class EnvDirective(object):
@property
def env(self):
return self.state.document.settings.env
@classmethod
def changes(cls, env):
return env.temp_data['ChangeLogDirective_changes']
class ChangeLogDirective(EnvDirective, Directive):
has_content = True
default_section = 'misc'
def _organize_by_section(self, changes):
compound_sections = [(s, s.split(" ")) for s in
self.sections if " " in s]
bysection = collections.defaultdict(list)
all_sections = set()
for rec in changes:
if self.version not in rec['versions']:
continue
inner_tag = rec['tags'].intersection(self.inner_tag_sort)
if inner_tag:
inner_tag = inner_tag.pop()
else:
inner_tag = ""
for compound, comp_words in compound_sections:
if rec['tags'].issuperset(comp_words):
bysection[(compound, inner_tag)].append(rec)
all_sections.add(compound)
break
else:
intersect = rec['tags'].intersection(self.sections)
if intersect:
for sec in rec['sorted_tags']:
if sec in intersect:
bysection[(sec, inner_tag)].append(rec)
all_sections.add(sec)
break
else:
bysection[(self.default_section, inner_tag)].append(rec)
return bysection, all_sections
def _setup_run(self):
self.sections = self.env.config.changelog_sections
self.inner_tag_sort = self.env.config.changelog_inner_tag_sort + [""]
if 'ChangeLogDirective_changes' not in self.env.temp_data:
self.env.temp_data['ChangeLogDirective_changes'] = []
self._parsed_content = _parse_content(self.content)
self.version = version = self._parsed_content.get('version', '')
self.env.temp_data['ChangeLogDirective_version'] = version
p = nodes.paragraph('', '',)
self.state.nested_parse(self.content[1:], 0, p)
def run(self):
self._setup_run()
if 'ChangeLogDirective_includes' in self.env.temp_data:
return []
changes = self.changes(self.env)
output = []
id_prefix = "change-%s" % (self.version, )
topsection = self._run_top(id_prefix)
output.append(topsection)
bysection, all_sections = self._organize_by_section(changes)
counter = itertools.count()
sections_to_render = [s for s in self.sections if s in all_sections]
if not sections_to_render:
for cat in self.inner_tag_sort:
append_sec = self._append_node()
for rec in bysection[(self.default_section, cat)]:
rec["id"] = "%s-%s" % (id_prefix, next(counter))
self._render_rec(rec, None, cat, append_sec)
if append_sec.children:
topsection.append(append_sec)
else:
for section in sections_to_render + [self.default_section]:
sec = nodes.section('',
nodes.title(section, section),
ids=["%s-%s" % (id_prefix, section.replace(" ", "-"))]
)
append_sec = self._append_node()
sec.append(append_sec)
for cat in self.inner_tag_sort:
for rec in bysection[(section, cat)]:
rec["id"] = "%s-%s" % (id_prefix, next(counter))
self._render_rec(rec, section, cat, append_sec)
if append_sec.children:
topsection.append(sec)
return output
def _append_node(self):
return nodes.bullet_list()
def _run_top(self, id_prefix):
version = self._parsed_content.get('version', '')
topsection = nodes.section('',
nodes.title(version, version),
ids=[id_prefix]
)
if self._parsed_content.get("released"):
topsection.append(nodes.Text("Released: %s" %
self._parsed_content['released']))
else:
topsection.append(nodes.Text("no release date"))
intro_para = nodes.paragraph('', '')
len_ = -1
for len_, text in enumerate(self._parsed_content['text']):
if ".. change::" in text:
break
# if encountered any text elements that didn't start with
# ".. change::", those become the intro
if len_ > 0:
self.state.nested_parse(self._parsed_content['text'][0:len_], 0,
intro_para)
topsection.append(intro_para)
return topsection
def _render_rec(self, rec, section, cat, append_sec):
para = rec['node'].deepcopy()
text = _text_rawsource_from_node(para)
to_hash = "%s %s" % (self.version, text[0:100])
targetid = "change-%s" % (
md5.md5(to_hash.encode('ascii', 'ignore')
).hexdigest())
targetnode = nodes.target('', '', ids=[targetid])
para.insert(0, targetnode)
permalink = nodes.reference('', '',
nodes.Text(u"¶", u"¶"),
refid=targetid,
classes=['changeset-link', 'headerlink'],
)
para.append(permalink)
if len(rec['versions']) > 1:
backported_changes = rec['sorted_versions'][rec['sorted_versions'].index(self.version) + 1:]
if backported_changes:
backported = nodes.paragraph('')
backported.append(nodes.Text("This change is also ", ""))
backported.append(nodes.strong("", "backported"))
backported.append(nodes.Text(" to: %s" % ", ".join(backported_changes), ""))
para.append(backported)
insert_ticket = nodes.paragraph('')
para.append(insert_ticket)
i = 0
for collection, render, prefix in (
(rec['tickets'], self.env.config.changelog_render_ticket, "#%s"),
(rec['pullreq'], self.env.config.changelog_render_pullreq,
"pull request %s"),
(rec['changeset'], self.env.config.changelog_render_changeset, "r%s"),
):
for refname in collection:
if i > 0:
insert_ticket.append(nodes.Text(", ", ", "))
else:
insert_ticket.append(nodes.Text("References: """))
i += 1
if render is not None:
if isinstance(render, dict):
if ":" in refname:
typ, refval = refname.split(":")
else:
typ = "default"
refval = refname
refuri = render[typ] % refval
else:
refuri = render % refname
node = nodes.reference('', '',
nodes.Text(prefix % refname, prefix % refname),
refuri=refuri
)
else:
node = nodes.Text(prefix % refname, prefix % refname)
insert_ticket.append(node)
if rec['tags']:
tag_node = nodes.strong('',
" ".join("[%s]" % t for t
in
[t1 for t1 in [section, cat]
if t1 in rec['tags']] +
list(rec['tags'].difference([section, cat]))
) + " "
)
para.children[0].insert(0, tag_node)
append_sec.append(
nodes.list_item('',
nodes.target('', '', ids=[rec['id']]),
para
)
)
class ChangeLogImportDirective(EnvDirective, Directive):
has_content = True
def _setup_run(self):
if 'ChangeLogDirective_changes' not in self.env.temp_data:
self.env.temp_data['ChangeLogDirective_changes'] = []
def run(self):
self._setup_run()
# tell ChangeLogDirective we're here, also prevent
# nested .. include calls
if 'ChangeLogDirective_includes' not in self.env.temp_data:
self.env.temp_data['ChangeLogDirective_includes'] = True
p = nodes.paragraph('', '',)
self.state.nested_parse(self.content, 0, p)
del self.env.temp_data['ChangeLogDirective_includes']
return []
class ChangeDirective(EnvDirective, Directive):
has_content = True
def run(self):
content = _parse_content(self.content)
p = nodes.paragraph('', '',)
sorted_tags = _comma_list(content.get('tags', ''))
declared_version = self.env.temp_data['ChangeLogDirective_version']
versions = set(_comma_list(content.get("versions", ""))).difference(['']).\
union([declared_version])
# if we don't refer to any other versions and we're in an include,
# skip
if len(versions) == 1 and 'ChangeLogDirective_includes' in self.env.temp_data:
return []
def int_ver(ver):
out = []
for dig in ver.split("."):
try:
out.append(int(dig))
except ValueError:
out.append(0)
return tuple(out)
rec = {
'tags': set(sorted_tags).difference(['']),
'tickets': set(_comma_list(content.get('tickets', ''))).difference(['']),
'pullreq': set(_comma_list(content.get('pullreq', ''))).difference(['']),
'changeset': set(_comma_list(content.get('changeset', ''))).difference(['']),
'node': p,
'type': "change",
"title": content.get("title", None),
'sorted_tags': sorted_tags,
"versions": versions,
"sorted_versions": list(reversed(sorted(versions, key=int_ver)))
}
self.state.nested_parse(content['text'], 0, p)
ChangeLogDirective.changes(self.env).append(rec)
return []
def _text_rawsource_from_node(node):
src = []
stack = [node]
while stack:
n = stack.pop(0)
if isinstance(n, nodes.Text):
src.append(n.rawsource)
stack.extend(n.children)
return "".join(src)
def _rst2sphinx(text):
return StringList(
[line.strip() for line in textwrap.dedent(text).split("\n")]
)
def make_ticket_link(name, rawtext, text, lineno, inliner,
options={}, content=[]):
env = inliner.document.settings.env
render_ticket = env.config.changelog_render_ticket or "%s"
prefix = "#%s"
if render_ticket:
ref = render_ticket % text
node = nodes.reference(rawtext, prefix % text, refuri=ref, **options)
else:
node = nodes.Text(prefix % text, prefix % text)
return [node], []
def add_stylesheet(app):
app.add_stylesheet('changelog.css')
def copy_stylesheet(app, exception):
app.info(bold('The name of the builder is: %s' % app.builder.name), nonl=True)
if not _is_html(app) or exception:
return
app.info(bold('Copying sphinx_paramlinks stylesheet... '), nonl=True)
source = os.path.abspath(os.path.dirname(__file__))
# the '_static' directory name is hardcoded in
# sphinx.builders.html.StandaloneHTMLBuilder.copy_static_files.
# would be nice if Sphinx could improve the API here so that we just
# give it the path to a .css file and it does the right thing.
dest = os.path.join(app.builder.outdir, '_static', 'changelog.css')
copyfile(os.path.join(source, "changelog.css"), dest)
app.info('done')
def setup(app):
app.add_directive('changelog', ChangeLogDirective)
app.add_directive('change', ChangeDirective)
app.add_directive('changelog_imports', ChangeLogImportDirective)
app.add_config_value("changelog_sections", [], 'env')
app.add_config_value("changelog_inner_tag_sort", [], 'env')
app.add_config_value("changelog_render_ticket", None, 'env')
app.add_config_value("changelog_render_pullreq", None, 'env')
app.add_config_value("changelog_render_changeset", None, 'env')
app.connect('builder-inited', add_stylesheet)
app.connect('build-finished', copy_stylesheet)
app.add_role('ticket', make_ticket_link)
changelog-0.3.5/changelog/__init__.py 0000664 0001750 0001750 00000000063 13011372304 020565 0 ustar classic classic 0000000 0000000 __version__ = '0.3.5'
from .changelog import setup changelog-0.3.5/README.rst 0000664 0001750 0001750 00000004752 13011372263 016231 0 ustar classic classic 0000000 0000000 ==========
Changelog
==========
A `Sphinx `_ extension to generate changelog files.
This is an experimental, possibly-not-useful extension that's used by the
`SQLAlchemy `_ project and related projects.
Configuration
=============
A sample configuration in ``conf.py`` looks like this::
extensions = [
# changelog extension
'changelog',
# your other sphinx extensions
# ...
]
# section names - optional
changelog_sections = ["general", "rendering", "tests"]
# tags to sort on inside of sections - also optional
changelog_inner_tag_sort = ["feature", "bug"]
# how to render changelog links - these are plain
# python string templates, ticket/pullreq/changeset number goes
# in "%s"
changelog_render_ticket = "http://bitbucket.org/myusername/myproject/issue/%s"
changelog_render_pullreq = "http://bitbucket.org/myusername/myproject/pullrequest/%s"
changelog_render_changeset = "http://bitbucket.org/myusername/myproject/changeset/%s"
Usage
=====
Changelog introduces the ``changelog`` and ``change`` directives::
====================
Changelog for 1.5.6
====================
.. changelog::
:version: 1.5.6
:released: Sun Oct 12 2008
.. change::
:tags: general
:tickets: 27
Improved the frobnozzle.
.. change::
:tags: rendering, tests
:pullreq: 8
:changeset: a9d7cc0b56c2
Rendering tests now correctly render.
With the above markup, the changes above will be rendered into document sections
per changelog, then each change within organized into paragraphs, including
special markup for tags, tickets mentioned, pull requests, changesets. The entries will
be grouped and sorted by tag according to the configuration of the ``changelog_sections``
and ``changelog_inner_tag_sort`` configurations.
A "compound tag" can also be used, if the configuration has a section like this::
changelog_sections = ["orm declarative", "orm"]
Then change entries which contain both the ``orm`` and ``declarative`` tags will be
grouped under a section called ``orm declarative``, followed by the ``orm`` section where
change entries that only have ``orm`` will be placed.
Other Markup
============
The ``:ticket:`` directive will make use of the ``changelog_render_ticket`` markup
to render a ticket link::
:ticket:`456`
changelog-0.3.5/MANIFEST.in 0000664 0001750 0001750 00000000103 13011372263 016262 0 ustar classic classic 0000000 0000000 recursive-include changelog *.py *.css
include README* LICENSE
changelog-0.3.5/PKG-INFO 0000664 0001750 0001750 00000007510 13011374674 015642 0 ustar classic classic 0000000 0000000 Metadata-Version: 1.1
Name: changelog
Version: 0.3.5
Summary: Provides simple Sphinx markup to render changelog displays.
Home-page: http://bitbucket.org/zzzeek/changelog
Author: Mike Bayer
Author-email: mike@zzzcomputing.com
License: MIT
Description: ==========
Changelog
==========
A `Sphinx `_ extension to generate changelog files.
This is an experimental, possibly-not-useful extension that's used by the
`SQLAlchemy `_ project and related projects.
Configuration
=============
A sample configuration in ``conf.py`` looks like this::
extensions = [
# changelog extension
'changelog',
# your other sphinx extensions
# ...
]
# section names - optional
changelog_sections = ["general", "rendering", "tests"]
# tags to sort on inside of sections - also optional
changelog_inner_tag_sort = ["feature", "bug"]
# how to render changelog links - these are plain
# python string templates, ticket/pullreq/changeset number goes
# in "%s"
changelog_render_ticket = "http://bitbucket.org/myusername/myproject/issue/%s"
changelog_render_pullreq = "http://bitbucket.org/myusername/myproject/pullrequest/%s"
changelog_render_changeset = "http://bitbucket.org/myusername/myproject/changeset/%s"
Usage
=====
Changelog introduces the ``changelog`` and ``change`` directives::
====================
Changelog for 1.5.6
====================
.. changelog::
:version: 1.5.6
:released: Sun Oct 12 2008
.. change::
:tags: general
:tickets: 27
Improved the frobnozzle.
.. change::
:tags: rendering, tests
:pullreq: 8
:changeset: a9d7cc0b56c2
Rendering tests now correctly render.
With the above markup, the changes above will be rendered into document sections
per changelog, then each change within organized into paragraphs, including
special markup for tags, tickets mentioned, pull requests, changesets. The entries will
be grouped and sorted by tag according to the configuration of the ``changelog_sections``
and ``changelog_inner_tag_sort`` configurations.
A "compound tag" can also be used, if the configuration has a section like this::
changelog_sections = ["orm declarative", "orm"]
Then change entries which contain both the ``orm`` and ``declarative`` tags will be
grouped under a section called ``orm declarative``, followed by the ``orm`` section where
change entries that only have ``orm`` will be placed.
Other Markup
============
The ``:ticket:`` directive will make use of the ``changelog_render_ticket`` markup
to render a ticket link::
:ticket:`456`
Keywords: Sphinx
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Documentation
changelog-0.3.5/LICENSE 0000664 0001750 0001750 00000002162 13011372263 015540 0 ustar classic classic 0000000 0000000 This is the MIT license: http://www.opensource.org/licenses/mit-license.php
Copyright (C) 2012 by Michael Bayer.
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
changelog-0.3.5/changelog.egg-info/ 0000775 0001750 0001750 00000000000 13011374674 020163 5 ustar classic classic 0000000 0000000 changelog-0.3.5/changelog.egg-info/SOURCES.txt 0000664 0001750 0001750 00000000420 13011374674 022043 0 ustar classic classic 0000000 0000000 LICENSE
MANIFEST.in
README.rst
setup.py
changelog/__init__.py
changelog/changelog.css
changelog/changelog.py
changelog.egg-info/PKG-INFO
changelog.egg-info/SOURCES.txt
changelog.egg-info/dependency_links.txt
changelog.egg-info/not-zip-safe
changelog.egg-info/top_level.txt changelog-0.3.5/changelog.egg-info/PKG-INFO 0000664 0001750 0001750 00000007510 13011374674 021263 0 ustar classic classic 0000000 0000000 Metadata-Version: 1.1
Name: changelog
Version: 0.3.5
Summary: Provides simple Sphinx markup to render changelog displays.
Home-page: http://bitbucket.org/zzzeek/changelog
Author: Mike Bayer
Author-email: mike@zzzcomputing.com
License: MIT
Description: ==========
Changelog
==========
A `Sphinx `_ extension to generate changelog files.
This is an experimental, possibly-not-useful extension that's used by the
`SQLAlchemy `_ project and related projects.
Configuration
=============
A sample configuration in ``conf.py`` looks like this::
extensions = [
# changelog extension
'changelog',
# your other sphinx extensions
# ...
]
# section names - optional
changelog_sections = ["general", "rendering", "tests"]
# tags to sort on inside of sections - also optional
changelog_inner_tag_sort = ["feature", "bug"]
# how to render changelog links - these are plain
# python string templates, ticket/pullreq/changeset number goes
# in "%s"
changelog_render_ticket = "http://bitbucket.org/myusername/myproject/issue/%s"
changelog_render_pullreq = "http://bitbucket.org/myusername/myproject/pullrequest/%s"
changelog_render_changeset = "http://bitbucket.org/myusername/myproject/changeset/%s"
Usage
=====
Changelog introduces the ``changelog`` and ``change`` directives::
====================
Changelog for 1.5.6
====================
.. changelog::
:version: 1.5.6
:released: Sun Oct 12 2008
.. change::
:tags: general
:tickets: 27
Improved the frobnozzle.
.. change::
:tags: rendering, tests
:pullreq: 8
:changeset: a9d7cc0b56c2
Rendering tests now correctly render.
With the above markup, the changes above will be rendered into document sections
per changelog, then each change within organized into paragraphs, including
special markup for tags, tickets mentioned, pull requests, changesets. The entries will
be grouped and sorted by tag according to the configuration of the ``changelog_sections``
and ``changelog_inner_tag_sort`` configurations.
A "compound tag" can also be used, if the configuration has a section like this::
changelog_sections = ["orm declarative", "orm"]
Then change entries which contain both the ``orm`` and ``declarative`` tags will be
grouped under a section called ``orm declarative``, followed by the ``orm`` section where
change entries that only have ``orm`` will be placed.
Other Markup
============
The ``:ticket:`` directive will make use of the ``changelog_render_ticket`` markup
to render a ticket link::
:ticket:`456`
Keywords: Sphinx
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Documentation
changelog-0.3.5/changelog.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 13011374674 024231 0 ustar classic classic 0000000 0000000
changelog-0.3.5/changelog.egg-info/top_level.txt 0000664 0001750 0001750 00000000012 13011374674 022706 0 ustar classic classic 0000000 0000000 changelog
changelog-0.3.5/changelog.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 13011374666 022412 0 ustar classic classic 0000000 0000000
changelog-0.3.5/setup.cfg 0000664 0001750 0001750 00000000073 13011374674 016363 0 ustar classic classic 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0