diff -r 128b45caba5b conf.py --- a/conf.py Sun Jun 26 09:01:18 2016 +0300 +++ b/conf.py Wed Jun 29 21:41:03 2016 -0700 @@ -14,6 +14,7 @@ import os import sys import time +sys.path.append(os.path.abspath('tools/extensions')) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -93,22 +94,28 @@ # -- Options for HTML output --------------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'nature' +# Use our custom theme. Previously used builtin 'nature' theme. +#html_theme = 'nature' +html_theme = 'pydoctheme' +html_theme_path = ['tools'] +html_theme_options = {'collapsiblesidebar': True} -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". html_title = "%s %s" % (project, release) +# Path to find HTML templates. +templates_path = ['tools/templates'] + +# Custom sidebar templates, filenames relative to this file. +html_sidebars = { + 'index': 'indexsidebar.html', +} + +# Additional static files. +html_static_path = ['tools/static'] + # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None diff -r 128b45caba5b tools/extensions/c_annotations.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/extensions/c_annotations.py Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +""" + c_annotations.py + ~~~~~~~~~~~~~~~~ + + Supports annotations for C API elements: + + * reference count annotations for C API functions. Based on + refcount.py and anno-api.py in the old Python documentation tools. + + * stable API annotations + + Usage: Set the `refcount_file` config value to the path to the reference + count data file. + + :copyright: Copyright 2007-2014 by Georg Brandl. + :license: Python license. +""" + +from os import path +from docutils import nodes +from docutils.parsers.rst import directives + +from sphinx import addnodes +from sphinx.domains.c import CObject + + +class RCEntry: + def __init__(self, name): + self.name = name + self.args = [] + self.result_type = '' + self.result_refs = None + + +class Annotations(dict): + @classmethod + def fromfile(cls, filename): + d = cls() + fp = open(filename, 'r') + try: + for line in fp: + line = line.strip() + if line[:1] in ("", "#"): + # blank lines and comments + continue + parts = line.split(":", 4) + if len(parts) != 5: + raise ValueError("Wrong field count in %r" % line) + function, type, arg, refcount, comment = parts + # Get the entry, creating it if needed: + try: + entry = d[function] + except KeyError: + entry = d[function] = RCEntry(function) + if not refcount or refcount == "null": + refcount = None + else: + refcount = int(refcount) + # Update the entry with the new parameter or the result + # information. + if arg: + entry.args.append((arg, type, refcount)) + else: + entry.result_type = type + entry.result_refs = refcount + finally: + fp.close() + return d + + def add_annotations(self, app, doctree): + for node in doctree.traverse(addnodes.desc_content): + par = node.parent + if par['domain'] != 'c': + continue + if par['stableabi']: + node.insert(0, nodes.emphasis(' Part of the stable ABI.', + ' Part of the stable ABI.', + classes=['stableabi'])) + if par['objtype'] != 'function': + continue + if not par[0].has_key('names') or not par[0]['names']: + continue + name = par[0]['names'][0] + if name.startswith("c."): + name = name[2:] + entry = self.get(name) + if not entry: + continue + elif entry.result_type not in ("PyObject*", "PyVarObject*"): + continue + if entry.result_refs is None: + rc = 'Return value: Always NULL.' + elif entry.result_refs: + rc = 'Return value: New reference.' + else: + rc = 'Return value: Borrowed reference.' + node.insert(0, nodes.emphasis(rc, rc, classes=['refcount'])) + + +def init_annotations(app): + refcounts = Annotations.fromfile( + path.join(app.srcdir, app.config.refcount_file)) + app.connect('doctree-read', refcounts.add_annotations) + + +def setup(app): + app.add_config_value('refcount_file', '', True) + app.connect('builder-inited', init_annotations) + + # monkey-patch C object... + CObject.option_spec = { + 'noindex': directives.flag, + 'stableabi': directives.flag, + } + old_handle_signature = CObject.handle_signature + def new_handle_signature(self, sig, signode): + signode.parent['stableabi'] = 'stableabi' in self.options + return old_handle_signature(self, sig, signode) + CObject.handle_signature = new_handle_signature + return {'version': '1.0', 'parallel_read_safe': True} diff -r 128b45caba5b tools/extensions/patchlevel.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/extensions/patchlevel.py Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" + patchlevel.py + ~~~~~~~~~~~~~ + + Extract version info from Include/patchlevel.h. + Adapted from Doc/tools/getversioninfo. + + :copyright: 2007-2008 by Georg Brandl. + :license: Python license. +""" + +import os +import re +import sys + +def get_header_version_info(srcdir): + patchlevel_h = os.path.join(srcdir, '..', 'Include', 'patchlevel.h') + + # This won't pick out all #defines, but it will pick up the ones we + # care about. + rx = re.compile(r'\s*#define\s+([a-zA-Z][a-zA-Z_0-9]*)\s+([a-zA-Z_0-9]+)') + + d = {} + f = open(patchlevel_h) + try: + for line in f: + m = rx.match(line) + if m is not None: + name, value = m.group(1, 2) + d[name] = value + finally: + f.close() + + release = version = '%s.%s' % (d['PY_MAJOR_VERSION'], d['PY_MINOR_VERSION']) + micro = int(d['PY_MICRO_VERSION']) + release += '.' + str(micro) + + level = d['PY_RELEASE_LEVEL'] + suffixes = { + 'PY_RELEASE_LEVEL_ALPHA': 'a', + 'PY_RELEASE_LEVEL_BETA': 'b', + 'PY_RELEASE_LEVEL_GAMMA': 'rc', + } + if level != 'PY_RELEASE_LEVEL_FINAL': + release += suffixes[level] + str(int(d['PY_RELEASE_SERIAL'])) + return version, release + + +def get_sys_version_info(): + major, minor, micro, level, serial = sys.version_info + release = version = '%s.%s' % (major, minor) + release += '.%s' % micro + if level != 'final': + release += '%s%s' % (level[0], serial) + return version, release + + +def get_version_info(): + try: + return get_header_version_info('.') + except (IOError, OSError): + version, release = get_sys_version_info() + print >>sys.stderr, 'Can\'t get version info from Include/patchlevel.h, ' \ + 'using version of this interpreter (%s).' % release + return version, release + +if __name__ == '__main__': + print(get_header_version_info('.')[1]) diff -r 128b45caba5b tools/extensions/pyspecific.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/extensions/pyspecific.py Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +""" + pyspecific.py + ~~~~~~~~~~~~~ + + Sphinx extension with Python doc-specific markup. + + :copyright: 2008-2014 by Georg Brandl. + :license: Python license. +""" + +import re +import codecs +from os import path +from time import asctime +from pprint import pformat +from docutils.io import StringOutput +from docutils.utils import new_document + +from docutils import nodes, utils + +from sphinx import addnodes +from sphinx.builders import Builder +from sphinx.util.nodes import split_explicit_title +from sphinx.util.compat import Directive +from sphinx.writers.html import HTMLTranslator +from sphinx.writers.text import TextWriter +from sphinx.writers.latex import LaTeXTranslator +from sphinx.domains.python import PyModulelevel, PyClassmember + +# Support for checking for suspicious markup + +import suspicious + + +ISSUE_URI = 'https://bugs.python.org/issue%s' +SOURCE_URI = 'https://hg.python.org/cpython/file/default/%s' + +# monkey-patch reST parser to disable alphabetic and roman enumerated lists +from docutils.parsers.rst.states import Body +Body.enum.converters['loweralpha'] = \ + Body.enum.converters['upperalpha'] = \ + Body.enum.converters['lowerroman'] = \ + Body.enum.converters['upperroman'] = lambda x: None + +# monkey-patch HTML and LaTeX translators to keep doctest blocks in the +# doctest docs themselves +orig_visit_literal_block = HTMLTranslator.visit_literal_block +orig_depart_literal_block = LaTeXTranslator.depart_literal_block + + +def new_visit_literal_block(self, node): + meta = self.builder.env.metadata[self.builder.current_docname] + old_trim_doctest_flags = self.highlighter.trim_doctest_flags + if 'keepdoctest' in meta: + self.highlighter.trim_doctest_flags = False + try: + orig_visit_literal_block(self, node) + finally: + self.highlighter.trim_doctest_flags = old_trim_doctest_flags + + +def new_depart_literal_block(self, node): + meta = self.builder.env.metadata[self.curfilestack[-1]] + old_trim_doctest_flags = self.highlighter.trim_doctest_flags + if 'keepdoctest' in meta: + self.highlighter.trim_doctest_flags = False + try: + orig_depart_literal_block(self, node) + finally: + self.highlighter.trim_doctest_flags = old_trim_doctest_flags + + +HTMLTranslator.visit_literal_block = new_visit_literal_block +LaTeXTranslator.depart_literal_block = new_depart_literal_block + + +# Support for marking up and linking to bugs.python.org issues + +def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): + issue = utils.unescape(text) + text = 'issue ' + issue + refnode = nodes.reference(text, text, refuri=ISSUE_URI % issue) + return [refnode], [] + + +# Support for linking to Python source files easily + +def source_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): + has_t, title, target = split_explicit_title(text) + title = utils.unescape(title) + target = utils.unescape(target) + refnode = nodes.reference(title, title, refuri=SOURCE_URI % target) + return [refnode], [] + + +# Support for marking up implementation details + +class ImplementationDetail(Directive): + + has_content = True + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + + def run(self): + pnode = nodes.compound(classes=['impl-detail']) + content = self.content + add_text = nodes.strong('CPython implementation detail:', + 'CPython implementation detail:') + if self.arguments: + n, m = self.state.inline_text(self.arguments[0], self.lineno) + pnode.append(nodes.paragraph('', '', *(n + m))) + self.state.nested_parse(content, self.content_offset, pnode) + if pnode.children and isinstance(pnode[0], nodes.paragraph): + pnode[0].insert(0, add_text) + pnode[0].insert(1, nodes.Text(' ')) + else: + pnode.insert(0, nodes.paragraph('', '', add_text)) + return [pnode] + + +# Support for documenting decorators + +class PyDecoratorMixin(object): + def handle_signature(self, sig, signode): + ret = super(PyDecoratorMixin, self).handle_signature(sig, signode) + signode.insert(0, addnodes.desc_addname('@', '@')) + return ret + + def needs_arglist(self): + return False + + +class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel): + def run(self): + # a decorator function is a function after all + self.name = 'py:function' + return PyModulelevel.run(self) + + +class PyDecoratorMethod(PyDecoratorMixin, PyClassmember): + def run(self): + self.name = 'py:method' + return PyClassmember.run(self) + + +class PyCoroutineMixin(object): + def handle_signature(self, sig, signode): + ret = super(PyCoroutineMixin, self).handle_signature(sig, signode) + signode.insert(0, addnodes.desc_annotation('coroutine ', 'coroutine ')) + return ret + + +class PyCoroutineFunction(PyCoroutineMixin, PyModulelevel): + def run(self): + self.name = 'py:function' + return PyModulelevel.run(self) + + +class PyCoroutineMethod(PyCoroutineMixin, PyClassmember): + def run(self): + self.name = 'py:method' + return PyClassmember.run(self) + + +class PyAbstractMethod(PyClassmember): + + def handle_signature(self, sig, signode): + ret = super(PyAbstractMethod, self).handle_signature(sig, signode) + signode.insert(0, addnodes.desc_annotation('abstractmethod ', + 'abstractmethod ')) + return ret + + def run(self): + self.name = 'py:method' + return PyClassmember.run(self) + + +# Support for documenting version of removal in deprecations + +class DeprecatedRemoved(Directive): + has_content = True + required_arguments = 2 + optional_arguments = 1 + final_argument_whitespace = True + option_spec = {} + + _label = 'Deprecated since version %s, will be removed in version %s' + + def run(self): + node = addnodes.versionmodified() + node.document = self.state.document + node['type'] = 'deprecated-removed' + version = (self.arguments[0], self.arguments[1]) + node['version'] = version + text = self._label % version + if len(self.arguments) == 3: + inodes, messages = self.state.inline_text(self.arguments[2], + self.lineno+1) + para = nodes.paragraph(self.arguments[2], '', *inodes) + node.append(para) + else: + messages = [] + if self.content: + self.state.nested_parse(self.content, self.content_offset, node) + if len(node): + if isinstance(node[0], nodes.paragraph) and node[0].rawsource: + content = nodes.inline(node[0].rawsource, translatable=True) + content.source = node[0].source + content.line = node[0].line + content += node[0].children + node[0].replace_self(nodes.paragraph('', '', content)) + node[0].insert(0, nodes.inline('', '%s: ' % text, + classes=['versionmodified'])) + else: + para = nodes.paragraph('', '', + nodes.inline('', '%s.' % text, + classes=['versionmodified'])) + node.append(para) + env = self.state.document.settings.env + env.note_versionchange('deprecated', version[0], node, self.lineno) + return [node] + messages + + +# Support for including Misc/NEWS + +issue_re = re.compile('([Ii])ssue #([0-9]+)') +whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$") + + +class MiscNews(Directive): + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + fname = self.arguments[0] + source = self.state_machine.input_lines.source( + self.lineno - self.state_machine.input_offset - 1) + source_dir = path.dirname(path.abspath(source)) + fpath = path.join(source_dir, fname) + self.state.document.settings.record_dependencies.add(fpath) + try: + fp = codecs.open(fpath, encoding='utf-8') + try: + content = fp.read() + finally: + fp.close() + except Exception: + text = 'The NEWS file is not available.' + node = nodes.strong(text, text) + return [node] + content = issue_re.sub(r'`\1ssue #\2 `__', + content) + content = whatsnew_re.sub(r'\1', content) + # remove first 3 lines as they are the main heading + lines = ['.. default-role:: obj', ''] + content.splitlines()[3:] + self.state_machine.insert_input(lines, fname) + return [] + + +# Support for building "topic help" for pydoc + +pydoc_topic_labels = [ + 'assert', 'assignment', 'atom-identifiers', 'atom-literals', + 'attribute-access', 'attribute-references', 'augassign', 'binary', + 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object', + 'bltin-null-object', 'bltin-type-objects', 'booleans', + 'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound', + 'context-managers', 'continue', 'conversions', 'customization', 'debugger', + 'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel', + 'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global', + 'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers', + 'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types', + 'objects', 'operator-summary', 'pass', 'power', 'raise', 'return', + 'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames', + 'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types', + 'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules', + 'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield' +] + + +class PydocTopicsBuilder(Builder): + name = 'pydoc-topics' + + def init(self): + self.topics = {} + + def get_outdated_docs(self): + return 'all pydoc topics' + + def get_target_uri(self, docname, typ=None): + return '' # no URIs + + def write(self, *ignored): + writer = TextWriter(self) + for label in self.status_iterator(pydoc_topic_labels, + 'building topics... ', + length=len(pydoc_topic_labels)): + if label not in self.env.domaindata['std']['labels']: + self.warn('label %r not in documentation' % label) + continue + docname, labelid, sectname = self.env.domaindata['std']['labels'][label] + doctree = self.env.get_and_resolve_doctree(docname, self) + document = new_document('
') + document.append(doctree.ids[labelid]) + destination = StringOutput(encoding='utf-8') + writer.write(document, destination) + self.topics[label] = writer.output + + def finish(self): + f = open(path.join(self.outdir, 'topics.py'), 'wb') + try: + f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8')) + f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8')) + f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8')) + finally: + f.close() + + +# Support for documenting Opcodes + +opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?') + + +def parse_opcode_signature(env, sig, signode): + """Transform an opcode signature into RST nodes.""" + m = opcode_sig_re.match(sig) + if m is None: + raise ValueError + opname, arglist = m.groups() + signode += addnodes.desc_name(opname, opname) + if arglist is not None: + paramlist = addnodes.desc_parameterlist() + signode += paramlist + paramlist += addnodes.desc_parameter(arglist, arglist) + return opname.strip() + + +# Support for documenting pdb commands + +pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)') + +# later... +# pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+ | # identifiers +# [.,:]+ | # punctuation +# [\[\]()] | # parens +# \s+ # whitespace +# ''', re.X) + + +def parse_pdb_command(env, sig, signode): + """Transform a pdb command signature into RST nodes.""" + m = pdbcmd_sig_re.match(sig) + if m is None: + raise ValueError + name, args = m.groups() + fullname = name.replace('(', '').replace(')', '') + signode += addnodes.desc_name(name, name) + if args: + signode += addnodes.desc_addname(' '+args, ' '+args) + return fullname + + +def setup(app): + app.add_role('issue', issue_role) + app.add_role('source', source_role) + app.add_directive('impl-detail', ImplementationDetail) + app.add_directive('deprecated-removed', DeprecatedRemoved) + app.add_builder(PydocTopicsBuilder) + app.add_builder(suspicious.CheckSuspiciousMarkupBuilder) + app.add_description_unit('opcode', 'opcode', '%s (opcode)', + parse_opcode_signature) + app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)', + parse_pdb_command) + app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)') + app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction) + app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod) + app.add_directive_to_domain('py', 'coroutinefunction', PyCoroutineFunction) + app.add_directive_to_domain('py', 'coroutinemethod', PyCoroutineMethod) + app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod) + app.add_directive('miscnews', MiscNews) + return {'version': '1.0', 'parallel_read_safe': True} diff -r 128b45caba5b tools/extensions/suspicious.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/extensions/suspicious.py Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,274 @@ +""" +Try to detect suspicious constructs, resembling markup +that has leaked into the final output. + +Suspicious lines are reported in a comma-separated-file, +``suspicious.csv``, located in the output directory. + +The file is utf-8 encoded, and each line contains four fields: + + * document name (normalized) + * line number in the source document + * problematic text + * complete line showing the problematic text in context + +It is common to find many false positives. To avoid reporting them +again and again, they may be added to the ``ignored.csv`` file +(located in the configuration directory). The file has the same +format as ``suspicious.csv`` with a few differences: + + - each line defines a rule; if the rule matches, the issue + is ignored. + - line number may be empty (that is, nothing between the + commas: ",,"). In this case, line numbers are ignored (the + rule matches anywhere in the file). + - the last field does not have to be a complete line; some + surrounding text (never more than a line) is enough for + context. + +Rules are processed sequentially. A rule matches when: + + * document names are the same + * problematic texts are the same + * line numbers are close to each other (5 lines up or down) + * the rule text is completely contained into the source line + +The simplest way to create the ignored.csv file is by copying +undesired entries from suspicious.csv (possibly trimming the last +field.) + +Copyright 2009 Gabriel A. Genellina + +""" + +import os +import re +import csv +import sys + +from docutils import nodes +from sphinx.builders import Builder + +detect_all = re.compile(r''' + ::(?=[^=])| # two :: (but NOT ::=) + :[a-zA-Z][a-zA-Z0-9]+| # :foo + `| # ` (seldom used by itself) + (?= (3, 0) + + +class Rule: + def __init__(self, docname, lineno, issue, line): + """A rule for ignoring issues""" + self.docname = docname # document to which this rule applies + self.lineno = lineno # line number in the original source; + # this rule matches only near that. + # None -> don't care + self.issue = issue # the markup fragment that triggered this rule + self.line = line # text of the container element (single line only) + self.used = False + + def __repr__(self): + return '{0.docname},,{0.issue},{0.line}'.format(self) + + + +class dialect(csv.excel): + """Our dialect: uses only linefeed as newline.""" + lineterminator = '\n' + + +class CheckSuspiciousMarkupBuilder(Builder): + """ + Checks for possibly invalid markup that may leak into the output. + """ + name = 'suspicious' + + def init(self): + # create output file + self.log_file_name = os.path.join(self.outdir, 'suspicious.csv') + open(self.log_file_name, 'w').close() + # load database of previously ignored issues + self.load_rules(os.path.join(os.path.dirname(__file__), '..', + 'susp-ignored.csv')) + + def get_outdated_docs(self): + return self.env.found_docs + + def get_target_uri(self, docname, typ=None): + return '' + + def prepare_writing(self, docnames): + pass + + def write_doc(self, docname, doctree): + # set when any issue is encountered in this document + self.any_issue = False + self.docname = docname + visitor = SuspiciousVisitor(doctree, self) + doctree.walk(visitor) + + def finish(self): + unused_rules = [rule for rule in self.rules if not rule.used] + if unused_rules: + self.warn('Found %s/%s unused rules:' % + (len(unused_rules), len(self.rules))) + for rule in unused_rules: + self.info(repr(rule)) + return + + def check_issue(self, line, lineno, issue): + if not self.is_ignored(line, lineno, issue): + self.report_issue(line, lineno, issue) + + def is_ignored(self, line, lineno, issue): + """Determine whether this issue should be ignored.""" + docname = self.docname + for rule in self.rules: + if rule.docname != docname: continue + if rule.issue != issue: continue + # Both lines must match *exactly*. This is rather strict, + # and probably should be improved. + # Doing fuzzy matches with levenshtein distance could work, + # but that means bringing other libraries... + # Ok, relax that requirement: just check if the rule fragment + # is contained in the document line + if rule.line not in line: continue + # Check both line numbers. If they're "near" + # this rule matches. (lineno=None means "don't care") + if (rule.lineno is not None) and \ + abs(rule.lineno - lineno) > 5: continue + # if it came this far, the rule matched + rule.used = True + return True + return False + + def report_issue(self, text, lineno, issue): + if not self.any_issue: self.info() + self.any_issue = True + self.write_log_entry(lineno, issue, text) + if py3: + self.warn('[%s:%d] "%s" found in "%-.120s"' % + (self.docname, lineno, issue, text)) + else: + self.warn('[%s:%d] "%s" found in "%-.120s"' % ( + self.docname.encode(sys.getdefaultencoding(),'replace'), + lineno, + issue.encode(sys.getdefaultencoding(),'replace'), + text.strip().encode(sys.getdefaultencoding(),'replace'))) + self.app.statuscode = 1 + + def write_log_entry(self, lineno, issue, text): + if py3: + f = open(self.log_file_name, 'a') + writer = csv.writer(f, dialect) + writer.writerow([self.docname, lineno, issue, text.strip()]) + f.close() + else: + f = open(self.log_file_name, 'ab') + writer = csv.writer(f, dialect) + writer.writerow([self.docname.encode('utf-8'), + lineno, + issue.encode('utf-8'), + text.strip().encode('utf-8')]) + f.close() + + def load_rules(self, filename): + """Load database of previously ignored issues. + + A csv file, with exactly the same format as suspicious.csv + Fields: document name (normalized), line number, issue, surrounding text + """ + self.info("loading ignore rules... ", nonl=1) + self.rules = rules = [] + try: + if py3: + f = open(filename, 'r') + else: + f = open(filename, 'rb') + except IOError: + return + for i, row in enumerate(csv.reader(f)): + if len(row) != 4: + raise ValueError( + "wrong format in %s, line %d: %s" % (filename, i+1, row)) + docname, lineno, issue, text = row + if lineno: + lineno = int(lineno) + else: + lineno = None + if not py3: + docname = docname.decode('utf-8') + issue = issue.decode('utf-8') + text = text.decode('utf-8') + rule = Rule(docname, lineno, issue, text) + rules.append(rule) + f.close() + self.info('done, %d rules loaded' % len(self.rules)) + + +def get_lineno(node): + """Obtain line number information for a node.""" + lineno = None + while lineno is None and node: + node = node.parent + lineno = node.line + return lineno + + +def extract_line(text, index): + """text may be a multiline string; extract + only the line containing the given character index. + + >>> extract_line("abc\ndefgh\ni", 6) + >>> 'defgh' + >>> for i in (0, 2, 3, 4, 10): + ... print extract_line("abc\ndefgh\ni", i) + abc + abc + abc + defgh + defgh + i + """ + p = text.rfind('\n', 0, index) + 1 + q = text.find('\n', index) + if q < 0: + q = len(text) + return text[p:q] + + +class SuspiciousVisitor(nodes.GenericNodeVisitor): + + lastlineno = 0 + + def __init__(self, document, builder): + nodes.GenericNodeVisitor.__init__(self, document) + self.builder = builder + + def default_visit(self, node): + if isinstance(node, (nodes.Text, nodes.image)): # direct text containers + text = node.astext() + # lineno seems to go backwards sometimes (?) + self.lastlineno = lineno = max(get_lineno(node) or 0, self.lastlineno) + seen = set() # don't report the same issue more than only once per line + for match in detect_all(text): + issue = match.group() + line = extract_line(text, match.start()) + if (issue, line) not in seen: + self.builder.check_issue(line, lineno, issue) + seen.add((issue, line)) + + unknown_visit = default_visit + + def visit_document(self, node): + self.lastlineno = 0 + + def visit_comment(self, node): + # ignore comments -- too much false positives. + # (although doing this could miss some errors; + # there were two sections "commented-out" by mistake + # in the Python docs that would not be catched) + raise nodes.SkipNode diff -r 128b45caba5b tools/pydoctheme/static/pydoctheme.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/pydoctheme/static/pydoctheme.css Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,189 @@ +@import url("default.css"); + +body { + background-color: white; + margin-left: 0; + margin-right: 0; +} + +div.related { + margin-bottom: 1.2em; + line-height: 32px; + color: #fff; + text-shadow: 0px 1px 0 #444; + font-size: 0.9em; + background-color: #6BA81E; +} + +div.related a { + color: #fff; +} + +div.related a:hover { + color: #0095C4; +} + +div.related:first-child { + border-top: 0; + border-bottom: 1px solid #ccc; +} + +div.sphinxsidebar { + background-color: #eeeeee; + border-radius: 5px; + line-height: 130%; + font-size: smaller; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4 { + margin-top: 1.5em; +} + +div.sphinxsidebarwrapper > h3:first-child { + margin-top: 0.2em; +} + +div.sphinxsidebarwrapper > ul > li > ul > li { + margin-bottom: 0.4em; +} + +div.sphinxsidebar a:hover { + color: #0095C4; +} + +div.sphinxsidebar input { + font-family: 'Lucida Grande',Arial,sans-serif; + border: 1px solid #999999; + font-size: smaller; + border-radius: 3px; +} + +div.sphinxsidebar input[type=text] { + max-width: 150px; +} + +div.body { + padding: 0 0 0 1.2em; +} + +div.body p { + line-height: 140%; +} + +div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { + margin: 0; + border: 0; + padding: 0.3em 0; +} + +div.body hr { + border: 0; + background-color: #ccc; + height: 1px; +} + +div.body pre { + border-radius: 3px; + border: 1px solid #ac9; +} + +div.body div.admonition, div.body div.impl-detail { + border-radius: 3px; +} + +div.body div.impl-detail > p { + margin: 0; +} + +div.body div.seealso { + border: 1px solid #dddd66; +} + +div.body a { + color: #0072aa; +} + +div.body a:visited { + color: #6363bb; +} + +div.body a:hover { + color: #00B0E4; +} + +tt, code, pre { + font-family: monospace, sans-serif; + font-size: 96.5%; +} + +div.body tt, div.body code { + border-radius: 3px; +} + +div.body tt.descname, div.body code.descname { + font-size: 120%; +} + +div.body tt.xref, div.body a tt, div.body code.xref, div.body a code { + font-weight: normal; +} + +.deprecated { + border-radius: 3px; +} + +table.docutils { + border: 1px solid #ddd; + min-width: 20%; + border-radius: 3px; + margin-top: 10px; + margin-bottom: 10px; +} + +table.docutils td, table.docutils th { + border: 1px solid #ddd !important; + border-radius: 3px; +} + +table p, table li { + text-align: left !important; +} + +table.docutils th { + background-color: #eee; + padding: 0.3em 0.5em; +} + +table.docutils td { + background-color: white; + padding: 0.3em 0.5em; +} + +table.footnote, table.footnote td { + border: 0 !important; +} + +div.footer { + line-height: 150%; + margin-top: -2em; + text-align: right; + width: auto; + margin-right: 10px; +} + +div.footer a:hover { + color: #0095C4; +} + +.refcount { + color: #060; +} + +.stableabi { + color: #229; +} + +.highlight { + background: none !important; +} + diff -r 128b45caba5b tools/pydoctheme/theme.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/pydoctheme/theme.conf Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,23 @@ +[theme] +inherit = default +stylesheet = pydoctheme.css +pygments_style = sphinx + +[options] +bodyfont = 'Lucida Grande', Arial, sans-serif +headfont = 'Lucida Grande', Arial, sans-serif +footerbgcolor = white +footertextcolor = #555555 +relbarbgcolor = white +relbartextcolor = #666666 +relbarlinkcolor = #444444 +sidebarbgcolor = white +sidebartextcolor = #444444 +sidebarlinkcolor = #444444 +bgcolor = white +textcolor = #222222 +linkcolor = #0090c0 +visitedlinkcolor = #00608f +headtextcolor = #1a1a1a +headbgcolor = white +headlinkcolor = #aaaaaa diff -r 128b45caba5b tools/rstlint.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/rstlint.py Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Check for stylistic and formal issues in .rst and .py +# files included in the documentation. +# +# 01/2009, Georg Brandl + +# TODO: - wrong versions in versionadded/changed +# - wrong markup after versionchanged directive + +from __future__ import with_statement + +import os +import re +import sys +import getopt +from os.path import join, splitext, abspath, exists +from collections import defaultdict + +directives = [ + # standard docutils ones + 'admonition', 'attention', 'caution', 'class', 'compound', 'container', + 'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph', + 'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image', + 'important', 'include', 'line-block', 'list-table', 'meta', 'note', + 'parsed-literal', 'pull-quote', 'raw', 'replace', + 'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar', + 'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning', + # Sphinx and Python docs custom ones + 'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata', + 'autoexception', 'autofunction', 'automethod', 'automodule', 'centered', + 'cfunction', 'class', 'classmethod', 'cmacro', 'cmdoption', 'cmember', + 'code-block', 'confval', 'cssclass', 'ctype', 'currentmodule', 'cvar', + 'data', 'decorator', 'decoratormethod', 'deprecated-removed', + 'deprecated(?!-removed)', 'describe', 'directive', 'doctest', 'envvar', + 'event', 'exception', 'function', 'glossary', 'highlight', 'highlightlang', + 'impl-detail', 'index', 'literalinclude', 'method', 'miscnews', 'module', + 'moduleauthor', 'opcode', 'pdbcommand', 'productionlist', + 'program', 'role', 'sectionauthor', 'seealso', 'sourcecode', 'staticmethod', + 'tabularcolumns', 'testcode', 'testoutput', 'testsetup', 'toctree', 'todo', + 'todolist', 'versionadded', 'versionchanged' +] + +all_directives = '(' + '|'.join(directives) + ')' +seems_directive_re = re.compile(r'(? 81: + # don't complain about tables, links and function signatures + if line.lstrip()[0] not in '+|' and \ + 'http://' not in line and \ + not line.lstrip().startswith(('.. function', + '.. method', + '.. cfunction')): + yield lno+1, "line too long" + + +@checker('.html', severity=2, falsepositives=True) +def check_leaked_markup(fn, lines): + """Check HTML files for leaked reST markup; this only works if + the HTML files have been built. + """ + for lno, line in enumerate(lines): + if leaked_markup_re.search(line): + yield lno+1, 'possibly leaked markup: %r' % line + + +def main(argv): + usage = '''\ +Usage: %s [-v] [-f] [-s sev] [-i path]* [path] + +Options: -v verbose (print all checked file names) + -f enable checkers that yield many false positives + -s sev only show problems with severity >= sev + -i path ignore subdir or file path +''' % argv[0] + try: + gopts, args = getopt.getopt(argv[1:], 'vfs:i:') + except getopt.GetoptError: + print(usage) + return 2 + + verbose = False + severity = 1 + ignore = [] + falsepos = False + for opt, val in gopts: + if opt == '-v': + verbose = True + elif opt == '-f': + falsepos = True + elif opt == '-s': + severity = int(val) + elif opt == '-i': + ignore.append(abspath(val)) + + if len(args) == 0: + path = '.' + elif len(args) == 1: + path = args[0] + else: + print(usage) + return 2 + + if not exists(path): + print('Error: path %s does not exist' % path) + return 2 + + count = defaultdict(int) + + for root, dirs, files in os.walk(path): + # ignore subdirs in ignore list + if abspath(root) in ignore: + del dirs[:] + continue + + for fn in files: + fn = join(root, fn) + if fn[:2] == './': + fn = fn[2:] + + # ignore files in ignore list + if abspath(fn) in ignore: + continue + + ext = splitext(fn)[1] + checkerlist = checkers.get(ext, None) + if not checkerlist: + continue + + if verbose: + print('Checking %s...' % fn) + + try: + with open(fn, 'r', encoding='utf-8') as f: + lines = list(f) + except (IOError, OSError) as err: + print('%s: cannot open: %s' % (fn, err)) + count[4] += 1 + continue + + for checker in checkerlist: + if checker.falsepositives and not falsepos: + continue + csev = checker.severity + if csev >= severity: + for lno, msg in checker(fn, lines): + print('[%d] %s:%d: %s' % (csev, fn, lno, msg)) + count[csev] += 1 + if verbose: + print() + if not count: + if severity > 1: + print('No problems with severity >= %d found.' % severity) + else: + print('No problems found.') + else: + for severity in sorted(count): + number = count[severity] + print('%d problem%s with severity %d found.' % + (number, number > 1 and 's' or '', severity)) + return int(bool(count)) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff -r 128b45caba5b tools/static/copybutton.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/static/copybutton.js Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,62 @@ +$(document).ready(function() { + /* Add a [>>>] button on the top-right corner of code samples to hide + * the >>> and ... prompts and the output and thus make the code + * copyable. */ + var div = $('.highlight-python .highlight,' + + '.highlight-python3 .highlight') + var pre = div.find('pre'); + + // get the styles from the current theme + pre.parent().parent().css('position', 'relative'); + var hide_text = 'Hide the prompts and output'; + var show_text = 'Show the prompts and output'; + var border_width = pre.css('border-top-width'); + var border_style = pre.css('border-top-style'); + var border_color = pre.css('border-top-color'); + var button_styles = { + 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', + 'border-color': border_color, 'border-style': border_style, + 'border-width': border_width, 'color': border_color, 'text-size': '75%', + 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', + 'border-radius': '0 3px 0 0' + } + + // create and add the button to all the code blocks that contain >>> + div.each(function(index) { + var jthis = $(this); + if (jthis.find('.gp').length > 0) { + var button = $('>>>'); + button.css(button_styles) + button.attr('title', hide_text); + button.data('hidden', 'false'); + jthis.prepend(button); + } + // tracebacks (.gt) contain bare text elements that need to be + // wrapped in a span to work with .nextUntil() (see later) + jthis.find('pre:has(.gt)').contents().filter(function() { + return ((this.nodeType == 3) && (this.data.trim().length > 0)); + }).wrap(''); + }); + + // define the behavior of the button when it's clicked + $('.copybutton').click(function(e){ + e.preventDefault(); + var button = $(this); + if (button.data('hidden') === 'false') { + // hide the code output + button.parent().find('.go, .gp, .gt').hide(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); + button.css('text-decoration', 'line-through'); + button.attr('title', show_text); + button.data('hidden', 'true'); + } else { + // show the code output + button.parent().find('.go, .gp, .gt').show(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); + button.css('text-decoration', 'none'); + button.attr('title', hide_text); + button.data('hidden', 'false'); + } + }); +}); + diff -r 128b45caba5b tools/static/py.png Binary file tools/static/py.png has changed diff -r 128b45caba5b tools/static/sidebar.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/static/sidebar.js Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,193 @@ +/* + * sidebar.js + * ~~~~~~~~~~ + * + * This script makes the Sphinx sidebar collapsible and implements intelligent + * scrolling. + * + * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds in + * .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton used to + * collapse and expand the sidebar. + * + * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden and the + * width of the sidebar and the margin-left of the document are decreased. + * When the sidebar is expanded the opposite happens. This script saves a + * per-browser/per-session cookie used to remember the position of the sidebar + * among the pages. Once the browser is closed the cookie is deleted and the + * position reset to the default (expanded). + * + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +$(function() { + // global elements used by the functions. + // the 'sidebarbutton' element is defined as global after its + // creation, in the add_sidebar_button function + var jwindow = $(window); + var jdocument = $(document); + var bodywrapper = $('.bodywrapper'); + var sidebar = $('.sphinxsidebar'); + var sidebarwrapper = $('.sphinxsidebarwrapper'); + + // original margin-left of the bodywrapper and width of the sidebar + // with the sidebar expanded + var bw_margin_expanded = bodywrapper.css('margin-left'); + var ssb_width_expanded = sidebar.width(); + + // margin-left of the bodywrapper and width of the sidebar + // with the sidebar collapsed + var bw_margin_collapsed = '.8em'; + var ssb_width_collapsed = '.8em'; + + // colors used by the current theme + var dark_color = '#AAAAAA'; + var light_color = '#CCCCCC'; + + function get_viewport_height() { + if (window.innerHeight) + return window.innerHeight; + else + return jwindow.height(); + } + + function sidebar_is_collapsed() { + return sidebarwrapper.is(':not(:visible)'); + } + + function toggle_sidebar() { + if (sidebar_is_collapsed()) + expand_sidebar(); + else + collapse_sidebar(); + // adjust the scrolling of the sidebar + scroll_sidebar(); + } + + function collapse_sidebar() { + sidebarwrapper.hide(); + sidebar.css('width', ssb_width_collapsed); + bodywrapper.css('margin-left', bw_margin_collapsed); + sidebarbutton.css({ + 'margin-left': '0', + 'height': bodywrapper.height(), + 'border-radius': '5px' + }); + sidebarbutton.find('span').text('»'); + sidebarbutton.attr('title', _('Expand sidebar')); + document.cookie = 'sidebar=collapsed'; + } + + function expand_sidebar() { + bodywrapper.css('margin-left', bw_margin_expanded); + sidebar.css('width', ssb_width_expanded); + sidebarwrapper.show(); + sidebarbutton.css({ + 'margin-left': ssb_width_expanded-12, + 'height': bodywrapper.height(), + 'border-radius': '0 5px 5px 0' + }); + sidebarbutton.find('span').text('«'); + sidebarbutton.attr('title', _('Collapse sidebar')); + //sidebarwrapper.css({'padding-top': + // Math.max(window.pageYOffset - sidebarwrapper.offset().top, 10)}); + document.cookie = 'sidebar=expanded'; + } + + function add_sidebar_button() { + sidebarwrapper.css({ + 'float': 'left', + 'margin-right': '0', + 'width': ssb_width_expanded - 28 + }); + // create the button + sidebar.append( + '
«
' + ); + var sidebarbutton = $('#sidebarbutton'); + // find the height of the viewport to center the '<<' in the page + var viewport_height = get_viewport_height(); + var sidebar_offset = sidebar.offset().top; + var sidebar_height = Math.max(bodywrapper.height(), sidebar.height()); + sidebarbutton.find('span').css({ + 'display': 'block', + 'position': 'fixed', + 'top': Math.min(viewport_height/2, sidebar_height/2 + sidebar_offset) - 10 + }); + + sidebarbutton.click(toggle_sidebar); + sidebarbutton.attr('title', _('Collapse sidebar')); + sidebarbutton.css({ + 'border-radius': '0 5px 5px 0', + 'color': '#444444', + 'background-color': '#CCCCCC', + 'font-size': '1.2em', + 'cursor': 'pointer', + 'height': sidebar_height, + 'padding-top': '1px', + 'padding-left': '1px', + 'margin-left': ssb_width_expanded - 12 + }); + + sidebarbutton.hover( + function () { + $(this).css('background-color', dark_color); + }, + function () { + $(this).css('background-color', light_color); + } + ); + } + + function set_position_from_cookie() { + if (!document.cookie) + return; + var items = document.cookie.split(';'); + for(var k=0; k wintop && curbot > winbot) { + sidebarwrapper.css('top', $u.max([wintop - offset - 10, 0])); + } + else if (curtop < wintop && curbot < winbot) { + sidebarwrapper.css('top', $u.min([winbot - sidebar_height - offset - 20, + jdocument.height() - sidebar_height - 200])); + } + } + } + jwindow.scroll(scroll_sidebar); +}); diff -r 128b45caba5b tools/static/version_switch.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/static/version_switch.js Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,68 @@ +(function() { + 'use strict'; + + var all_versions = { + '3.6': 'dev (3.6)', + '3.5': '3.5', + '3.4': '3.4', + '3.3': '3.3', + '3.2': '3.2', + '2.7': '2.7', + '2.6': '2.6' + }; + + function build_select(current_version, current_release) { + var buf = [''); + return buf.join(''); + } + + function patch_url(url, new_version) { + var url_re = /\.org\/(\d|py3k|dev|((release\/)?\d\.\d[\w\d\.]*))\//, + new_url = url.replace(url_re, '.org/' + new_version + '/'); + + if (new_url == url && !new_url.match(url_re)) { + // python 2 url without version? + new_url = url.replace(/\.org\//, '.org/' + new_version + '/'); + } + return new_url; + } + + function on_switch() { + var selected = $(this).children('option:selected').attr('value'); + + var url = window.location.href, + new_url = patch_url(url, selected); + + if (new_url != url) { + // check beforehand if url exists, else redirect to version's start page + $.ajax({ + url: new_url, + success: function() { + window.location.href = new_url; + }, + error: function() { + window.location.href = 'https://docs.python.org/' + selected; + } + }); + } + } + + $(document).ready(function() { + var release = DOCUMENTATION_OPTIONS.VERSION; + var version = release.substr(0, 3); + var select = build_select(version, release); + + $('.version_switcher_placeholder').html(select); + $('.version_switcher_placeholder select').bind('change', on_switch); + }); +})(); diff -r 128b45caba5b tools/susp-ignored.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/susp-ignored.csv Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,298 @@ +c-api/arg,,:ref,"PyArg_ParseTuple(args, ""O|O:ref"", &object, &callback)" +c-api/list,,:high,list[low:high] +c-api/sequence,,:i2,del o[i1:i2] +c-api/sequence,,:i2,o[i1:i2] +c-api/unicode,,:end,str[start:end] +c-api/unicode,,:start,unicode[start:start+length] +distutils/examples,267,`,This is the description of the ``foobar`` package. +distutils/setupscript,,::, +extending/embedding,,:numargs,"if(!PyArg_ParseTuple(args, "":numargs""))" +extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);" +extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {" +extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {" +faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(" +faq/programming,,::,for x in sequence[::-1]: +faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y," +faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro," +faq/windows,,:bd8afb90ebf2,"Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:55:48) [MSC v.1600 32 bit (Intel)] on win32" +howto/cporting,,:encode,"if (!PyArg_ParseTuple(args, ""O:encode_object"", &myobj))" +howto/cporting,,:say,"if (!PyArg_ParseTuple(args, ""U:say_hello"", &name))" +howto/curses,,:black,"colors when it activates color mode. They are: 0:black, 1:red," +howto/curses,,:red,"colors when it activates color mode. They are: 0:black, 1:red," +howto/curses,,:green,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/curses,,:yellow,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/curses,,:blue,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/curses,,:magenta,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/curses,,:cyan,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/curses,,:white,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The" +howto/ipaddress,,:DB8,>>> ipaddress.ip_address('2001:DB8::1') +howto/ipaddress,,::,>>> ipaddress.ip_address('2001:DB8::1') +howto/ipaddress,,:db8,IPv6Address('2001:db8::1') +howto/ipaddress,,::,IPv6Address('2001:db8::1') +howto/ipaddress,,::,IPv6Address('::1') +howto/ipaddress,,:db8,>>> ipaddress.ip_network('2001:db8::0/96') +howto/ipaddress,,::,>>> ipaddress.ip_network('2001:db8::0/96') +howto/ipaddress,,:db8,IPv6Network('2001:db8::/96') +howto/ipaddress,,::,IPv6Network('2001:db8::/96') +howto/ipaddress,,:db8,IPv6Network('2001:db8::/128') +howto/ipaddress,,::,IPv6Network('2001:db8::/128') +howto/ipaddress,,:db8,IPv6Interface('2001:db8::1/96') +howto/ipaddress,,::,IPv6Interface('2001:db8::1/96') +howto/ipaddress,,:db8,>>> addr6 = ipaddress.ip_address('2001:db8::1') +howto/ipaddress,,::,>>> addr6 = ipaddress.ip_address('2001:db8::1') +howto/ipaddress,,:db8,>>> host6 = ipaddress.ip_interface('2001:db8::1/96') +howto/ipaddress,,::,>>> host6 = ipaddress.ip_interface('2001:db8::1/96') +howto/ipaddress,,:db8,>>> net6 = ipaddress.ip_network('2001:db8::0/96') +howto/ipaddress,,::,>>> net6 = ipaddress.ip_network('2001:db8::0/96') +howto/ipaddress,,:ffff,IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::') +howto/ipaddress,,::,IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::') +howto/ipaddress,,::,IPv6Address('::ffff:ffff') +howto/ipaddress,,:ffff,IPv6Address('::ffff:ffff') +howto/ipaddress,,:db8,'2001:db8::/96' +howto/ipaddress,,::,'2001:db8::/96' +howto/ipaddress,,:db8,>>> ipaddress.ip_interface('2001:db8::1/96') +howto/ipaddress,,::,>>> ipaddress.ip_interface('2001:db8::1/96') +howto/ipaddress,,:db8,'2001:db8::1' +howto/ipaddress,,::,'2001:db8::1' +howto/ipaddress,,:db8,IPv6Address('2001:db8::ffff:ffff') +howto/ipaddress,,::,IPv6Address('2001:db8::ffff:ffff') +howto/ipaddress,,:ffff,IPv6Address('2001:db8::ffff:ffff') +howto/logging,,:And,"WARNING:And this, too" +howto/logging,,:And,"WARNING:root:And this, too" +howto/logging,,:Doing,INFO:root:Doing something +howto/logging,,:Finished,INFO:root:Finished +howto/logging,,:logger,severity:logger name:message +howto/logging,,:Look,WARNING:root:Look before you leap! +howto/logging,,:message,severity:logger name:message +howto/logging,,:root,DEBUG:root:This message should go to the log file +howto/logging,,:root,INFO:root:Doing something +howto/logging,,:root,INFO:root:Finished +howto/logging,,:root,INFO:root:So should this +howto/logging,,:root,INFO:root:Started +howto/logging,,:root,"WARNING:root:And this, too" +howto/logging,,:root,WARNING:root:Look before you leap! +howto/logging,,:root,WARNING:root:Watch out! +howto/logging,,:So,INFO:root:So should this +howto/logging,,:So,INFO:So should this +howto/logging,,:Started,INFO:root:Started +howto/logging,,:This,DEBUG:root:This message should go to the log file +howto/logging,,:This,DEBUG:This message should appear on the console +howto/logging,,:Watch,WARNING:root:Watch out! +howto/pyporting,,::,Programming Language :: Python :: 2 +howto/pyporting,,::,Programming Language :: Python :: 3 +howto/regex,,::, +howto/regex,,:foo,(?:foo) +howto/urllib2,,:password,"for example ""joe:password@example.com""" +library/audioop,,:ipos,"# factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)]," +library/bisect,32,:hi,all(val >= x for val in a[i:hi]) +library/bisect,42,:hi,all(val > x for val in a[i:hi]) +library/configparser,,:home,my_dir: ${Common:home_dir}/twosheds +library/configparser,,:option,${section:option} +library/configparser,,:path,python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python} +library/configparser,,:Python,python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python} +library/configparser,,:system,path: ${Common:system_dir}/Library/Frameworks/ +library/datetime,,:MM, +library/datetime,,:SS, +library/decimal,,:optional,"trailneg:optional trailing minus indicator" +library/difflib,,:ahi,a[alo:ahi] +library/difflib,,:bhi,b[blo:bhi] +library/difflib,,:i1, +library/difflib,,:i2, +library/difflib,,:j2, +library/doctest,,`,``factorial`` from the ``example`` module: +library/doctest,,`,The ``example`` module +library/doctest,,`,Using ``factorial`` +library/exceptions,,:err,err.object[err.start:err.end] +library/functions,,:step,a[start:stop:step] +library/functions,,:stop,"a[start:stop, i]" +library/functions,,:stop,a[start:stop:step] +library/http.client,,:port,host:port +library/http.cookies,,`,!#$%&'*+-.^_`|~: +library/imaplib,,:MM,"""DD-Mmm-YYYY HH:MM:SS" +library/imaplib,,:SS,"""DD-Mmm-YYYY HH:MM:SS" +library/inspect,,:int,">>> def foo(a, *, b:int, **kwargs):" +library/inspect,,:int,"'(a, *, b:int, **kwargs)'" +library/inspect,,:int,'b:int' +library/ipaddress,,:db8,>>> ipaddress.ip_address('2001:db8::') +library/ipaddress,,::,>>> ipaddress.ip_address('2001:db8::') +library/ipaddress,,:db8,IPv6Address('2001:db8::') +library/ipaddress,,::,IPv6Address('2001:db8::') +library/ipaddress,,:db8,>>> ipaddress.IPv6Address('2001:db8::1000') +library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000') +library/ipaddress,,:db8,IPv6Address('2001:db8::1000') +library/ipaddress,,::,IPv6Address('2001:db8::1000') +library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer" +library/ipaddress,,::,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer" +library/ipaddress,,::,"""::abc:7:def""" +library/ipaddress,,:def,"""::abc:7:def""" +library/ipaddress,,::,::FFFF/96 +library/ipaddress,,::,2002::/16 +library/ipaddress,,::,2001::/32 +library/ipaddress,,::,>>> str(ipaddress.IPv6Address('::1')) +library/ipaddress,,::,'::1' +library/ipaddress,,:ff00,ffff:ff00:: +library/ipaddress,,:db00,2001:db00::0/24 +library/ipaddress,,::,2001:db00::0/24 +library/ipaddress,,:db00,2001:db00::0/ffff:ff00:: +library/ipaddress,,::,2001:db00::0/ffff:ff00:: +library/itertools,,:step,elements from seq[start:stop:step] +library/itertools,,:stop,elements from seq[start:stop:step] +library/logging.handlers,,:port,host:port +library/mmap,,:i2,obj[i1:i2] +library/multiprocessing,,`,# Add more tasks using `put()` +library/multiprocessing,,:queue,">>> QueueManager.register('get_queue', callable=lambda:queue)" +library/multiprocessing,,`,# register the Foo class; make `f()` and `g()` accessible via proxy +library/multiprocessing,,`,# register the Foo class; make `g()` and `_h()` accessible via proxy +library/multiprocessing,,`,# register the generator function baz; use `GeneratorProxy` to make proxies +library/nntplib,,:bytes,:bytes +library/nntplib,,:lines,:lines +library/optparse,,:len,"del parser.rargs[:len(value)]" +library/os.path,,:foo,c:foo +library/pathlib,,:bar,">>> PureWindowsPath('c:/Windows', 'd:bar')" +library/pathlib,,:bar,PureWindowsPath('d:bar') +library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').root +library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').anchor +library/pdb,,:lineno,filename:lineno +library/pickle,,:memory,"conn = sqlite3.connect("":memory:"")" +library/posix,,`,"CFLAGS=""`getconf LFS_CFLAGS`"" OPT=""-g -O2 $CFLAGS""" +library/pprint,,::,"'Programming Language :: Python :: 2 :: Only']," +library/pprint,,::,"'Programming Language :: Python :: 2.6'," +library/pprint,,::,"'Programming Language :: Python :: 2.7'," +library/profile,,:lineno,filename:lineno(function) +library/pyexpat,,:elem1, +library/pyexpat,,:py,"xmlns:py = ""http://www.python.org/ns/"">" +library/smtplib,,:port,method must support that as well as a regular host:port +library/socket,,::,'5aef:2b::8' +library/socket,,:can,"return (can_id, can_dlc, data[:can_dlc])" +library/socket,,:len,fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) +library/sqlite3,,:age,"cur.execute(""select * from people where name_last=:who and age=:age"", {""who"": who, ""age"": age})" +library/sqlite3,,:memory, +library/sqlite3,,:who,"cur.execute(""select * from people where name_last=:who and age=:age"", {""who"": who, ""age"": age})" +library/sqlite3,,:path,"db = sqlite3.connect('file:path/to/database?mode=ro', uri=True)" +library/ssl,,:My,"Organizational Unit Name (eg, section) []:My Group" +library/ssl,,:My,"Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc." +library/ssl,,:myserver,"Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com" +library/ssl,,:MyState,State or Province Name (full name) [Some-State]:MyState +library/ssl,,:ops,Email Address []:ops@myserver.mygroup.myorganization.com +library/ssl,,:Some,"Locality Name (eg, city) []:Some City" +library/ssl,,:US,Country Name (2 letter code) [AU]:US +library/stdtypes,,:end,s[start:end] +library/stdtypes,,::,>>> hash(v[::-2]) == hash(b'abcefg'[::-2]) +library/stdtypes,,:len,s[len(s):len(s)] +library/stdtypes,,::,>>> y = m[::2] +library/stdtypes,,::,>>> z = y[::-2] +library/subprocess,,`,"output=`dmesg | grep hda`" +library/subprocess,,`,"output=`mycmd myarg`" +library/tarfile,,:bz2, +library/tarfile,,:compression,filemode[:compression] +library/tarfile,,:gz, +library/tarfile,,:xz,'a:xz' +library/tarfile,,:xz,'r:xz' +library/tarfile,,:xz,'w:xz' +library/time,,:mm, +library/time,,:ss, +library/tracemalloc,,:limit,"for index, stat in enumerate(top_stats[:limit], 1):" +library/turtle,,::,Example:: +library/unittest,,:foo,"self.assertEqual(cm.output, ['INFO:foo:first message'," +library/unittest,,:first,"self.assertEqual(cm.output, ['INFO:foo:first message'," +library/unittest,,:foo,'ERROR:foo.bar:second message']) +library/unittest,,:second,'ERROR:foo.bar:second message']) +library/urllib.request,,:close,Connection:close +library/urllib.request,,:port,:port +library/urllib.request,,:lang,"xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"" lang=""en"">\n\n\n" +library/urllib.request,,:password,"""joe:password@python.org""" +library/uuid,,:uuid,urn:uuid:12345678-1234-5678-1234-567812345678 +library/venv,,:param,":param nodist: If True, setuptools and pip are not installed into the" +library/venv,,:param,":param progress: If setuptools or pip are installed, the progress of the" +library/venv,,:param,":param nopip: If True, pip is not installed into the created" +library/venv,,:param,:param context: The information for the environment creation request +library/xmlrpc.client,,:pass,http://user:pass@host:port/path +library/xmlrpc.client,,:pass,user:pass +library/xmlrpc.client,,:port,http://user:pass@host:port/path +license,,`,"``Software''), to deal in the Software without restriction, including" +license,,`,"THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND," +license,,`,* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND +license,,`,THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +license,,`,* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY +license,,`,THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +license,,:zooko,mailto:zooko@zooko.com +reference/expressions,,:index,x[index:index] +reference/lexical_analysis,,`,$ ? ` +reference/lexical_analysis,,:fileencoding,# vim:fileencoding= +tutorial/datastructures,,:value,It is also possible to delete a key:value +tutorial/datastructures,,:value,key:value pairs within the braces adds initial key:value pairs +tutorial/stdlib2,,:config,"logging.warning('Warning:config file %s not found', 'server.conf')" +tutorial/stdlib2,,:config,WARNING:root:Warning:config file server.conf not found +tutorial/stdlib2,,:Critical,CRITICAL:root:Critical error -- shutting down +tutorial/stdlib2,,:Error,ERROR:root:Error occurred +tutorial/stdlib2,,:root,CRITICAL:root:Critical error -- shutting down +tutorial/stdlib2,,:root,ERROR:root:Error occurred +tutorial/stdlib2,,:root,WARNING:root:Warning:config file server.conf not found +tutorial/stdlib2,,:start,extra = data[start:start+extra_size] +tutorial/stdlib2,,:start,"fields = struct.unpack('>> urlparse.urlparse('http://[1080::8:800:200C:417A]/foo') +whatsnew/2.7,,:Sunday,'2009:4:Sunday' +whatsnew/2.7,,:Cookie,"export PYTHONWARNINGS=all,error:::Cookie:0" +whatsnew/2.7,,::,"export PYTHONWARNINGS=all,error:::Cookie:0" +whatsnew/3.2,,:affe,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]'," +whatsnew/3.2,,:affe,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/') +whatsnew/3.2,,:beef,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]'," +whatsnew/3.2,,:beef,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/') +whatsnew/3.2,,:cafe,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]'," +whatsnew/3.2,,:cafe,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/') +whatsnew/3.2,,:deaf,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]'," +whatsnew/3.2,,:deaf,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/') +whatsnew/3.2,,:directory,${buildout:directory}/downloads/dist +whatsnew/3.2,,::,"$ export PYTHONWARNINGS='ignore::RuntimeWarning::,once::UnicodeWarning::'" +whatsnew/3.2,,:feed,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]'," +whatsnew/3.2,,:feed,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/') +whatsnew/3.2,,:gz,">>> with tarfile.open(name='myarchive.tar.gz', mode='w:gz') as tf:" +whatsnew/3.2,,:location,zope9-location = ${zope9:location} +whatsnew/3.2,,:prefix,zope-conf = ${custom:prefix}/etc/zope.conf +whatsnew/changelog,,:gz,": TarFile opened with external fileobj and ""w:gz"" mode didn't" +whatsnew/changelog,,::,": Use ""127.0.0.1"" or ""::1"" instead of ""localhost"" as much as" +library/tarfile,149,:xz,'x:xz' +library/xml.etree.elementtree,290,:sometag,prefix:sometag +library/xml.etree.elementtree,301,:fictional,"Lancelot +library/xml.etree.elementtree,301,:character,Archie Leach +library/xml.etree.elementtree,301,:character,Sir Robin +library/xml.etree.elementtree,301,:character,Gunther +library/xml.etree.elementtree,301,:character,Commander Clement +library/xml.etree.elementtree,332,:actor,"for actor in root.findall('real_person:actor', ns):" +library/xml.etree.elementtree,332,:name,"name = actor.find('real_person:name', ns)" +library/xml.etree.elementtree,332,:character,"for char in actor.findall('role:character', ns):" +library/zipapp,31,:main,"$ python -m zipapp myapp -m ""myapp:main""" +library/zipapp,82,:fn,"argument should have the form ""pkg.mod:fn"", where ""pkg.mod"" is a" +library/zipapp,155,:callable,"""pkg.module:callable"" and the archive will be run by importing" +library/stdtypes,,::,>>> m[::2].tolist() +library/sys,,`,# ``wrapper`` creates a ``wrap(coro)`` coroutine: +tutorial/venv,77,:c7b9645a6f35,"Python 3.4.3+ (3.4:c7b9645a6f35+, May 22 2015, 09:31:25)" +whatsnew/3.5,,:root,'WARNING:root:warning\n' +whatsnew/3.5,,:warning,'WARNING:root:warning\n' +whatsnew/3.5,,::,>>> addr6 = ipaddress.IPv6Address('::1') +whatsnew/3.5,,:root,ERROR:root:exception +whatsnew/3.5,,:exception,ERROR:root:exception diff -r 128b45caba5b tools/templates/download.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/templates/download.html Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,65 @@ +{% extends "layout.html" %} +{% set title = 'Download' %} +{% if daily is defined %} + {% set dlbase = pathto('archives', 1) %} +{% else %} + {% set dlbase = 'https://docs.python.org/ftp/python/doc/' + release %} +{% endif %} + +{% block body %} +

