Run through black and isort

This commit is contained in:
Jan Dittberner 2021-01-02 06:20:53 +01:00
parent 0af66c6964
commit fdb126996a
8 changed files with 297 additions and 252 deletions

View file

@ -1 +1 @@
__import__('pkg_resources').declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__)

View file

@ -1 +1 @@
__import__('pkg_resources').declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__)

View file

@ -11,11 +11,9 @@
import re import re
from ipcalc import Network
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import Directive from docutils.parsers.rst import Directive
from ipcalc import Network
from sphinx import addnodes from sphinx import addnodes
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.errors import NoUri from sphinx.errors import NoUri
@ -24,15 +22,15 @@ from sphinx.roles import XRefRole
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
__version__ = '0.3.0' __version__ = "0.4.0"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def ip_object_anchor(typ, path): def ip_object_anchor(typ, path):
path = re.sub(r'[.:/]', '-', path) path = re.sub(r"[.:/]", "-", path)
return typ.lower() + '-' + path return typ.lower() + "-" + path
class ip_node(nodes.Inline, nodes.TextElement): class ip_node(nodes.Inline, nodes.TextElement):
@ -47,17 +45,16 @@ class IPXRefRole(XRefRole):
""" """
Cross referencing role for the IP domain. Cross referencing role for the IP domain.
""" """
def __init__(self, method, index_type, **kwargs): def __init__(self, method, index_type, **kwargs):
self.method = method self.method = method
self.index_type = index_type self.index_type = index_type
innernodeclass = None innernodeclass = None
if method in ('v4', 'v6'): if method in ("v4", "v6"):
innernodeclass = ip_node innernodeclass = ip_node
super(IPXRefRole, self).__init__( super(IPXRefRole, self).__init__(innernodeclass=innernodeclass, **kwargs)
innernodeclass=innernodeclass, **kwargs)
def __cal__(self, typ, rawtext, text, lineno, inliner, def __cal__(self, typ, rawtext, text, lineno, inliner, options=None, content=None):
options=None, content=None):
if content is None: if content is None:
content = [] content = []
if options is None: if options is None:
@ -66,28 +63,31 @@ class IPXRefRole(XRefRole):
Network(text) Network(text)
except ValueError as e: except ValueError as e:
env = inliner.document.settings.env env = inliner.document.settings.env
logger.warning("invalid ip address/range %s" % text, location=(env.docname, lineno)) logger.warning(
"invalid ip address/range %s" % text, location=(env.docname, lineno)
)
return [nodes.literal(text, text), []] return [nodes.literal(text, text), []]
return super(IPXRefRole, self).__call__( return super(IPXRefRole, self).__call__(
typ, rawtext, text, lineno, inliner, options, content) typ, rawtext, text, lineno, inliner, options, content
)
def process_link(self, env, refnode, has_explicit_title, title, target): def process_link(self, env, refnode, has_explicit_title, title, target):
domaindata = env.domaindata['ip'] domaindata = env.domaindata["ip"]
domaindata[self.method][target] = (target, refnode) domaindata[self.method][target] = (target, refnode)
return title, target return title, target
def result_nodes(self, document, env, node, is_ref): def result_nodes(self, document, env, node, is_ref):
try: try:
node['typ'] = self.method node["typ"] = self.method
indexnode = addnodes.index() indexnode = addnodes.index()
targetid = 'index-%s' % env.new_serialno('index') targetid = "index-%s" % env.new_serialno("index")
targetnode = nodes.target('', '', ids=[targetid]) targetnode = nodes.target("", "", ids=[targetid])
doctitle = list(document.traverse(nodes.title))[0].astext() doctitle = list(document.traverse(nodes.title))[0].astext()
idxtext = "%s; %s" % (node.astext(), doctitle) idxtext = "%s; %s" % (node.astext(), doctitle)
idxtext2 = "%s; %s" % (self.index_type, node.astext()) idxtext2 = "%s; %s" % (self.index_type, node.astext())
indexnode['entries'] = [ indexnode["entries"] = [
('single', idxtext, targetid, '', None), ("single", idxtext, targetid, "", None),
('single', idxtext2, targetid, '', None), ("single", idxtext2, targetid, "", None),
] ]
return [indexnode, targetnode, node], [] return [indexnode, targetnode, node], []
except KeyError as e: except KeyError as e:
@ -104,20 +104,21 @@ class IPRange(Directive):
def handle_rangespec(self, node): def handle_rangespec(self, node):
titlenode = nodes.title() titlenode = nodes.title()
node.append(titlenode) node.append(titlenode)
titlenode.append(nodes.inline('', self.get_prefix_title())) titlenode.append(nodes.inline("", self.get_prefix_title()))
titlenode.append(nodes.literal('', self.rangespec)) titlenode.append(nodes.literal("", self.rangespec))
ids = ip_object_anchor(self.typ, self.rangespec) ids = ip_object_anchor(self.typ, self.rangespec)
node['ids'].append(ids) node["ids"].append(ids)
self.env.domaindata[self.domain][self.typ][ids] = ( self.env.domaindata[self.domain][self.typ][ids] = (
self.env.docname, self.env.docname,
self.options.get('synopsis', '')) self.options.get("synopsis", ""),
)
return ids return ids
def run(self): def run(self):
if ':' in self.name: if ":" in self.name:
self.domain, self.objtype = self.name.split(':', 1) self.domain, self.objtype = self.name.split(":", 1)
else: else:
self.domain, self.objtype = '', self.name self.domain, self.objtype = "", self.name
self.env = self.state.document.settings.env self.env = self.state.document.settings.env
self.rangespec = self.arguments[0] self.rangespec = self.arguments[0]
node = nodes.section() node = nodes.section()
@ -127,97 +128,98 @@ class IPRange(Directive):
else: else:
doctitle = list(self.state.document.traverse(nodes.title))[0].astext() doctitle = list(self.state.document.traverse(nodes.title))[0].astext()
idx_text = "%s; %s" % (self.rangespec, doctitle) idx_text = "%s; %s" % (self.rangespec, doctitle)
self.indexnode = addnodes.index(entries=[ self.indexnode = addnodes.index(
('single', idx_text, name, '', None), entries=[
('single', self.get_index_text(), name, '', None) ("single", idx_text, name, "", None),
]) ("single", self.get_index_text(), name, "", None),
]
)
if self.content: if self.content:
contentnode = nodes.paragraph('') contentnode = nodes.paragraph("")
node.append(contentnode) node.append(contentnode)
self.state.nested_parse( self.state.nested_parse(self.content, self.content_offset, contentnode)
self.content, self.content_offset, contentnode)
iprange = ip_range() iprange = ip_range()
node.append(iprange) node.append(iprange)
iprange['rangespec'] = self.rangespec iprange["rangespec"] = self.rangespec
return [self.indexnode, node] return [self.indexnode, node]
class IPv4Range(IPRange): class IPv4Range(IPRange):
typ = 'v4range' typ = "v4range"
def get_prefix_title(self): def get_prefix_title(self):
return _('IPv4 address range ') return _("IPv4 address range ")
def get_index_text(self): def get_index_text(self):
return "%s; %s" % (_('IPv4 range'), self.rangespec) return "%s; %s" % (_("IPv4 range"), self.rangespec)
class IPv6Range(IPRange): class IPv6Range(IPRange):
typ = 'v6range' typ = "v6range"
def get_prefix_title(self): def get_prefix_title(self):
return _('IPv6 address range ') return _("IPv6 address range ")
def get_index_text(self): def get_index_text(self):
return "%s; %s" % (_('IPv6 range'), self.rangespec) return "%s; %s" % (_("IPv6 range"), self.rangespec)
class IPDomain(Domain): class IPDomain(Domain):
""" """
IP address and range domain. IP address and range domain.
""" """
name = 'ip'
label = 'IP addresses and ranges.' name = "ip"
label = "IP addresses and ranges."
object_types = { object_types = {
'v4': ObjType(_('v4'), 'v4', 'obj'), "v4": ObjType(_("v4"), "v4", "obj"),
'v6': ObjType(_('v6'), 'v6', 'obj'), "v6": ObjType(_("v6"), "v6", "obj"),
'v4range': ObjType(_('v4range'), 'v4range', 'obj'), "v4range": ObjType(_("v4range"), "v4range", "obj"),
'v6range': ObjType(_('v6range'), 'v6range', 'obj'), "v6range": ObjType(_("v6range"), "v6range", "obj"),
} }
directives = { directives = {
'v4range': IPv4Range, "v4range": IPv4Range,
'v6range': IPv6Range, "v6range": IPv6Range,
} }
roles = { roles = {
'v4': IPXRefRole('v4', _('IPv4 address')), "v4": IPXRefRole("v4", _("IPv4 address")),
'v6': IPXRefRole('v6', _('IPv6 address')), "v6": IPXRefRole("v6", _("IPv6 address")),
'v4range': IPXRefRole('v4range', _('IPv4 range')), "v4range": IPXRefRole("v4range", _("IPv4 range")),
'v6range': IPXRefRole('v6range', _('IPv6 range')), "v6range": IPXRefRole("v6range", _("IPv6 range")),
} }
initial_data = { initial_data = {
'v4': {}, "v4": {},
'v6': {}, "v6": {},
'v4range': {}, "v4range": {},
'v6range': {}, "v6range": {},
'ips': [], "ips": [],
} }
def clear_doc(self, docname): def clear_doc(self, docname):
to_remove = [] to_remove = []
for key, value in self.data['v4range'].items(): for key, value in self.data["v4range"].items():
if docname == value[0]: if docname == value[0]:
to_remove.append(key) to_remove.append(key)
for key in to_remove: for key in to_remove:
del self.data['v4range'][key] del self.data["v4range"][key]
to_remove = [] to_remove = []
for key, value in self.data['v6range'].items(): for key, value in self.data["v6range"].items():
if docname == value[0]: if docname == value[0]:
to_remove.append(key) to_remove.append(key)
for key in to_remove: for key in to_remove:
del self.data['v6range'][key] del self.data["v6range"][key]
self.data['ips'] = [ self.data["ips"] = [
item for item in self.data['ips'] if item['docname'] != docname item for item in self.data["ips"] if item["docname"] != docname
] ]
def resolve_xref(self, env, fromdocname, builder, typ, target, node, def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
contnode):
key = ip_object_anchor(typ, target) key = ip_object_anchor(typ, target)
try: try:
info = self.data[typ][key] info = self.data[typ][key]
@ -226,19 +228,20 @@ class IPDomain(Domain):
role = self.roles.get(typ) role = self.roles.get(typ)
if role is None: if role is None:
return None return None
resnode = role.result_nodes(env.get_doctree(fromdocname), resnode = role.result_nodes(env.get_doctree(fromdocname), env, node, True)[
env, node, True)[0][2] 0
][2]
if isinstance(resnode, addnodes.pending_xref): if isinstance(resnode, addnodes.pending_xref):
text = node[0][0] text = node[0][0]
reporter = env.get_doctree(fromdocname).reporter reporter = env.get_doctree(fromdocname).reporter
reporter.warning('Cannot resolve reference to %r' % text, reporter.warning(
line=node.line) "Cannot resolve reference to %r" % text, line=node.line
)
return node.children return node.children
return resnode return resnode
else: else:
title = typ.upper() + ' ' + target title = typ.upper() + " " + target
return make_refnode(builder, fromdocname, info[0], key, return make_refnode(builder, fromdocname, info[0], key, contnode, title)
contnode, title)
@property @property
def items(self): def items(self):
@ -257,13 +260,15 @@ def process_ips(app, doctree):
for node in doctree.traverse(ip_node): for node in doctree.traverse(ip_node):
ip = node.astext() ip = node.astext()
domaindata['ips'].append({ domaindata["ips"].append(
'docname': env.docname, {
'source': node.parent.source or env.doc2path(env.docname), "docname": env.docname,
'lineno': node.parent.line, "source": node.parent.source or env.doc2path(env.docname),
'ip': ip, "lineno": node.parent.line,
'typ': node.parent['typ'], "ip": ip,
}) "typ": node.parent["typ"],
}
)
replacement = nodes.literal(ip, ip) replacement = nodes.literal(ip, ip)
node.replace_self(replacement) node.replace_self(replacement)
@ -285,17 +290,14 @@ def process_ip_nodes(app, doctree, fromdocname):
env = app.builder.env env = app.builder.env
domaindata = env.domaindata[IPDomain.name] domaindata = env.domaindata[IPDomain.name]
header = (_('IP address'), _('Used by')) header = (_("IP address"), _("Used by"))
colwidths = (1, 3) colwidths = (1, 3)
for node in doctree.traverse(ip_range): for node in doctree.traverse(ip_range):
content = [] content = []
net = Network(node['rangespec']) net = Network(node["rangespec"])
ips = {} ips = {}
for key, value in [ for key, value in [(ip_info["ip"], ip_info) for ip_info in domaindata["ips"]]:
(ip_info['ip'], ip_info) for ip_info in
domaindata['ips']
]:
try: try:
if not key in net: if not key in net:
continue continue
@ -313,34 +315,32 @@ def process_ip_nodes(app, doctree, fromdocname):
tgroup += nodes.colspec(colwidth=colwidth) tgroup += nodes.colspec(colwidth=colwidth)
thead = nodes.thead() thead = nodes.thead()
tgroup += thead tgroup += thead
thead += create_table_row([ thead += create_table_row([nodes.paragraph(text=label) for label in header])
nodes.paragraph(text=label) for label in header])
tbody = nodes.tbody() tbody = nodes.tbody()
tgroup += tbody tgroup += tbody
for ip, ip_info in [ for ip, ip_info in [(ip, ips[ip]) for ip in sorted(ips, key=sort_ip)]:
(ip, ips[ip]) for ip in sorted(ips, key=sort_ip)
]:
para = nodes.paragraph() para = nodes.paragraph()
para += nodes.literal('', ip) para += nodes.literal("", ip)
refnode = nodes.paragraph() refnode = nodes.paragraph()
refuris = set() refuris = set()
refnodes = [] refnodes = []
for item in ip_info: for item in ip_info:
ids = ip_object_anchor(item['typ'], item['ip']) ids = ip_object_anchor(item["typ"], item["ip"])
if ids not in para['ids']: if ids not in para["ids"]:
para['ids'].append(ids) para["ids"].append(ids)
domaindata[item['typ']][ids] = (fromdocname, '') domaindata[item["typ"]][ids] = (fromdocname, "")
newnode = nodes.reference('', '', internal=True) newnode = nodes.reference("", "", internal=True)
try: try:
newnode['refuri'] = app.builder.get_relative_uri( newnode["refuri"] = app.builder.get_relative_uri(
fromdocname, item['docname']) fromdocname, item["docname"]
if newnode['refuri'] in refuris: )
if newnode["refuri"] in refuris:
continue continue
refuris.add(newnode['refuri']) refuris.add(newnode["refuri"])
except NoUri: except NoUri:
pass pass
title = env.titles[item['docname']] title = env.titles[item["docname"]]
innernode = nodes.Text(title.astext()) innernode = nodes.Text(title.astext())
newnode.append(innernode) newnode.append(innernode)
refnodes.append(newnode) refnodes.append(newnode)
@ -351,13 +351,13 @@ def process_ip_nodes(app, doctree, fromdocname):
tbody += create_table_row([para, refnode]) tbody += create_table_row([para, refnode])
content.append(table) content.append(table)
else: else:
para = nodes.paragraph(_('No IP addresses in this range')) para = nodes.paragraph(_("No IP addresses in this range"))
content.append(para) content.append(para)
node.replace_self(content) node.replace_self(content)
def setup(app): def setup(app):
app.add_domain(IPDomain) app.add_domain(IPDomain)
app.connect('doctree-read', process_ips) app.connect("doctree-read", process_ips)
app.connect('doctree-resolved', process_ip_nodes) app.connect("doctree-resolved", process_ip_nodes)
return {'version': __version__} return {"version": __version__}

