# -*- coding: utf-8 -*- """ jandd.sphinxext.ip ~~~~~~~~~~~~~~~~~~ The IP domain. :copyright: Copyright (c) 2016 Jan Dittberner :license: GPLv3+, see COPYING file for details. """ __version__ = '0.1.0' import re from docutils import nodes from sphinx import addnodes from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType from sphinx.locale import l_ from sphinx.roles import XRefRole from sphinx.util.nodes import make_refnode def ip_object_anchor(typ, path): path = re.sub(r'[.:/]', '-', path) return typ.lower() + '-' + path class IPXRefRole(XRefRole): """ Cross referencing role for the IP domain. """ def __init__(self, method, **kwargs): super(IPXRefRole, self).__init__(**kwargs) self.method = method def process_link(self, env, refnode, has_explicit_title, title, target): return title, target def result_nodes(self, document, env, node, is_ref): indexnode = addnodes.index() targetid = 'index-%s' % env.new_serialno('index') targetnode = nodes.target('', '', ids=[targetid]) idxtext = "%s; %s" % (node.astext(), env.docname) indexnode['entries'] = [('single', idxtext, targetid, '', None)] return [indexnode, targetnode, node], [] class IPObject(ObjectDescription): typ = NotImplemented typelabel = NotImplemented required_arguments = 1 def handle_signature(self, sig, signode): ipaddress = sig signode['typ'] = self.typ signode['path'] = sig signode.append(nodes.inline(self.typelabel, self.typelabel)) signode.append(nodes.literal(sig, sig)) return (ipaddress, self.typ, sig) def needs_arglist(self): return False def add_target_and_index(self, name, sig, signode): sigid = ip_object_anchor(*name[1:]) if sigid not in self.state.document.ids: signode['names'].append(name[0]) signode['ids'].append(sigid) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) objects = self.env.domaindata['ip'][self.typ] if sigid in objects: self.state_machine.reporter.warning( 'duplicate object description of %s, ' % sigid + 'other instance in ' + self.env.doc2path(objects[sigid][0]) + ', use: noindex: for one of them', line=self.lineno ) objects[sigid] = ( self.env.docname, self.options.get('synopsis', '')) idxtext = self.get_index_text(name) if idxtext: self.indexnode['entries'].append( ('single', idxtext, sigid, '') ) def get_index_text(self, name): idxname = name[0] return idxname class IPv4Address(IPObject): typ = 'v4' typelabel = "IPv4 address " class IPv4Range(IPObject): typ = 'v4range' typelabel = "IPv4 range " class IPv6Address(IPObject): typ = 'v6' typelabel = "IPv6 address " class IPv6Range(IPObject): typ = 'v6range' typelabel = "IPv6 range " class IPDomain(Domain): """ IP address and range domain. """ name = 'ip' label = 'IP addresses and ranges.' object_types = { 'v4': ObjType(l_('v4'), 'v4'), 'v6': ObjType(l_('v6'), 'v6'), 'v4range': ObjType(l_('v4range'), 'v4range'), 'v6range': ObjType(l_('v6range'), 'v6range'), } directives = { 'v4': IPv4Address, 'v6': IPv6Address, 'v4range': IPv4Range, 'v6range': IPv6Range, } roles = { 'v4': IPXRefRole('ip'), 'v6': IPXRefRole('ip'), 'v4range': IPXRefRole('range'), 'v6range': IPXRefRole('range'), } initial_data = { 'v4': {}, 'v6': {}, 'v4range': {}, 'v6range': {} } def clear_doc(self, docname): pass def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): key = ip_object_anchor(typ, target) try: info = self.data[typ][key] except KeyError: text = contnode.rawsource role = self.roles.get(typ) if role is None: return None resnode = role.result_nodes(env.get_doctree(fromdocname), env, node, None)[0][0] if isinstance(resnode, addnodes.pending_xref): text = node[0][0] reporter = env.get_doctree(fromdocname).reporter reporter.warning('Cannot resolve reference to %r' % text, line=node.line) return None return resnode else: title = typ.upper() + ' ' + target anchor = ip_object_anchor(typ, target) return make_refnode(builder, fromdocname, info[0], anchor, contnode, title) @property def items(self): return dict((key, self.data[key]) for key in self.object_types) def get_objects(self): for typ, items in self.items.items(): for path, info in items.items(): anchor = ip_object_anchor(typ, path) yield (path, path, typ, info[0], anchor, 1) def setup(app): app.add_domain(IPDomain) return {'version': __version__}