Download Python {{ release }} Documentation

+ +{% if last_updated %}

Last updated on: {{ last_updated }}.

{% endif %} + +

To download an archive containing all the documents for this version of +Python in one of various formats, follow one of links in this table. The numbers +in the table are the size of the download files in megabytes.

+ + + + + + + + + + + + + + + + + + + + + + + +
FormatPacked as .zipPacked as .tar.bz2
PDF (US-Letter paper size)Download (ca. 8 MB)Download (ca. 8 MB)
PDF (A4 paper size)Download (ca. 8 MB)Download (ca. 8 MB)
HTMLDownload (ca. 6 MB)Download (ca. 4 MB)
Plain TextDownload (ca. 2 MB)Download (ca. 1.5 MB)
EPUBDownload (ca. 4.5 MB)
+ +

These archives contain all the content in the documentation.

+ +

HTML Help (.chm) files are made available in the "Windows" section +on the Python +download page.

+ + +

Unpacking

+ +

Unix users should download the .tar.bz2 archives; these are bzipped tar +archives and can be handled in the usual way using tar and the bzip2 +program. The InfoZIP unzip program can be +used to handle the ZIP archives if desired. The .tar.bz2 archives provide the +best compression and fastest download times.

+ +

Windows users can use the ZIP archives since those are customary on that +platform. These are created on Unix using the InfoZIP zip program.