View file

@ -1,16 +1,16 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from setuptools import setup, find_packages from setuptools import find_packages, setup
version = '0.3.1' version = "0.3.1"
with open('README.rst') as readme: with open("README.rst") as readme:
description = readme.read() + "\n\n" description = readme.read() + "\n\n"
with open('CHANGES.rst') as changes: with open("CHANGES.rst") as changes:
description += changes.read() description += changes.read()
requires = ['Sphinx>=3', 'ipcalc>=1.99'] requires = ["Sphinx>=3", "ipcalc>=1.99"]
setup( setup(
@ -24,9 +24,9 @@ setup(
license="GPLv3+", license="GPLv3+",
url="https://pypi.python.org/pypi/jandd.sphinxext.ip", url="https://pypi.python.org/pypi/jandd.sphinxext.ip",
name="jandd.sphinxext.ip", name="jandd.sphinxext.ip",
namespace_packages=['jandd', 'jandd.sphinxext'], namespace_packages=["jandd", "jandd.sphinxext"],
packages=find_packages(), packages=find_packages(),
platforms='any', platforms="any",
version=version, version=version,
zip_safe=False, zip_safe=False,
classifiers=[ classifiers=[

View file

@ -29,35 +29,35 @@
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = ['jandd.sphinxext.ip'] extensions = ["jandd.sphinxext.ip"]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ["_templates"]
# The suffix(es) of source filenames. # The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string: # You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md'] # source_suffix = ['.rst', '.md']
source_suffix = '.rst' source_suffix = ".rst"
# The encoding of source files. # The encoding of source files.
# source_encoding = 'utf-8-sig' # source_encoding = 'utf-8-sig'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = "index"
# General information about the project. # General information about the project.
project = 'Sphinxext IP Tests' project = "Sphinxext IP Tests"
copyright = '2016, Jan Dittberner' copyright = "2016, Jan Dittberner"
author = 'Jan Dittberner' author = "Jan Dittberner"
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.1.0' version = "0.1.0"
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.1.0' release = "0.1.0"
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@ -75,7 +75,7 @@ language = None
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path # This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The reST default role (used for this markup: `text`) to use for all # The reST default role (used for this markup: `text`) to use for all
# documents. # documents.
@ -93,7 +93,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# show_authors = False # show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting. # A list of ignored prefixes for module index sorting.
# modindex_common_prefix = [] # modindex_common_prefix = []
@ -109,7 +109,7 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = 'alabaster' html_theme = "alabaster"
# Theme options are theme-specific and customize the look and feel of a theme # 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 # further. For a list of options available for each theme, see the
@ -138,7 +138,7 @@ html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static'] html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
@ -203,20 +203,17 @@ html_static_path = ['_static']
# html_search_scorer = 'scorer.js' # html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'SphinxextIPTestsdoc' htmlhelp_basename = "SphinxextIPTestsdoc"
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper', #'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt', #'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
#'preamble': '', #'preamble': '',
# Latex figure (float) alignment # Latex figure (float) alignment
#'figure_align': 'htbp', #'figure_align': 'htbp',
} }
@ -225,8 +222,13 @@ latex_elements = {
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'SphinxextIPTests.tex', 'Sphinxext IP Tests Documentation', (
'Jan Dittberner', 'manual'), master_doc,
"SphinxextIPTests.tex",
"Sphinxext IP Tests Documentation",
"Jan Dittberner",
"manual",
),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -255,8 +257,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
(master_doc, 'sphinxextiptests', 'Sphinxext IP Tests Documentation', (master_doc, "sphinxextiptests", "Sphinxext IP Tests Documentation", [author], 1)
[author], 1)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@ -269,9 +270,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'SphinxextIPTests', 'Sphinxext IP Tests Documentation', (
author, 'SphinxextIPTests', 'One line description of project.', master_doc,
'Miscellaneous'), "SphinxextIPTests",
"Sphinxext IP Tests Documentation",
author,
"SphinxextIPTests",
"One line description of project.",
"Miscellaneous",
),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.

View file

@ -11,23 +11,25 @@ This script runs the jandd.sphinxext.ip unit test suite.
""" """
import sys import sys
from os import path
import unittest import unittest
from os import path
def run(extra_args=[]): def run(extra_args=[]):
sys.path.insert(0, path.join(path.dirname(__file__), path.pardir)) sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
sys.path.insert(1, path.abspath( sys.path.insert(
path.join(path.dirname(__file__), path.pardir, 1,
'jandd', 'sphinxext', 'ip' path.abspath(
)) path.join(path.dirname(__file__), path.pardir, "jandd", "sphinxext", "ip")
),
) )
try: try:
import sphinx import sphinx
except ImportError: except ImportError:
print("The sphinx package is needed to run the jandd.sphinxext.ip " print(
"test suite.") "The sphinx package is needed to run the jandd.sphinxext.ip " "test suite."
)
from .test_ip import TestIPExtension from .test_ip import TestIPExtension
@ -37,5 +39,5 @@ def run(extra_args=[]):
unittest.TextTestRunner(verbosity=2).run(suite) unittest.TextTestRunner(verbosity=2).run(suite)
if __name__ == '__main__': if __name__ == "__main__":
run() run()

View file

@ -1,40 +1,41 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from io import StringIO
from .util import SphinxTestApplication, test_root
import unittest import unittest
from io import StringIO
from .util import SphinxTestApplication, test_root
IP4_ADDRESSES = ['127.0.0.1', '192.168.0.1'] IP4_ADDRESSES = ["127.0.0.1", "192.168.0.1"]
IP6_ADDRESSES = ['::1', '2001:dead:beef::1'] IP6_ADDRESSES = ["::1", "2001:dead:beef::1"]
IP4_RANGES = ['172.16.0.0/24', '192.168.0.0/24'] IP4_RANGES = ["172.16.0.0/24", "192.168.0.0/24"]
IP6_RANGES = ['2001:dead:beef::/64', '2001:dada:b001::/64'] IP6_RANGES = ["2001:dead:beef::/64", "2001:dada:b001::/64"]
class TestIPExtension(unittest.TestCase): class TestIPExtension(unittest.TestCase):
def setUp(self): def setUp(self):
if not (test_root / '_static').exists(): if not (test_root / "_static").exists():
(test_root / '_static').mkdir() (test_root / "_static").mkdir()
self.feed_warnfile = StringIO() self.feed_warnfile = StringIO()
self.app = SphinxTestApplication( self.app = SphinxTestApplication(
buildername='html', warning=self.feed_warnfile, cleanenv=True) buildername="html", warning=self.feed_warnfile, cleanenv=True
)
self.app.build(force_all=True, filenames=[]) self.app.build(force_all=True, filenames=[])
def tearDown(self): def tearDown(self):
self.app.cleanup() self.app.cleanup()
(test_root / '_build').rmtree(True) (test_root / "_build").rmtree(True)
def test_ip_domaindata(self): def test_ip_domaindata(self):
self.assertIn('ip', self.app.env.domaindata) self.assertIn("ip", self.app.env.domaindata)
ipdomdata = self.app.env.domaindata['ip'] ipdomdata = self.app.env.domaindata["ip"]
self.assertIn('v4', ipdomdata) self.assertIn("v4", ipdomdata)
self.assertIn('v6', ipdomdata) self.assertIn("v6", ipdomdata)
self.assertIn('v4range', ipdomdata) self.assertIn("v4range", ipdomdata)
self.assertIn('v6range', ipdomdata) self.assertIn("v6range", ipdomdata)
self.assertIn('ips', ipdomdata) self.assertIn("ips", ipdomdata)
def find_in_index(self, entry): def find_in_index(self, entry):
indexentries = self.app.env.get_domain('index').entries indexentries = self.app.env.get_domain("index").entries
for index in indexentries: for index in indexentries:
for value in indexentries[index]: for value in indexentries[index]:
if value[1] == entry: if value[1] == entry:
@ -42,19 +43,19 @@ class TestIPExtension(unittest.TestCase):
self.fail("%s not found in index" % entry) self.fail("%s not found in index" % entry)
def test_ip4_addresses(self): def test_ip4_addresses(self):
ipv4 = self.app.env.domaindata['ip']['v4'] ipv4 = self.app.env.domaindata["ip"]["v4"]
ips = self.app.env.domaindata['ip']['ips'] ips = self.app.env.domaindata["ip"]["ips"]
for ip in IP4_ADDRESSES: for ip in IP4_ADDRESSES:
self.assertIn(ip, ipv4) self.assertIn(ip, ipv4)
self.assertIn(ip, [item['ip'] for item in ips]) self.assertIn(ip, [item["ip"] for item in ips])
self.find_in_index("IPv4 address; %s" % ip) self.find_in_index("IPv4 address; %s" % ip)
self.find_in_index("%s; Test page 2" % ip) self.find_in_index("%s; Test page 2" % ip)
def test_ip6_addresses(self): def test_ip6_addresses(self):
ipv6 = self.app.env.domaindata['ip']['v6'] ipv6 = self.app.env.domaindata["ip"]["v6"]
ips = self.app.env.domaindata['ip']['ips'] ips = self.app.env.domaindata["ip"]["ips"]
for ip in IP6_ADDRESSES: for ip in IP6_ADDRESSES:
self.assertIn(ip, ipv6) self.assertIn(ip, ipv6)
self.assertIn(ip, [item['ip'] for item in ips]) self.assertIn(ip, [item["ip"] for item in ips])
self.find_in_index("IPv6 address; %s" % ip) self.find_in_index("IPv6 address; %s" % ip)
self.find_in_index("%s; Test page 2" % ip) self.find_in_index("%s; Test page 2" % ip)

View file

@ -17,15 +17,22 @@ from path import Path
from sphinx import application from sphinx import application
__all__ = [ __all__ = [
'test_root', "test_root",
'raises', 'raises_msg', 'Struct', "raises",
'ListOutput', 'SphinxTestApplication', 'with_app', 'gen_with_app', "raises_msg",
'Path', 'with_tempdir', 'write_file', "Struct",
'sprint', "ListOutput",
"SphinxTestApplication",
"with_app",
"gen_with_app",
"Path",
"with_tempdir",
"write_file",
"sprint",
] ]
test_root = Path(__file__).parent.joinpath('root').abspath() test_root = Path(__file__).parent.joinpath("root").abspath()
def _excstr(exc): def _excstr(exc):
@ -44,8 +51,7 @@ def raises(exc, func, *args, **kwds):
except exc: except exc:
pass pass
else: else:
raise AssertionError('%s did not raise %s' % raise AssertionError("%s did not raise %s" % (func.__name__, _excstr(exc)))
(func.__name__, _excstr(exc)))
def raises_msg(exc, msg, func, *args, **kwds): def raises_msg(exc, msg, func, *args, **kwds):
@ -56,10 +62,9 @@ def raises_msg(exc, msg, func, *args, **kwds):
try: try:
func(*args, **kwds) func(*args, **kwds)
except exc as err: except exc as err:
assert msg in str(err), "\"%s\" not in \"%s\"" % (msg, err) assert msg in str(err), '"%s" not in "%s"' % (msg, err)
else: else:
raise AssertionError('%s did not raise %s' % raise AssertionError("%s did not raise %s" % (func.__name__, _excstr(exc)))
(func.__name__, _excstr(exc)))
class Struct(object): class Struct(object):
@ -71,6 +76,7 @@ class ListOutput(object):
""" """
File-like object that collects written text in a list. File-like object that collects written text in a list.
""" """
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self.content = [] self.content = []
@ -88,27 +94,38 @@ class SphinxTestApplication(application.Sphinx):
better default values for the initialization parameters. better default values for the initialization parameters.
""" """
def __init__(self, srcdir=None, confdir=None, outdir=None, doctreedir=None, def __init__(
buildername='html', confoverrides=None, self,
status=None, warning=None, freshenv=None, srcdir=None,
warningiserror=None, tags=None, confdir=None,
confname='conf.py', cleanenv=False): outdir=None,
doctreedir=None,
buildername="html",
confoverrides=None,
status=None,
warning=None,
freshenv=None,
warningiserror=None,
tags=None,
confname="conf.py",
cleanenv=False,
):
application.CONFIG_FILENAME = confname application.CONFIG_FILENAME = confname
self.cleanup_trees = [test_root / 'generated'] self.cleanup_trees = [test_root / "generated"]
if srcdir is None: if srcdir is None:
srcdir = test_root srcdir = test_root
if srcdir == '(temp)': if srcdir == "(temp)":
tempdir = Path(tempfile.mkdtemp()) tempdir = Path(tempfile.mkdtemp())
self.cleanup_trees.append(tempdir) self.cleanup_trees.append(tempdir)
temproot = tempdir / 'root' temproot = tempdir / "root"
test_root.copytree(temproot) test_root.copytree(temproot)
srcdir = temproot srcdir = temproot
else: else:
srcdir = Path(srcdir) srcdir = Path(srcdir)
self.builddir = srcdir.joinpath('_build') self.builddir = srcdir.joinpath("_build")
if confdir is None: if confdir is None:
confdir = srcdir confdir = srcdir
if outdir is None: if outdir is None:
@ -117,7 +134,7 @@ class SphinxTestApplication(application.Sphinx):
outdir.makedirs() outdir.makedirs()
self.cleanup_trees.insert(0, outdir) self.cleanup_trees.insert(0, outdir)
if doctreedir is None: if doctreedir is None:
doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees') doctreedir = srcdir.joinpath(srcdir, self.builddir, "doctrees")
if cleanenv: if cleanenv:
self.cleanup_trees.insert(0, doctreedir) self.cleanup_trees.insert(0, doctreedir)
if confoverrides is None: if confoverrides is None:
@ -125,15 +142,26 @@ class SphinxTestApplication(application.Sphinx):
if status is None: if status is None:
status = io.StringIO() status = io.StringIO()
if warning is None: if warning is None:
warning = ListOutput('stderr') warning = ListOutput("stderr")
if freshenv is None: if freshenv is None:
freshenv = False freshenv = False
if warningiserror is None: if warningiserror is None:
warningiserror = False warningiserror = False
application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir, application.Sphinx.__init__(
buildername, confoverrides, status, warning, self,
freshenv, warningiserror, tags) srcdir,
confdir,
outdir,
doctreedir,
buildername,
confoverrides,
status,
warning,
freshenv,
warningiserror,
tags,
)
def cleanup(self, doctrees=False): def cleanup(self, doctrees=False):
for tree in self.cleanup_trees: for tree in self.cleanup_trees:
@ -145,6 +173,7 @@ def with_app(*args, **kwargs):
Make a TestApp with args and kwargs, pass it to the test and clean up Make a TestApp with args and kwargs, pass it to the test and clean up
properly. properly.
""" """
def generator(func): def generator(func):
@wraps(func) @wraps(func)
def deco(*args2, **kwargs2): def deco(*args2, **kwargs2):
@ -152,7 +181,9 @@ def with_app(*args, **kwargs):
func(app, *args2, **kwargs2) func(app, *args2, **kwargs2)
# don't execute cleanup if test failed # don't execute cleanup if test failed
app.cleanup() app.cleanup()
return deco return deco
return generator return generator
@ -161,6 +192,7 @@ def gen_with_app(*args, **kwargs):
Make a TestApp with args and kwargs, pass it to the test and clean up Make a TestApp with args and kwargs, pass it to the test and clean up
properly. properly.
""" """
def generator(func): def generator(func):
@wraps(func) @wraps(func)
def deco(*args2, **kwargs2): def deco(*args2, **kwargs2):
@ -169,7 +201,9 @@ def gen_with_app(*args, **kwargs):
yield item yield item
# don't execute cleanup if test failed # don't execute cleanup if test failed
app.cleanup() app.cleanup()
return deco return deco
return generator return generator
@ -178,15 +212,16 @@ def with_tempdir(func):
tempdir = Path(tempfile.mkdtemp()) tempdir = Path(tempfile.mkdtemp())
func(tempdir) func(tempdir)
tempdir.rmtree() tempdir.rmtree()
new_func.__name__ = func.__name__ new_func.__name__ = func.__name__
return new_func return new_func
def write_file(name, contents): def write_file(name, contents):
f = open(str(name), 'wb') f = open(str(name), "wb")
f.write(contents) f.write(contents)
f.close() f.close()
def sprint(*args): def sprint(*args):
sys.stderr.write(' '.join(map(str, args)) + '\n') sys.stderr.write(" ".join(map(str, args)) + "\n")