1010
1111import pbr .version
1212import sphinx .addnodes as SphinxNodes
13- from docutils .nodes import Text , emphasis , inline
1413from sphinx .transforms import SphinxTransform
14+ from docutils .nodes import Text , emphasis , field , field_name , field_body , inline , pending
1515
1616if False :
1717 # For type annotations
1818 from typing import Any , Dict # noqa
19+ from docutils .nodes import Node # noqa
1920 from sphinx .application import Sphinx # noqa
2021
2122__version__ = pbr .version .VersionInfo (
@@ -41,11 +42,11 @@ def patch_node(node, text=None, children=None, *, constructor=None):
4142
4243
4344def function_transformer (new_name ):
44- def xf (name_node , parameters_node , selfref ):
45+ def xf (name_node , parameters_node , self_param ):
4546 return (
4647 patch_node (name_node , new_name , ()),
4748 patch_node (parameters_node , '' , [
48- SphinxNodes .desc_parameter ('' , selfref ),
49+ SphinxNodes .desc_parameter ('' , self_param ),
4950 * parameters_node .children ,
5051 ])
5152 )
@@ -54,20 +55,20 @@ def xf(name_node, parameters_node, selfref):
5455
5556
5657def unary_op_transformer (op ):
57- def xf (name_node , parameters_node , selfref ):
58+ def xf (name_node , parameters_node , self_param ):
5859 return (
5960 patch_node (name_node , op , ()),
60- emphasis ('' , selfref ),
61+ emphasis ('' , self_param ),
6162 )
6263
6364 return xf
6465
6566
6667def binary_op_transformer (op ):
67- def xf (name_node , parameters_node , selfref ):
68+ def xf (name_node , parameters_node , self_param ):
6869 return inline (
6970 '' , '' ,
70- emphasis ('' , selfref ),
71+ emphasis ('' , self_param ),
7172 Text (' ' ),
7273 patch_node (name_node , op , ()),
7374 Text (' ' ),
@@ -77,22 +78,22 @@ def xf(name_node, parameters_node, selfref):
7778 return xf
7879
7980
80- def brackets (parameters_node , selfref ):
81+ def brackets (parameters_node , self_param ):
8182 return [
82- emphasis ('' , selfref ),
83+ emphasis ('' , self_param ),
8384 SphinxNodes .desc_name ('' , '' , Text ('[' )),
8485 emphasis ('' , parameters_node .children [0 ].astext ()),
8586 SphinxNodes .desc_name ('' , '' , Text (']' )),
8687 ]
8788
8889
8990SPECIAL_METHODS = {
90- '__getitem__' : lambda name_node , parameters_node , selfref : inline (
91- '' , '' , * brackets (parameters_node , selfref )
91+ '__getitem__' : lambda name_node , parameters_node , self_param : inline (
92+ '' , '' , * brackets (parameters_node , self_param )
9293 ),
93- '__setitem__' : lambda name_node , parameters_node , selfref : inline (
94+ '__setitem__' : lambda name_node , parameters_node , self_param : inline (
9495 '' , '' ,
95- * brackets (parameters_node , selfref ),
96+ * brackets (parameters_node , self_param ),
9697 Text (' ' ),
9798 SphinxNodes .desc_name ('' , '' , Text ('=' )),
9899 Text (' ' ),
@@ -101,26 +102,26 @@ def brackets(parameters_node, selfref):
101102 if len (parameters_node .children ) > 1 else ''
102103 )),
103104 ),
104- '__delitem__' : lambda name_node , parameters_node , selfref : inline (
105+ '__delitem__' : lambda name_node , parameters_node , self_param : inline (
105106 '' , '' ,
106107 SphinxNodes .desc_name ('' , '' , Text ('del' )),
107108 Text (' ' ),
108- * brackets (parameters_node , selfref ),
109+ * brackets (parameters_node , self_param ),
109110 ),
110- '__contains__' : lambda name_node , parameters_node , selfref : inline (
111+ '__contains__' : lambda name_node , parameters_node , self_param : inline (
111112 '' , '' ,
112113 emphasis ('' , parameters_node .children [0 ].astext ()),
113114 Text (' ' ),
114115 SphinxNodes .desc_name ('' , '' , Text ('in' )),
115116 Text (' ' ),
116- emphasis ('' , selfref ),
117+ emphasis ('' , self_param ),
117118 ),
118119
119- '__await__' : lambda name_node , parameters_node , selfref : inline (
120+ '__await__' : lambda name_node , parameters_node , self_param : inline (
120121 '' , '' ,
121122 SphinxNodes .desc_name ('' , '' , Text ('await' )),
122123 Text (' ' ),
123- emphasis ('' , selfref ),
124+ emphasis ('' , self_param ),
124125 ),
125126
126127 '__lt__' : binary_op_transformer ('<' ),
@@ -156,8 +157,8 @@ def brackets(parameters_node, selfref):
156157 '__abs__' : function_transformer ('abs' ),
157158 '__invert__' : unary_op_transformer ('~' ),
158159
159- '__call__' : lambda name_node , parameters_node , selfref : (
160- emphasis ('' , selfref ),
160+ '__call__' : lambda name_node , parameters_node , self_param : (
161+ emphasis ('' , self_param ),
161162 patch_node (parameters_node , '' , parameters_node .children )
162163 ),
163164 '__getattr__' : function_transformer ('getattr' ),
@@ -186,12 +187,51 @@ def brackets(parameters_node, selfref):
186187}
187188
188189
190+ class PendingSelfParamName (pending ):
191+ def __init__ (self , name ):
192+ # type(str)
193+ super ().__init__ (
194+ transform = PrettifySpecialMethods ,
195+ details = {'self_param' : name },
196+ )
197+
198+ @property
199+ def name (self ):
200+ # type() -> str
201+ return self .details ['self_param' ]
202+
203+
204+ def is_meta_self_param_info_field (node ):
205+ # type: (Node)
206+ if not isinstance (node , field ):
207+ return False
208+
209+ name = node .next_node (field_name ).astext ()
210+ return name == 'meta self-param'
211+
212+
213+ def convert_meta_self_param (app , domain , objtype , contentnode ):
214+ # type: (Sphinx, str, str, Node) -> Dict[str, Any]
215+ if not domain == 'py' or 'method' not in objtype :
216+ return
217+
218+ # Note: Using next_node means we only find the first instance
219+ # of selfparam. Additional selfparam fields are ignored and eventually
220+ # deleted by the Python domain's meta filter.
221+ selfparam_field = contentnode .next_node (is_meta_self_param_info_field )
222+
223+ if selfparam_field :
224+ selfparam : str = selfparam_field .next_node (field_body ).astext ()
225+ contentnode .append (PendingSelfParamName (selfparam ))
226+ selfparam_field .replace_self (())
227+
228+
189229class PrettifySpecialMethods (SphinxTransform ):
190230 default_priority = 800
191231
192232 def apply (self ):
193233 methods = (
194- sig for sig in self .document .traverse (SphinxNodes .desc_signature )
234+ sig . parent for sig in self .document .traverse (SphinxNodes .desc_signature )
195235 if 'class' in sig
196236 )
197237
@@ -200,11 +240,22 @@ def apply(self):
200240 method_name = name_node .astext ()
201241
202242 if method_name in SPECIAL_METHODS :
243+ # Determine name to use for self in new specification
244+ # using first child occurence
245+ pending_self_param = ref .next_node (PendingSelfParamName )
246+ self_param = pending_self_param .name if pending_self_param else 'self'
247+
203248 parameters_node = ref .next_node (SphinxNodes .desc_parameterlist )
204249
205- name_node .replace_self (SPECIAL_METHODS [method_name ](name_node , parameters_node , 'self' ))
250+ new_sig = SPECIAL_METHODS [method_name ](name_node , parameters_node , self_param )
251+
252+ name_node .replace_self (new_sig )
206253 parameters_node .replace_self (())
207254
255+ # Remove ALL occurrences of PendingSelfParamName
256+ for p in self .document .traverse (PendingSelfParamName ):
257+ p .replace_self (())
258+
208259
209260def show_special_methods (app , what , name , obj , skip , options ):
210261 if what == 'class' and name in SPECIAL_METHODS and getattr (obj , '__doc__' , None ):
@@ -214,6 +265,7 @@ def show_special_methods(app, what, name, obj, skip, options):
214265def setup (app ):
215266 # type: (Sphinx) -> Dict[str, Any]
216267 app .add_transform (PrettifySpecialMethods )
268+ app .connect ('object-description-transform' , convert_meta_self_param , priority = 450 )
217269 app .setup_extension ('sphinx.ext.autodoc' )
218270 app .connect ('autodoc-skip-member' , show_special_methods )
219271 return {'version' : __version__ , 'parallel_read_safe' : True }
0 commit comments