+ + +

Problems

+ +

If you have comments or suggestions for the Python documentation, please send +email to docs@python.org.

+{% endblock %} diff -r 128b45caba5b tools/templates/indexcontent.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/templates/indexcontent.html Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,59 @@ +{% extends "defindex.html" %} +{% block tables %} +

{% trans %}Parts of the documentation:{% endtrans %}

+ + +
+ + + + + + + + + + + + +
+ +

{% trans %}Indices and tables:{% endtrans %}

+ + +
+ + + + + + +
+ +

{% trans %}Meta information:{% endtrans %}

+ + +
+ + + + + +
+{% endblock %} diff -r 128b45caba5b tools/templates/indexsidebar.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/templates/indexsidebar.html Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,17 @@ +

{% trans %}Download{% endtrans %}

+

{% trans %}Download these documents{% endtrans %}

+

{% trans %}Docs for other versions{% endtrans %}

+ + +

{% trans %}Other resources{% endtrans %}

+ diff -r 128b45caba5b tools/templates/layout.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/templates/layout.html Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,102 @@ +{% extends "!layout.html" %} +{% block rootrellink %} +
  • +
  • Python{{ reldelim1 }}
  • +
  • + {%- if versionswitcher is defined %} + {{ release }} + {% trans %}Documentation {% endtrans %}{{ reldelim1 }} + {%- else %} + {{ shorttitle }}{{ reldelim1 }} + {%- endif %} +
  • +{% endblock %} +{% block relbar1 %} {% if builder != 'qthelp' %} {{ relbar() }} {% endif %} {% endblock %} +{% block relbar2 %} {% if builder != 'qthelp' %} {{ relbar() }} {% endif %} {% endblock %} +{% block extrahead %} + + {% if not embedded %}{% endif %} + {% if versionswitcher is defined and not embedded %}{% endif %} + {% if pagename == 'whatsnew/changelog' and not embedded %} + + {% endif %} +{{ super() }} +{% endblock %} +{% block footer %} + +{% endblock %} +{% block sidebarsourcelink %} +{%- if show_source and has_source and sourcename %} +

    {{ _('This Page') }}

    + +{%- endif %} +{% endblock %} diff -r 128b45caba5b tools/templates/opensearch.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/templates/opensearch.xml Wed Jun 29 21:41:03 2016 -0700 @@ -0,0 +1,4 @@ +{% extends "!opensearch.xml" %} +{% block extra -%} +https://www.python.org/images/favicon16x16.ico +{%- endblock %}