Initial version of the IP Sphinx extension
This commit is contained in:
		
						commit
						0012b495b3
					
				
					 7 changed files with 916 additions and 0 deletions
				
			
		
							
								
								
									
										196
									
								
								jandd/sphinxext/ip/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								jandd/sphinxext/ip/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,196 @@ | |||
| # -*- 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__} | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue