summaryrefslogtreecommitdiff
path: root/doc/build/builder
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-10-20 18:28:02 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-10-20 18:28:02 -0400
commit690f484f5c172cbef735fe5050bd562c41d7f746 (patch)
tree8f009585d7d985e72a7e010925010d2f2e1a3ae1 /doc/build/builder
parente02bcfb127b6a4469e7df09290c7990f4e8f1fd0 (diff)
downloadsqlalchemy-690f484f5c172cbef735fe5050bd562c41d7f746.tar.gz
- move all CHANGES files into the sphinx docs. this is rudimental now
but puts the changelog in a more data driven format, so that we can begin linking version directives with them as well as with migration documents, once those are also moved into sphinx.
Diffstat (limited to 'doc/build/builder')
-rw-r--r--doc/build/builder/__init__.py3
-rw-r--r--doc/build/builder/changelog.py268
-rw-r--r--doc/build/builder/dialect_info.py2
3 files changed, 270 insertions, 3 deletions
diff --git a/doc/build/builder/__init__.py b/doc/build/builder/__init__.py
index 0e29e69c4..f3ea8b93a 100644
--- a/doc/build/builder/__init__.py
+++ b/doc/build/builder/__init__.py
@@ -1,6 +1,6 @@
-from . import autodoc_mods, dialect_info, sqlformatter, mako
+from . import autodoc_mods, dialect_info, sqlformatter, mako, changelog
def setup(app):
app.add_config_value('release_date', "", True)
@@ -10,3 +10,4 @@ def setup(app):
autodoc_mods.setup(app)
dialect_info.setup(app)
sqlformatter.setup(app)
+ changelog.setup(app)
diff --git a/doc/build/builder/changelog.py b/doc/build/builder/changelog.py
new file mode 100644
index 000000000..16126f4ff
--- /dev/null
+++ b/doc/build/builder/changelog.py
@@ -0,0 +1,268 @@
+import re
+from sphinx.util.compat import Directive
+from docutils.statemachine import StringList
+from docutils import nodes
+import textwrap
+import itertools
+import collections
+
+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 ''
+ else:
+ break
+ d["text"] = content[idx:]
+ return d
+
+def _ticketurl(ticket):
+ return "http://www.sqlalchemy.org/trac/ticket/%s" % ticket
+
+class EnvDirective(object):
+ @property
+ def env(self):
+ return self.state.document.settings.env
+
+class ChangeLogDirective(EnvDirective, Directive):
+ has_content = True
+
+ type_ = "change"
+
+ sections = _comma_list("general, orm, orm declarative, orm querying, \
+ orm configuration, engine, sql, \
+ schema, \
+ postgresql, mysql, sqlite, mssql, oracle, firebird, misc")
+
+ subsections = ["feature", "bug", "moved", "changed", "removed", ""]
+
+
+ def _organize_by_section(self, changes):
+ compound_sections = [(s, s.split(" ")) for s in
+ self.sections if " " in s]
+
+ bysection = collections.defaultdict(list)
+ for rec in changes:
+ subsection = rec['tags'].intersection(self.subsections)
+ if subsection:
+ subsection = subsection.pop()
+ else:
+ subsection = ""
+
+ for compound, comp_words in compound_sections:
+ if rec['tags'].issuperset(comp_words):
+ bysection[(compound, subsection)].append(rec)
+ break
+
+ intersect = rec['tags'].intersection(self.sections)
+ if intersect:
+ bysection[(intersect.pop(), subsection)].append(rec)
+ continue
+
+ bysection[('misc', subsection)].append(rec)
+ return bysection
+
+ @classmethod
+ def changes(cls, env):
+ return env.temp_data['ChangeLogDirective_%s_changes' % cls.type_]
+
+ def _setup_run(self):
+ self.env.temp_data['ChangeLogDirective_%s_changes' % self.type_] = []
+ self._parsed_content = _parse_content(self.content)
+
+ p = nodes.paragraph('', '',)
+ self.state.nested_parse(self.content[1:], 0, p)
+
+ def run(self):
+ self._setup_run()
+ changes = self.changes(self.env)
+ output = []
+
+ version = self._parsed_content.get('version', '')
+ id_prefix = "%s-%s" % (self.type_, version)
+ topsection = self._run_top(id_prefix)
+ output.append(topsection)
+
+ bysection = self._organize_by_section(changes)
+
+ counter = itertools.count()
+
+ for section in self.sections:
+ sec, append_sec = self._section(section, id_prefix)
+
+ for cat in self.subsections:
+ 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 _section(self, section, id_prefix):
+ bullets = nodes.bullet_list()
+ sec = nodes.section('',
+ nodes.title(section, section),
+ bullets,
+ ids=["%s-%s" % (id_prefix, section.replace(" ", "-"))]
+ )
+ return sec, bullets
+
+ 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"))
+ return topsection
+
+ def _render_rec(self, rec, section, cat, append_sec):
+ para = rec['node'].deepcopy()
+ insert_ticket = nodes.paragraph('')
+ para.append(insert_ticket)
+
+ for i, ticket in enumerate(rec['tickets']):
+ if i > 0:
+ insert_ticket.append(nodes.Text(", ", ", "))
+ else:
+ insert_ticket.append(nodes.Text(" ", " "))
+ insert_ticket.append(
+ nodes.reference('', '',
+ nodes.Text("#%s" % ticket, "#%s" % ticket),
+ refuri=_ticketurl(ticket)
+ )
+ )
+
+ if cat or rec['tags']:
+ #tag_node = nodes.strong('',
+ # "[" + cat + "] "
+ # )
+ tag_node = nodes.strong('',
+ " ".join("[%s]" % t for t
+ in
+ [cat] +
+ list(rec['tags'].difference([cat]))
+ if t
+ ) + " "
+ )
+ para.children[0].insert(0, tag_node)
+
+ append_sec.append(
+ nodes.list_item('',
+ nodes.target('', '', ids=[rec['id']]),
+ para
+ )
+ )
+
+
+class MigrationLogDirective(ChangeLogDirective):
+ type_ = "migration"
+
+ sections = _comma_list("New Features, Behavioral Changes, Removed")
+
+ subsections = _comma_list("general, orm, orm declarative, orm querying, \
+ orm configuration, engine, sql, \
+ postgresql, mysql, sqlite")
+
+ def _run_top(self, id_prefix):
+ version = self._parsed_content.get('version', '')
+ title = "What's new in %s?" % version
+ topsection = nodes.section('',
+ nodes.title(title, title),
+ ids=[id_prefix]
+ )
+ if "released" in self._parsed_content:
+ topsection.append(nodes.Text("Released: %s" % self._parsed_content['released']))
+ return topsection
+
+ def _section(self, section, id_prefix):
+ sec = nodes.section('',
+ nodes.title(section, section),
+ ids=["%s-%s" % (id_prefix, section.replace(" ", "-"))]
+ )
+ return sec, sec
+
+ def _render_rec(self, rec, section, cat, append_sec):
+ para = rec['node'].deepcopy()
+
+ insert_ticket = nodes.paragraph('')
+ para.append(insert_ticket)
+
+ for i, ticket in enumerate(rec['tickets']):
+ if i > 0:
+ insert_ticket.append(nodes.Text(", ", ", "))
+ else:
+ insert_ticket.append(nodes.Text(" ", " "))
+ insert_ticket.append(
+ nodes.reference('', '',
+ nodes.Text("#%s" % ticket, "#%s" % ticket),
+ refuri=_ticketurl(ticket)
+ )
+ )
+
+ append_sec.append(
+ nodes.section('',
+ nodes.title(rec['title'], rec['title']),
+ para,
+ ids=[rec['id']]
+ )
+ )
+
+
+
+class ChangeDirective(EnvDirective, Directive):
+ has_content = True
+
+ type_ = "change"
+ parent_cls = ChangeLogDirective
+
+ def run(self):
+ content = _parse_content(self.content)
+ p = nodes.paragraph('', '',)
+ rec = {
+ 'tags': set(_comma_list(content.get('tags', ''))).difference(['']),
+ 'tickets': set(_comma_list(content.get('tickets', ''))).difference(['']),
+ 'node': p,
+ 'type': self.type_,
+ "title": content.get("title", None)
+ }
+
+ if "declarative" in rec['tags']:
+ rec['tags'].add("orm")
+
+ self.state.nested_parse(content['text'], 0, p)
+ self.parent_cls.changes(self.env).append(rec)
+
+ return []
+
+class MigrationDirective(ChangeDirective):
+ type_ = "migration"
+ parent_cls = MigrationLogDirective
+
+
+def _rst2sphinx(text):
+ return StringList(
+ [line.strip() for line in textwrap.dedent(text).split("\n")]
+ )
+
+def setup(app):
+ app.add_directive('changelog', ChangeLogDirective)
+ app.add_directive('migrationlog', MigrationLogDirective)
+ app.add_directive('migration', MigrationDirective)
+ app.add_directive('change', ChangeDirective)
+
diff --git a/doc/build/builder/dialect_info.py b/doc/build/builder/dialect_info.py
index 454f26f4e..22e3fb085 100644
--- a/doc/build/builder/dialect_info.py
+++ b/doc/build/builder/dialect_info.py
@@ -91,8 +91,6 @@ class DialectDirective(Directive):
if "connectstring" in content:
- # TODO: wish I knew how to just embed RST here and parse it into
- # nodes
sec = nodes.section(
'',
nodes.title("Connecting", "Connecting"),