mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Enable automatic formatting for `sphinx/domains/python/
`
This commit is contained in:
parent
917b74ab43
commit
e48a88d05f
@ -394,8 +394,5 @@ preview = true
|
|||||||
quote-style = "single"
|
quote-style = "single"
|
||||||
exclude = [
|
exclude = [
|
||||||
"sphinx/builders/latex/constants.py",
|
"sphinx/builders/latex/constants.py",
|
||||||
"sphinx/domains/python/_annotations.py",
|
|
||||||
"sphinx/domains/python/__init__.py",
|
|
||||||
"sphinx/domains/python/_object.py",
|
|
||||||
"sphinx/domains/std/__init__.py",
|
"sphinx/domains/std/__init__.py",
|
||||||
]
|
]
|
||||||
|
@ -87,16 +87,19 @@ class PyFunction(PyObject):
|
|||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
|
def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
|
||||||
if 'async' in self.options:
|
if 'async' in self.options:
|
||||||
return [addnodes.desc_sig_keyword('', 'async'),
|
return [
|
||||||
addnodes.desc_sig_space()]
|
addnodes.desc_sig_keyword('', 'async'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def needs_arglist(self) -> bool:
|
def needs_arglist(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def add_target_and_index(self, name_cls: tuple[str, str], sig: str,
|
def add_target_and_index(
|
||||||
signode: desc_signature) -> None:
|
self, name_cls: tuple[str, str], sig: str, signode: desc_signature
|
||||||
|
) -> None:
|
||||||
super().add_target_and_index(name_cls, sig, signode)
|
super().add_target_and_index(name_cls, sig, signode)
|
||||||
if 'no-index-entry' not in self.options:
|
if 'no-index-entry' not in self.options:
|
||||||
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
||||||
@ -147,17 +150,24 @@ class PyVariable(PyObject):
|
|||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
annotations = _parse_annotation(typ, self.env)
|
annotations = _parse_annotation(typ, self.env)
|
||||||
signode += addnodes.desc_annotation(typ, '',
|
signode += addnodes.desc_annotation(
|
||||||
addnodes.desc_sig_punctuation('', ':'),
|
typ,
|
||||||
addnodes.desc_sig_space(), *annotations)
|
'',
|
||||||
|
addnodes.desc_sig_punctuation('', ':'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
*annotations,
|
||||||
|
)
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
signode += addnodes.desc_annotation(value, '',
|
signode += addnodes.desc_annotation(
|
||||||
addnodes.desc_sig_space(),
|
value,
|
||||||
addnodes.desc_sig_punctuation('', '='),
|
'',
|
||||||
addnodes.desc_sig_space(),
|
addnodes.desc_sig_space(),
|
||||||
nodes.Text(value))
|
addnodes.desc_sig_punctuation('', '='),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(value),
|
||||||
|
)
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@ -183,8 +193,12 @@ class PyClasslike(PyObject):
|
|||||||
|
|
||||||
def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
|
def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
|
||||||
if 'final' in self.options:
|
if 'final' in self.options:
|
||||||
return [nodes.Text('final'), addnodes.desc_sig_space(),
|
return [
|
||||||
nodes.Text(self.objtype), addnodes.desc_sig_space()]
|
nodes.Text('final'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(self.objtype),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
return [nodes.Text(self.objtype), addnodes.desc_sig_space()]
|
return [nodes.Text(self.objtype), addnodes.desc_sig_space()]
|
||||||
|
|
||||||
@ -308,18 +322,24 @@ class PyAttribute(PyObject):
|
|||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
annotations = _parse_annotation(typ, self.env)
|
annotations = _parse_annotation(typ, self.env)
|
||||||
signode += addnodes.desc_annotation(typ, '',
|
signode += addnodes.desc_annotation(
|
||||||
addnodes.desc_sig_punctuation('', ':'),
|
typ,
|
||||||
addnodes.desc_sig_space(),
|
'',
|
||||||
*annotations)
|
addnodes.desc_sig_punctuation('', ':'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
*annotations,
|
||||||
|
)
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
signode += addnodes.desc_annotation(value, '',
|
signode += addnodes.desc_annotation(
|
||||||
addnodes.desc_sig_space(),
|
value,
|
||||||
addnodes.desc_sig_punctuation('', '='),
|
'',
|
||||||
addnodes.desc_sig_space(),
|
addnodes.desc_sig_space(),
|
||||||
nodes.Text(value))
|
addnodes.desc_sig_punctuation('', '='),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
nodes.Text(value),
|
||||||
|
)
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@ -354,10 +374,13 @@ class PyProperty(PyObject):
|
|||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
annotations = _parse_annotation(typ, self.env)
|
annotations = _parse_annotation(typ, self.env)
|
||||||
signode += addnodes.desc_annotation(typ, '',
|
signode += addnodes.desc_annotation(
|
||||||
addnodes.desc_sig_punctuation('', ':'),
|
typ,
|
||||||
addnodes.desc_sig_space(),
|
'',
|
||||||
*annotations)
|
addnodes.desc_sig_punctuation('', ':'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
*annotations,
|
||||||
|
)
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@ -405,7 +428,8 @@ class PyTypeAlias(PyObject):
|
|||||||
if canonical := self.options.get('canonical'):
|
if canonical := self.options.get('canonical'):
|
||||||
canonical_annotations = _parse_annotation(canonical, self.env)
|
canonical_annotations = _parse_annotation(canonical, self.env)
|
||||||
signode += addnodes.desc_annotation(
|
signode += addnodes.desc_annotation(
|
||||||
canonical, '',
|
canonical,
|
||||||
|
'',
|
||||||
addnodes.desc_sig_space(),
|
addnodes.desc_sig_space(),
|
||||||
addnodes.desc_sig_punctuation('', '='),
|
addnodes.desc_sig_punctuation('', '='),
|
||||||
addnodes.desc_sig_space(),
|
addnodes.desc_sig_space(),
|
||||||
@ -513,12 +537,18 @@ class PyCurrentModule(SphinxDirective):
|
|||||||
|
|
||||||
|
|
||||||
class PyXRefRole(XRefRole):
|
class PyXRefRole(XRefRole):
|
||||||
def process_link(self, env: BuildEnvironment, refnode: Element,
|
def process_link(
|
||||||
has_explicit_title: bool, title: str, target: str) -> tuple[str, str]:
|
self,
|
||||||
|
env: BuildEnvironment,
|
||||||
|
refnode: Element,
|
||||||
|
has_explicit_title: bool,
|
||||||
|
title: str,
|
||||||
|
target: str,
|
||||||
|
) -> tuple[str, str]:
|
||||||
refnode['py:module'] = env.ref_context.get('py:module')
|
refnode['py:module'] = env.ref_context.get('py:module')
|
||||||
refnode['py:class'] = env.ref_context.get('py:class')
|
refnode['py:class'] = env.ref_context.get('py:class')
|
||||||
if not has_explicit_title:
|
if not has_explicit_title:
|
||||||
title = title.lstrip('.') # only has a meaning for the target
|
title = title.lstrip('.') # only has a meaning for the target
|
||||||
target = target.lstrip('~') # only has a meaning for the title
|
target = target.lstrip('~') # only has a meaning for the title
|
||||||
# if the first character is a tilde, don't display the module/class
|
# if the first character is a tilde, don't display the module/class
|
||||||
# parts of the contents
|
# parts of the contents
|
||||||
@ -526,7 +556,7 @@ class PyXRefRole(XRefRole):
|
|||||||
title = title[1:]
|
title = title[1:]
|
||||||
dot = title.rfind('.')
|
dot = title.rfind('.')
|
||||||
if dot != -1:
|
if dot != -1:
|
||||||
title = title[dot + 1:]
|
title = title[dot + 1 :]
|
||||||
# if the first character is a dot, search more specific namespaces first
|
# if the first character is a dot, search more specific namespaces first
|
||||||
# else search builtins first
|
# else search builtins first
|
||||||
if target[0:1] == '.':
|
if target[0:1] == '.':
|
||||||
@ -535,7 +565,9 @@ class PyXRefRole(XRefRole):
|
|||||||
return title, target
|
return title, target
|
||||||
|
|
||||||
|
|
||||||
def filter_meta_fields(app: Sphinx, domain: str, objtype: str, content: Element) -> None:
|
def filter_meta_fields(
|
||||||
|
app: Sphinx, domain: str, objtype: str, content: Element
|
||||||
|
) -> None:
|
||||||
"""Filter ``:meta:`` field from its docstring."""
|
"""Filter ``:meta:`` field from its docstring."""
|
||||||
if domain != 'py':
|
if domain != 'py':
|
||||||
return
|
return
|
||||||
@ -560,8 +592,9 @@ class PythonModuleIndex(Index):
|
|||||||
shortname = _('modules')
|
shortname = _('modules')
|
||||||
domain: PythonDomain
|
domain: PythonDomain
|
||||||
|
|
||||||
def generate(self, docnames: Iterable[str] | None = None,
|
def generate(
|
||||||
) -> tuple[list[tuple[str, list[IndexEntry]]], bool]:
|
self, docnames: Iterable[str] | None = None
|
||||||
|
) -> tuple[list[tuple[str, list[IndexEntry]]], bool]:
|
||||||
doc_names = frozenset(docnames) if docnames is not None else None
|
doc_names = frozenset(docnames) if docnames is not None else None
|
||||||
|
|
||||||
content: dict[str, list[IndexEntry]] = {}
|
content: dict[str, list[IndexEntry]] = {}
|
||||||
@ -657,46 +690,46 @@ class PythonDomain(Domain):
|
|||||||
name = 'py'
|
name = 'py'
|
||||||
label = 'Python'
|
label = 'Python'
|
||||||
object_types: dict[str, ObjType] = {
|
object_types: dict[str, ObjType] = {
|
||||||
'function': ObjType(_('function'), 'func', 'obj'),
|
'function': ObjType(_('function'), 'func', 'obj'),
|
||||||
'data': ObjType(_('data'), 'data', 'obj'),
|
'data': ObjType(_('data'), 'data', 'obj'),
|
||||||
'class': ObjType(_('class'), 'class', 'exc', 'obj'),
|
'class': ObjType(_('class'), 'class', 'exc', 'obj'),
|
||||||
'exception': ObjType(_('exception'), 'exc', 'class', 'obj'),
|
'exception': ObjType(_('exception'), 'exc', 'class', 'obj'),
|
||||||
'method': ObjType(_('method'), 'meth', 'obj'),
|
'method': ObjType(_('method'), 'meth', 'obj'),
|
||||||
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
|
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
|
||||||
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
|
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
|
||||||
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
|
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
|
||||||
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
|
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
|
||||||
'type': ObjType(_('type alias'), 'type', 'obj'),
|
'type': ObjType(_('type alias'), 'type', 'obj'),
|
||||||
'module': ObjType(_('module'), 'mod', 'obj'),
|
'module': ObjType(_('module'), 'mod', 'obj'),
|
||||||
}
|
}
|
||||||
|
|
||||||
directives = {
|
directives = {
|
||||||
'function': PyFunction,
|
'function': PyFunction,
|
||||||
'data': PyVariable,
|
'data': PyVariable,
|
||||||
'class': PyClasslike,
|
'class': PyClasslike,
|
||||||
'exception': PyClasslike,
|
'exception': PyClasslike,
|
||||||
'method': PyMethod,
|
'method': PyMethod,
|
||||||
'classmethod': PyClassMethod,
|
'classmethod': PyClassMethod,
|
||||||
'staticmethod': PyStaticMethod,
|
'staticmethod': PyStaticMethod,
|
||||||
'attribute': PyAttribute,
|
'attribute': PyAttribute,
|
||||||
'property': PyProperty,
|
'property': PyProperty,
|
||||||
'type': PyTypeAlias,
|
'type': PyTypeAlias,
|
||||||
'module': PyModule,
|
'module': PyModule,
|
||||||
'currentmodule': PyCurrentModule,
|
'currentmodule': PyCurrentModule,
|
||||||
'decorator': PyDecoratorFunction,
|
'decorator': PyDecoratorFunction,
|
||||||
'decoratormethod': PyDecoratorMethod,
|
'decoratormethod': PyDecoratorMethod,
|
||||||
}
|
}
|
||||||
roles = {
|
roles = {
|
||||||
'data': PyXRefRole(),
|
'data': PyXRefRole(),
|
||||||
'exc': PyXRefRole(),
|
'exc': PyXRefRole(),
|
||||||
'func': PyXRefRole(fix_parens=True),
|
'func': PyXRefRole(fix_parens=True),
|
||||||
'class': PyXRefRole(),
|
'class': PyXRefRole(),
|
||||||
'const': PyXRefRole(),
|
'const': PyXRefRole(),
|
||||||
'attr': PyXRefRole(),
|
'attr': PyXRefRole(),
|
||||||
'type': PyXRefRole(),
|
'type': PyXRefRole(),
|
||||||
'meth': PyXRefRole(fix_parens=True),
|
'meth': PyXRefRole(fix_parens=True),
|
||||||
'mod': PyXRefRole(),
|
'mod': PyXRefRole(),
|
||||||
'obj': PyXRefRole(),
|
'obj': PyXRefRole(),
|
||||||
}
|
}
|
||||||
initial_data: dict[str, dict[str, tuple[Any]]] = {
|
initial_data: dict[str, dict[str, tuple[Any]]] = {
|
||||||
'objects': {}, # fullname -> docname, objtype
|
'objects': {}, # fullname -> docname, objtype
|
||||||
@ -710,8 +743,14 @@ class PythonDomain(Domain):
|
|||||||
def objects(self) -> dict[str, ObjectEntry]:
|
def objects(self) -> dict[str, ObjectEntry]:
|
||||||
return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
|
return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
|
||||||
|
|
||||||
def note_object(self, name: str, objtype: str, node_id: str,
|
def note_object(
|
||||||
aliased: bool = False, location: Any = None) -> None:
|
self,
|
||||||
|
name: str,
|
||||||
|
objtype: str,
|
||||||
|
node_id: str,
|
||||||
|
aliased: bool = False,
|
||||||
|
location: Any = None,
|
||||||
|
) -> None:
|
||||||
"""Note a python object for cross reference.
|
"""Note a python object for cross reference.
|
||||||
|
|
||||||
.. versionadded:: 2.1
|
.. versionadded:: 2.1
|
||||||
@ -726,9 +765,15 @@ class PythonDomain(Domain):
|
|||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# duplicated
|
# duplicated
|
||||||
logger.warning(__('duplicate object description of %s, '
|
logger.warning(
|
||||||
'other instance in %s, use :no-index: for one of them'),
|
__(
|
||||||
name, other.docname, location=location)
|
'duplicate object description of %s, '
|
||||||
|
'other instance in %s, use :no-index: for one of them'
|
||||||
|
),
|
||||||
|
name,
|
||||||
|
other.docname,
|
||||||
|
location=location,
|
||||||
|
)
|
||||||
self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, aliased)
|
self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, aliased)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -771,9 +816,15 @@ class PythonDomain(Domain):
|
|||||||
if mod.docname in docnames:
|
if mod.docname in docnames:
|
||||||
self.modules[modname] = mod
|
self.modules[modname] = mod
|
||||||
|
|
||||||
def find_obj(self, env: BuildEnvironment, modname: str, classname: str,
|
def find_obj(
|
||||||
name: str, type: str | None, searchmode: int = 0,
|
self,
|
||||||
) -> list[tuple[str, ObjectEntry]]:
|
env: BuildEnvironment,
|
||||||
|
modname: str,
|
||||||
|
classname: str,
|
||||||
|
name: str,
|
||||||
|
type: str | None,
|
||||||
|
searchmode: int = 0,
|
||||||
|
) -> list[tuple[str, ObjectEntry]]:
|
||||||
"""Find a Python object for "name", perhaps using the given module
|
"""Find a Python object for "name", perhaps using the given module
|
||||||
and/or classname. Returns a list of (name, object entry) tuples.
|
and/or classname. Returns a list of (name, object entry) tuples.
|
||||||
"""
|
"""
|
||||||
@ -794,20 +845,31 @@ class PythonDomain(Domain):
|
|||||||
if objtypes is not None:
|
if objtypes is not None:
|
||||||
if modname and classname:
|
if modname and classname:
|
||||||
fullname = modname + '.' + classname + '.' + name
|
fullname = modname + '.' + classname + '.' + name
|
||||||
if fullname in self.objects and self.objects[fullname].objtype in objtypes:
|
if (
|
||||||
|
fullname in self.objects
|
||||||
|
and self.objects[fullname].objtype in objtypes
|
||||||
|
):
|
||||||
newname = fullname
|
newname = fullname
|
||||||
if not newname:
|
if not newname:
|
||||||
if modname and modname + '.' + name in self.objects and \
|
if (
|
||||||
self.objects[modname + '.' + name].objtype in objtypes:
|
modname
|
||||||
newname = modname + '.' + name
|
and f'{modname}.{name}' in self.objects
|
||||||
elif name in self.objects and self.objects[name].objtype in objtypes:
|
and self.objects[f'{modname}.{name}'].objtype in objtypes
|
||||||
|
):
|
||||||
|
newname = f'{modname}.{name}'
|
||||||
|
elif (
|
||||||
|
name in self.objects and self.objects[name].objtype in objtypes
|
||||||
|
):
|
||||||
newname = name
|
newname = name
|
||||||
else:
|
else:
|
||||||
# "fuzzy" searching mode
|
# "fuzzy" searching mode
|
||||||
searchname = '.' + name
|
searchname = f'.{name}'
|
||||||
matches = [(oname, self.objects[oname]) for oname in self.objects
|
matches = [
|
||||||
if oname.endswith(searchname) and
|
(oname, self.objects[oname])
|
||||||
self.objects[oname].objtype in objtypes]
|
for oname in self.objects
|
||||||
|
if oname.endswith(searchname)
|
||||||
|
and self.objects[oname].objtype in objtypes
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
# NOTE: searching for exact match, object type is not considered
|
# NOTE: searching for exact match, object type is not considered
|
||||||
if name in self.objects:
|
if name in self.objects:
|
||||||
@ -819,21 +881,30 @@ class PythonDomain(Domain):
|
|||||||
newname = classname + '.' + name
|
newname = classname + '.' + name
|
||||||
elif modname and modname + '.' + name in self.objects:
|
elif modname and modname + '.' + name in self.objects:
|
||||||
newname = modname + '.' + name
|
newname = modname + '.' + name
|
||||||
elif modname and classname and \
|
elif (
|
||||||
modname + '.' + classname + '.' + name in self.objects:
|
modname
|
||||||
|
and classname
|
||||||
|
and modname + '.' + classname + '.' + name in self.objects
|
||||||
|
):
|
||||||
newname = modname + '.' + classname + '.' + name
|
newname = modname + '.' + classname + '.' + name
|
||||||
if newname is not None:
|
if newname is not None:
|
||||||
matches.append((newname, self.objects[newname]))
|
matches.append((newname, self.objects[newname]))
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
def resolve_xref(
|
||||||
type: str, target: str, node: pending_xref, contnode: Element,
|
self,
|
||||||
) -> nodes.reference | None:
|
env: BuildEnvironment,
|
||||||
|
fromdocname: str,
|
||||||
|
builder: Builder,
|
||||||
|
type: str,
|
||||||
|
target: str,
|
||||||
|
node: pending_xref,
|
||||||
|
contnode: Element,
|
||||||
|
) -> nodes.reference | None:
|
||||||
modname = node.get('py:module')
|
modname = node.get('py:module')
|
||||||
clsname = node.get('py:class')
|
clsname = node.get('py:class')
|
||||||
searchmode = 1 if node.hasattr('refspecific') else 0
|
searchmode = 1 if node.hasattr('refspecific') else 0
|
||||||
matches = self.find_obj(env, modname, clsname, target,
|
matches = self.find_obj(env, modname, clsname, target, type, searchmode)
|
||||||
type, searchmode)
|
|
||||||
|
|
||||||
if not matches and type == 'attr':
|
if not matches and type == 'attr':
|
||||||
# fallback to meth (for property; Sphinx 2.4.x)
|
# fallback to meth (for property; Sphinx 2.4.x)
|
||||||
@ -855,9 +926,14 @@ class PythonDomain(Domain):
|
|||||||
if len(canonicals) == 1:
|
if len(canonicals) == 1:
|
||||||
matches = canonicals
|
matches = canonicals
|
||||||
else:
|
else:
|
||||||
logger.warning(__('more than one target found for cross-reference %r: %s'),
|
logger.warning(
|
||||||
target, ', '.join(match[0] for match in matches),
|
__('more than one target found for cross-reference %r: %s'),
|
||||||
type='ref', subtype='python', location=node)
|
target,
|
||||||
|
', '.join(match[0] for match in matches),
|
||||||
|
type='ref',
|
||||||
|
subtype='python',
|
||||||
|
location=node,
|
||||||
|
)
|
||||||
name, obj = matches[0]
|
name, obj = matches[0]
|
||||||
|
|
||||||
if obj[2] == 'module':
|
if obj[2] == 'module':
|
||||||
@ -873,9 +949,15 @@ class PythonDomain(Domain):
|
|||||||
|
|
||||||
return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)
|
return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)
|
||||||
|
|
||||||
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
def resolve_any_xref(
|
||||||
target: str, node: pending_xref, contnode: Element,
|
self,
|
||||||
) -> list[tuple[str, nodes.reference]]:
|
env: BuildEnvironment,
|
||||||
|
fromdocname: str,
|
||||||
|
builder: Builder,
|
||||||
|
target: str,
|
||||||
|
node: pending_xref,
|
||||||
|
contnode: Element,
|
||||||
|
) -> list[tuple[str, nodes.reference]]:
|
||||||
modname = node.get('py:module')
|
modname = node.get('py:module')
|
||||||
clsname = node.get('py:class')
|
clsname = node.get('py:class')
|
||||||
results: list[tuple[str, nodes.reference]] = []
|
results: list[tuple[str, nodes.reference]] = []
|
||||||
@ -885,7 +967,6 @@ class PythonDomain(Domain):
|
|||||||
multiple_matches = len(matches) > 1
|
multiple_matches = len(matches) > 1
|
||||||
|
|
||||||
for name, obj in matches:
|
for name, obj in matches:
|
||||||
|
|
||||||
if multiple_matches and obj.aliased:
|
if multiple_matches and obj.aliased:
|
||||||
# Skip duplicated matches
|
# Skip duplicated matches
|
||||||
continue
|
continue
|
||||||
@ -893,7 +974,7 @@ class PythonDomain(Domain):
|
|||||||
if obj[2] == 'module':
|
if obj[2] == 'module':
|
||||||
results.append((
|
results.append((
|
||||||
'py:mod',
|
'py:mod',
|
||||||
self._make_module_refnode(builder, fromdocname, name, contnode)
|
self._make_module_refnode(builder, fromdocname, name, contnode),
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
# determine the content of the reference by conditions
|
# determine the content of the reference by conditions
|
||||||
@ -905,12 +986,15 @@ class PythonDomain(Domain):
|
|||||||
children = [contnode]
|
children = [contnode]
|
||||||
|
|
||||||
role = 'py:' + self.role_for_objtype(obj[2]) # type: ignore[operator]
|
role = 'py:' + self.role_for_objtype(obj[2]) # type: ignore[operator]
|
||||||
results.append((role, make_refnode(builder, fromdocname, obj[0], obj[1],
|
results.append((
|
||||||
children, name)))
|
role,
|
||||||
|
make_refnode(builder, fromdocname, obj[0], obj[1], children, name),
|
||||||
|
))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
|
def _make_module_refnode(
|
||||||
contnode: Node) -> nodes.reference:
|
self, builder: Builder, fromdocname: str, name: str, contnode: Node
|
||||||
|
) -> nodes.reference:
|
||||||
# get additional info for modules
|
# get additional info for modules
|
||||||
module: ModuleEntry = self.modules[name]
|
module: ModuleEntry = self.modules[name]
|
||||||
title_parts = [name]
|
title_parts = [name]
|
||||||
@ -946,9 +1030,11 @@ class PythonDomain(Domain):
|
|||||||
return '.'.join(filter(None, [modname, clsname, target]))
|
return '.'.join(filter(None, [modname, clsname, target]))
|
||||||
|
|
||||||
|
|
||||||
def builtin_resolver(app: Sphinx, env: BuildEnvironment,
|
def builtin_resolver(
|
||||||
node: pending_xref, contnode: Element) -> Element | None:
|
app: Sphinx, env: BuildEnvironment, node: pending_xref, contnode: Element
|
||||||
|
) -> Element | None:
|
||||||
"""Do not emit nitpicky warnings for built-in types."""
|
"""Do not emit nitpicky warnings for built-in types."""
|
||||||
|
|
||||||
def istyping(s: str) -> bool:
|
def istyping(s: str) -> bool:
|
||||||
if s.startswith('typing.'):
|
if s.startswith('typing.'):
|
||||||
s = s.split('.', 1)[1]
|
s = s.split('.', 1)[1]
|
||||||
@ -977,7 +1063,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
|
|||||||
app.add_domain(PythonDomain)
|
app.add_domain(PythonDomain)
|
||||||
app.add_config_value('python_use_unqualified_type_names', False, 'env')
|
app.add_config_value('python_use_unqualified_type_names', False, 'env')
|
||||||
app.add_config_value(
|
app.add_config_value(
|
||||||
'python_maximum_signature_line_length', None, 'env', {int, type(None)},
|
'python_maximum_signature_line_length', None, 'env', {int, type(None)}
|
||||||
)
|
)
|
||||||
app.add_config_value('python_display_short_literal_types', False, 'env')
|
app.add_config_value('python_display_short_literal_types', False, 'env')
|
||||||
app.connect('object-description-transform', filter_meta_fields)
|
app.connect('object-description-transform', filter_meta_fields)
|
||||||
|
@ -23,8 +23,9 @@ if TYPE_CHECKING:
|
|||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
|
|
||||||
|
|
||||||
def parse_reftarget(reftarget: str, suppress_prefix: bool = False,
|
def parse_reftarget(
|
||||||
) -> tuple[str, str, str, bool]:
|
reftarget: str, suppress_prefix: bool = False
|
||||||
|
) -> tuple[str, str, str, bool]:
|
||||||
"""Parse a type string and return (reftype, reftarget, title, refspecific flag)"""
|
"""Parse a type string and return (reftype, reftarget, title, refspecific flag)"""
|
||||||
refspecific = False
|
refspecific = False
|
||||||
if reftarget.startswith('.'):
|
if reftarget.startswith('.'):
|
||||||
@ -50,12 +51,15 @@ def parse_reftarget(reftarget: str, suppress_prefix: bool = False,
|
|||||||
return reftype, reftarget, title, refspecific
|
return reftype, reftarget, title, refspecific
|
||||||
|
|
||||||
|
|
||||||
def type_to_xref(target: str, env: BuildEnvironment, *,
|
def type_to_xref(
|
||||||
suppress_prefix: bool = False) -> addnodes.pending_xref:
|
target: str, env: BuildEnvironment, *, suppress_prefix: bool = False
|
||||||
|
) -> addnodes.pending_xref:
|
||||||
"""Convert a type string to a cross reference node."""
|
"""Convert a type string to a cross reference node."""
|
||||||
if env:
|
if env:
|
||||||
kwargs = {'py:module': env.ref_context.get('py:module'),
|
kwargs = {
|
||||||
'py:class': env.ref_context.get('py:class')}
|
'py:module': env.ref_context.get('py:module'),
|
||||||
|
'py:class': env.ref_context.get('py:class'),
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
|
|
||||||
@ -66,14 +70,22 @@ def type_to_xref(target: str, env: BuildEnvironment, *,
|
|||||||
# nested classes. But python domain can't access the real python object because this
|
# nested classes. But python domain can't access the real python object because this
|
||||||
# module should work not-dynamically.
|
# module should work not-dynamically.
|
||||||
shortname = title.split('.')[-1]
|
shortname = title.split('.')[-1]
|
||||||
contnodes: list[Node] = [pending_xref_condition('', shortname, condition='resolved'),
|
contnodes: list[Node] = [
|
||||||
pending_xref_condition('', title, condition='*')]
|
pending_xref_condition('', shortname, condition='resolved'),
|
||||||
|
pending_xref_condition('', title, condition='*'),
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
contnodes = [nodes.Text(title)]
|
contnodes = [nodes.Text(title)]
|
||||||
|
|
||||||
return pending_xref('', *contnodes,
|
return pending_xref(
|
||||||
refdomain='py', reftype=reftype, reftarget=target,
|
'',
|
||||||
refspecific=refspecific, **kwargs)
|
*contnodes,
|
||||||
|
refdomain='py',
|
||||||
|
reftype=reftype,
|
||||||
|
reftarget=target,
|
||||||
|
refspecific=refspecific,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
|
def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
|
||||||
@ -82,19 +94,21 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
|
|||||||
|
|
||||||
def unparse(node: ast.AST) -> list[Node]:
|
def unparse(node: ast.AST) -> list[Node]:
|
||||||
if isinstance(node, ast.Attribute):
|
if isinstance(node, ast.Attribute):
|
||||||
return [nodes.Text(f"{unparse(node.value)[0]}.{node.attr}")]
|
return [nodes.Text(f'{unparse(node.value)[0]}.{node.attr}')]
|
||||||
if isinstance(node, ast.BinOp):
|
if isinstance(node, ast.BinOp):
|
||||||
result: list[Node] = unparse(node.left)
|
result: list[Node] = unparse(node.left)
|
||||||
result.extend(unparse(node.op))
|
result.extend(unparse(node.op))
|
||||||
result.extend(unparse(node.right))
|
result.extend(unparse(node.right))
|
||||||
return result
|
return result
|
||||||
if isinstance(node, ast.BitOr):
|
if isinstance(node, ast.BitOr):
|
||||||
return [addnodes.desc_sig_space(),
|
return [
|
||||||
addnodes.desc_sig_punctuation('', '|'),
|
addnodes.desc_sig_space(),
|
||||||
addnodes.desc_sig_space()]
|
addnodes.desc_sig_punctuation('', '|'),
|
||||||
|
addnodes.desc_sig_space(),
|
||||||
|
]
|
||||||
if isinstance(node, ast.Constant):
|
if isinstance(node, ast.Constant):
|
||||||
if node.value is Ellipsis:
|
if node.value is Ellipsis:
|
||||||
return [addnodes.desc_sig_punctuation('', "...")]
|
return [addnodes.desc_sig_punctuation('', '...')]
|
||||||
if isinstance(node.value, bool):
|
if isinstance(node.value, bool):
|
||||||
return [addnodes.desc_sig_keyword('', repr(node.value))]
|
return [addnodes.desc_sig_keyword('', repr(node.value))]
|
||||||
if isinstance(node.value, int):
|
if isinstance(node.value, int):
|
||||||
@ -157,8 +171,10 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
|
|||||||
result.pop()
|
result.pop()
|
||||||
result.pop()
|
result.pop()
|
||||||
else:
|
else:
|
||||||
result = [addnodes.desc_sig_punctuation('', '('),
|
result = [
|
||||||
addnodes.desc_sig_punctuation('', ')')]
|
addnodes.desc_sig_punctuation('', '('),
|
||||||
|
addnodes.desc_sig_punctuation('', ')'),
|
||||||
|
]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
if isinstance(node, ast.Call):
|
if isinstance(node, ast.Call):
|
||||||
@ -211,8 +227,11 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
|
|||||||
if isinstance(node, nodes.literal):
|
if isinstance(node, nodes.literal):
|
||||||
result.append(node[0])
|
result.append(node[0])
|
||||||
elif isinstance(node, nodes.Text) and node.strip():
|
elif isinstance(node, nodes.Text) and node.strip():
|
||||||
if (result and isinstance(result[-1], addnodes.desc_sig_punctuation) and
|
if (
|
||||||
result[-1].astext() == '~'):
|
result
|
||||||
|
and isinstance(result[-1], addnodes.desc_sig_punctuation)
|
||||||
|
and result[-1].astext() == '~'
|
||||||
|
):
|
||||||
result.pop()
|
result.pop()
|
||||||
result.append(type_to_xref(str(node), env, suppress_prefix=True))
|
result.append(type_to_xref(str(node), env, suppress_prefix=True))
|
||||||
else:
|
else:
|
||||||
@ -244,8 +263,7 @@ class _TypeParameterListParser(TokenProcessor):
|
|||||||
else:
|
else:
|
||||||
if current == token.INDENT:
|
if current == token.INDENT:
|
||||||
tokens += self.fetch_until(token.DEDENT)
|
tokens += self.fetch_until(token.DEDENT)
|
||||||
elif current.match(
|
elif current.match([token.OP, ':'], [token.OP, '='], [token.OP, ',']):
|
||||||
[token.OP, ':'], [token.OP, '='], [token.OP, ',']):
|
|
||||||
tokens.pop()
|
tokens.pop()
|
||||||
break
|
break
|
||||||
return tokens
|
return tokens
|
||||||
@ -254,7 +272,9 @@ class _TypeParameterListParser(TokenProcessor):
|
|||||||
while current := self.fetch_token():
|
while current := self.fetch_token():
|
||||||
if current == token.NAME:
|
if current == token.NAME:
|
||||||
tp_name = current.value.strip()
|
tp_name = current.value.strip()
|
||||||
if self.previous and self.previous.match([token.OP, '*'], [token.OP, '**']):
|
if self.previous and self.previous.match(
|
||||||
|
[token.OP, '*'], [token.OP, '**']
|
||||||
|
):
|
||||||
if self.previous == [token.OP, '*']:
|
if self.previous == [token.OP, '*']:
|
||||||
tp_kind = Parameter.VAR_POSITIONAL
|
tp_kind = Parameter.VAR_POSITIONAL
|
||||||
else:
|
else:
|
||||||
@ -275,9 +295,14 @@ class _TypeParameterListParser(TokenProcessor):
|
|||||||
tokens = self.fetch_type_param_spec()
|
tokens = self.fetch_type_param_spec()
|
||||||
tp_default = self._build_identifier(tokens)
|
tp_default = self._build_identifier(tokens)
|
||||||
|
|
||||||
if tp_kind != Parameter.POSITIONAL_OR_KEYWORD and tp_ann != Parameter.empty:
|
if (
|
||||||
msg = ('type parameter bound or constraint is not allowed '
|
tp_kind != Parameter.POSITIONAL_OR_KEYWORD
|
||||||
f'for {tp_kind.description} parameters')
|
and tp_ann != Parameter.empty
|
||||||
|
):
|
||||||
|
msg = (
|
||||||
|
'type parameter bound or constraint is not allowed '
|
||||||
|
f'for {tp_kind.description} parameters'
|
||||||
|
)
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
type_param = (tp_name, tp_kind, tp_default, tp_ann)
|
type_param = (tp_name, tp_kind, tp_default, tp_ann)
|
||||||
@ -315,12 +340,22 @@ class _TypeParameterListParser(TokenProcessor):
|
|||||||
idents.append(ident)
|
idents.append(ident)
|
||||||
# determine if the next token is an unpack operator depending
|
# determine if the next token is an unpack operator depending
|
||||||
# on the left and right hand side of the operator symbol
|
# on the left and right hand side of the operator symbol
|
||||||
is_unpack_operator = (
|
is_unpack_operator = op.match([token.OP, '*'], [token.OP, '**']) and not (
|
||||||
op.match([token.OP, '*'], [token.OP, '**']) and not (
|
tok.match(
|
||||||
tok.match(token.NAME, token.NUMBER, token.STRING,
|
token.NAME,
|
||||||
[token.OP, ')'], [token.OP, ']'], [token.OP, '}'])
|
token.NUMBER,
|
||||||
and after.match(token.NAME, token.NUMBER, token.STRING,
|
token.STRING,
|
||||||
[token.OP, '('], [token.OP, '['], [token.OP, '{'])
|
[token.OP, ')'],
|
||||||
|
[token.OP, ']'],
|
||||||
|
[token.OP, '}'],
|
||||||
|
)
|
||||||
|
and after.match(
|
||||||
|
token.NAME,
|
||||||
|
token.NUMBER,
|
||||||
|
token.STRING,
|
||||||
|
[token.OP, '('],
|
||||||
|
[token.OP, '['],
|
||||||
|
[token.OP, '{'],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -356,15 +391,14 @@ class _TypeParameterListParser(TokenProcessor):
|
|||||||
[token.OP, '@'], [token.OP, '/'], [token.OP, '//'], [token.OP, '%'],
|
[token.OP, '@'], [token.OP, '/'], [token.OP, '//'], [token.OP, '%'],
|
||||||
[token.OP, '<<'], [token.OP, '>>'], [token.OP, '>>>'],
|
[token.OP, '<<'], [token.OP, '>>'], [token.OP, '>>>'],
|
||||||
[token.OP, '<='], [token.OP, '>='], [token.OP, '=='], [token.OP, '!='],
|
[token.OP, '<='], [token.OP, '>='], [token.OP, '=='], [token.OP, '!='],
|
||||||
):
|
): # fmt: skip
|
||||||
return f' {tok.value} '
|
return f' {tok.value} '
|
||||||
|
|
||||||
return tok.value
|
return tok.value
|
||||||
|
|
||||||
|
|
||||||
def _parse_type_list(
|
def _parse_type_list(
|
||||||
tp_list: str, env: BuildEnvironment,
|
tp_list: str, env: BuildEnvironment, multi_line_parameter_list: bool = False
|
||||||
multi_line_parameter_list: bool = False,
|
|
||||||
) -> addnodes.desc_type_parameter_list:
|
) -> addnodes.desc_type_parameter_list:
|
||||||
"""Parse a list of type parameters according to PEP 695."""
|
"""Parse a list of type parameters according to PEP 695."""
|
||||||
type_params = addnodes.desc_type_parameter_list(tp_list)
|
type_params = addnodes.desc_type_parameter_list(tp_list)
|
||||||
@ -373,11 +407,13 @@ def _parse_type_list(
|
|||||||
# type annotations are interpreted as type parameter bound or constraints
|
# type annotations are interpreted as type parameter bound or constraints
|
||||||
parser = _TypeParameterListParser(tp_list)
|
parser = _TypeParameterListParser(tp_list)
|
||||||
parser.parse()
|
parser.parse()
|
||||||
for (tp_name, tp_kind, tp_default, tp_ann) in parser.type_params:
|
for tp_name, tp_kind, tp_default, tp_ann in parser.type_params:
|
||||||
# no positional-only or keyword-only allowed in a type parameters list
|
# no positional-only or keyword-only allowed in a type parameters list
|
||||||
if tp_kind in {Parameter.POSITIONAL_ONLY, Parameter.KEYWORD_ONLY}:
|
if tp_kind in {Parameter.POSITIONAL_ONLY, Parameter.KEYWORD_ONLY}:
|
||||||
msg = ('positional-only or keyword-only parameters '
|
msg = (
|
||||||
'are prohibited in type parameter lists')
|
'positional-only or keyword-only parameters '
|
||||||
|
'are prohibited in type parameter lists'
|
||||||
|
)
|
||||||
raise SyntaxError(msg)
|
raise SyntaxError(msg)
|
||||||
|
|
||||||
node = addnodes.desc_type_parameter()
|
node = addnodes.desc_type_parameter()
|
||||||
@ -395,8 +431,7 @@ def _parse_type_list(
|
|||||||
node += addnodes.desc_sig_punctuation('', ':')
|
node += addnodes.desc_sig_punctuation('', ':')
|
||||||
node += addnodes.desc_sig_space()
|
node += addnodes.desc_sig_space()
|
||||||
|
|
||||||
type_ann_expr = addnodes.desc_sig_name('', '',
|
type_ann_expr = addnodes.desc_sig_name('', '', *annotation) # type: ignore[arg-type]
|
||||||
*annotation) # type: ignore[arg-type]
|
|
||||||
# a type bound is ``T: U`` whereas type constraints
|
# a type bound is ``T: U`` whereas type constraints
|
||||||
# must be enclosed with parentheses. ``T: (U, V)``
|
# must be enclosed with parentheses. ``T: (U, V)``
|
||||||
if tp_ann.startswith('(') and tp_ann.endswith(')'):
|
if tp_ann.startswith('(') and tp_ann.endswith(')'):
|
||||||
@ -416,16 +451,16 @@ def _parse_type_list(
|
|||||||
node += addnodes.desc_sig_space()
|
node += addnodes.desc_sig_space()
|
||||||
node += addnodes.desc_sig_operator('', '=')
|
node += addnodes.desc_sig_operator('', '=')
|
||||||
node += addnodes.desc_sig_space()
|
node += addnodes.desc_sig_space()
|
||||||
node += nodes.inline('', tp_default,
|
node += nodes.inline(
|
||||||
classes=['default_value'],
|
'', tp_default, classes=['default_value'], support_smartquotes=False
|
||||||
support_smartquotes=False)
|
)
|
||||||
|
|
||||||
type_params += node
|
type_params += node
|
||||||
return type_params
|
return type_params
|
||||||
|
|
||||||
|
|
||||||
def _parse_arglist(
|
def _parse_arglist(
|
||||||
arglist: str, env: BuildEnvironment, multi_line_parameter_list: bool = False,
|
arglist: str, env: BuildEnvironment, multi_line_parameter_list: bool = False
|
||||||
) -> addnodes.desc_parameterlist:
|
) -> addnodes.desc_parameterlist:
|
||||||
"""Parse a list of arguments using AST parser"""
|
"""Parse a list of arguments using AST parser"""
|
||||||
params = addnodes.desc_parameterlist(arglist)
|
params = addnodes.desc_parameterlist(arglist)
|
||||||
@ -435,12 +470,18 @@ def _parse_arglist(
|
|||||||
for param in sig.parameters.values():
|
for param in sig.parameters.values():
|
||||||
if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
|
if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
|
||||||
# PEP-570: Separator for Positional Only Parameter: /
|
# PEP-570: Separator for Positional Only Parameter: /
|
||||||
params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/'))
|
params += addnodes.desc_parameter(
|
||||||
if param.kind == param.KEYWORD_ONLY and last_kind in {param.POSITIONAL_OR_KEYWORD,
|
'', '', addnodes.desc_sig_operator('', '/')
|
||||||
param.POSITIONAL_ONLY,
|
)
|
||||||
None}:
|
if param.kind == param.KEYWORD_ONLY and last_kind in {
|
||||||
|
param.POSITIONAL_OR_KEYWORD,
|
||||||
|
param.POSITIONAL_ONLY,
|
||||||
|
None,
|
||||||
|
}:
|
||||||
# PEP-3102: Separator for Keyword Only Parameter: *
|
# PEP-3102: Separator for Keyword Only Parameter: *
|
||||||
params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*'))
|
params += addnodes.desc_parameter(
|
||||||
|
'', '', addnodes.desc_sig_operator('', '*')
|
||||||
|
)
|
||||||
|
|
||||||
node = addnodes.desc_parameter()
|
node = addnodes.desc_parameter()
|
||||||
if param.kind == param.VAR_POSITIONAL:
|
if param.kind == param.VAR_POSITIONAL:
|
||||||
@ -464,8 +505,9 @@ def _parse_arglist(
|
|||||||
node += addnodes.desc_sig_space()
|
node += addnodes.desc_sig_space()
|
||||||
else:
|
else:
|
||||||
node += addnodes.desc_sig_operator('', '=')
|
node += addnodes.desc_sig_operator('', '=')
|
||||||
node += nodes.inline('', param.default, classes=['default_value'],
|
node += nodes.inline(
|
||||||
support_smartquotes=False)
|
'', param.default, classes=['default_value'], support_smartquotes=False
|
||||||
|
)
|
||||||
|
|
||||||
params += node
|
params += node
|
||||||
last_kind = param.kind
|
last_kind = param.kind
|
||||||
@ -478,9 +520,9 @@ def _parse_arglist(
|
|||||||
|
|
||||||
|
|
||||||
def _pseudo_parse_arglist(
|
def _pseudo_parse_arglist(
|
||||||
signode: desc_signature, arglist: str, multi_line_parameter_list: bool = False,
|
signode: desc_signature, arglist: str, multi_line_parameter_list: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
""""Parse" a list of arguments separated by commas.
|
"""'Parse' a list of arguments separated by commas.
|
||||||
|
|
||||||
Arguments can have "optional" annotations given by enclosing them in
|
Arguments can have "optional" annotations given by enclosing them in
|
||||||
brackets. Currently, this will split at any comma, even if it's inside a
|
brackets. Currently, this will split at any comma, even if it's inside a
|
||||||
@ -508,7 +550,8 @@ def _pseudo_parse_arglist(
|
|||||||
argument = argument[:-1].strip()
|
argument = argument[:-1].strip()
|
||||||
if argument:
|
if argument:
|
||||||
stack[-1] += addnodes.desc_parameter(
|
stack[-1] += addnodes.desc_parameter(
|
||||||
'', '', addnodes.desc_sig_name(argument, argument))
|
'', '', addnodes.desc_sig_name(argument, argument)
|
||||||
|
)
|
||||||
while ends_open:
|
while ends_open:
|
||||||
stack.append(addnodes.desc_optional())
|
stack.append(addnodes.desc_optional())
|
||||||
stack[-2] += stack[-1]
|
stack[-2] += stack[-1]
|
||||||
|
@ -35,13 +35,15 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# REs for Python signatures
|
# REs for Python signatures
|
||||||
py_sig_re = re.compile(
|
py_sig_re = re.compile(
|
||||||
r'''^ ([\w.]*\.)? # class name(s)
|
r"""^ ([\w.]*\.)? # class name(s)
|
||||||
(\w+) \s* # thing name
|
(\w+) \s* # thing name
|
||||||
(?: \[\s*(.*)\s*])? # optional: type parameters list
|
(?: \[\s*(.*)\s*])? # optional: type parameters list
|
||||||
(?: \(\s*(.*)\s*\) # optional: arguments
|
(?: \(\s*(.*)\s*\) # optional: arguments
|
||||||
(?:\s* -> \s* (.*))? # return annotation
|
(?:\s* -> \s* (.*))? # return annotation
|
||||||
)? $ # and nothing more
|
)? $ # and nothing more
|
||||||
''', re.VERBOSE)
|
""",
|
||||||
|
re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# This override allows our inline type specifiers to behave like :class: link
|
# This override allows our inline type specifiers to behave like :class: link
|
||||||
@ -60,9 +62,16 @@ class PyXrefMixin:
|
|||||||
) -> Node:
|
) -> Node:
|
||||||
# we use inliner=None to make sure we get the old behaviour with a single
|
# we use inliner=None to make sure we get the old behaviour with a single
|
||||||
# pending_xref node
|
# pending_xref node
|
||||||
result = super().make_xref(rolename, domain, target, # type: ignore[misc]
|
result = super().make_xref( # type: ignore[misc]
|
||||||
innernode, contnode,
|
rolename,
|
||||||
env, inliner=None, location=None)
|
domain,
|
||||||
|
target,
|
||||||
|
innernode,
|
||||||
|
contnode,
|
||||||
|
env,
|
||||||
|
inliner=None,
|
||||||
|
location=None,
|
||||||
|
)
|
||||||
if isinstance(result, pending_xref):
|
if isinstance(result, pending_xref):
|
||||||
assert env is not None
|
assert env is not None
|
||||||
result['refspecific'] = True
|
result['refspecific'] = True
|
||||||
@ -82,8 +91,10 @@ class PyXrefMixin:
|
|||||||
|
|
||||||
shortname = target.split('.')[-1]
|
shortname = target.split('.')[-1]
|
||||||
textnode = innernode('', shortname) # type: ignore[call-arg]
|
textnode = innernode('', shortname) # type: ignore[call-arg]
|
||||||
contnodes = [pending_xref_condition('', '', textnode, condition='resolved'),
|
contnodes = [
|
||||||
pending_xref_condition('', '', *children, condition='*')]
|
pending_xref_condition('', '', textnode, condition='resolved'),
|
||||||
|
pending_xref_condition('', '', *children, condition='*'),
|
||||||
|
]
|
||||||
result.extend(contnodes)
|
result.extend(contnodes)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -116,8 +127,18 @@ class PyXrefMixin:
|
|||||||
if in_literal or self._delimiters_re.match(sub_target):
|
if in_literal or self._delimiters_re.match(sub_target):
|
||||||
results.append(contnode or innernode(sub_target, sub_target)) # type: ignore[call-arg]
|
results.append(contnode or innernode(sub_target, sub_target)) # type: ignore[call-arg]
|
||||||
else:
|
else:
|
||||||
results.append(self.make_xref(rolename, domain, sub_target,
|
results.append(
|
||||||
innernode, contnode, env, inliner, location))
|
self.make_xref(
|
||||||
|
rolename,
|
||||||
|
domain,
|
||||||
|
sub_target,
|
||||||
|
innernode,
|
||||||
|
contnode,
|
||||||
|
env,
|
||||||
|
inliner,
|
||||||
|
location,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if sub_target in {'Literal', 'typing.Literal', '~typing.Literal'}:
|
if sub_target in {'Literal', 'typing.Literal', '~typing.Literal'}:
|
||||||
in_literal = True
|
in_literal = True
|
||||||
@ -161,22 +182,50 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
}
|
}
|
||||||
|
|
||||||
doc_field_types = [
|
doc_field_types = [
|
||||||
PyTypedField('parameter', label=_('Parameters'),
|
PyTypedField(
|
||||||
names=('param', 'parameter', 'arg', 'argument',
|
'parameter',
|
||||||
'keyword', 'kwarg', 'kwparam'),
|
label=_('Parameters'),
|
||||||
typerolename='class', typenames=('paramtype', 'type'),
|
names=(
|
||||||
can_collapse=True),
|
'param',
|
||||||
PyTypedField('variable', label=_('Variables'),
|
'parameter',
|
||||||
names=('var', 'ivar', 'cvar'),
|
'arg',
|
||||||
typerolename='class', typenames=('vartype',),
|
'argument',
|
||||||
can_collapse=True),
|
'keyword',
|
||||||
PyGroupedField('exceptions', label=_('Raises'), rolename='exc',
|
'kwarg',
|
||||||
names=('raises', 'raise', 'exception', 'except'),
|
'kwparam',
|
||||||
can_collapse=True),
|
),
|
||||||
Field('returnvalue', label=_('Returns'), has_arg=False,
|
typerolename='class',
|
||||||
names=('returns', 'return')),
|
typenames=('paramtype', 'type'),
|
||||||
PyField('returntype', label=_('Return type'), has_arg=False,
|
can_collapse=True,
|
||||||
names=('rtype',), bodyrolename='class'),
|
),
|
||||||
|
PyTypedField(
|
||||||
|
'variable',
|
||||||
|
label=_('Variables'),
|
||||||
|
names=('var', 'ivar', 'cvar'),
|
||||||
|
typerolename='class',
|
||||||
|
typenames=('vartype',),
|
||||||
|
can_collapse=True,
|
||||||
|
),
|
||||||
|
PyGroupedField(
|
||||||
|
'exceptions',
|
||||||
|
label=_('Raises'),
|
||||||
|
rolename='exc',
|
||||||
|
names=('raises', 'raise', 'exception', 'except'),
|
||||||
|
can_collapse=True,
|
||||||
|
),
|
||||||
|
Field(
|
||||||
|
'returnvalue',
|
||||||
|
label=_('Returns'),
|
||||||
|
has_arg=False,
|
||||||
|
names=('returns', 'return'),
|
||||||
|
),
|
||||||
|
PyField(
|
||||||
|
'returntype',
|
||||||
|
label=_('Return type'),
|
||||||
|
has_arg=False,
|
||||||
|
names=('rtype',),
|
||||||
|
bodyrolename='class',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
allow_nesting = False
|
allow_nesting = False
|
||||||
@ -212,18 +261,17 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
classname = self.env.ref_context.get('py:class')
|
classname = self.env.ref_context.get('py:class')
|
||||||
if classname:
|
if classname:
|
||||||
add_module = False
|
add_module = False
|
||||||
if prefix and (prefix == classname or
|
if prefix and (prefix == classname or prefix.startswith(f'{classname}.')):
|
||||||
prefix.startswith(classname + ".")):
|
|
||||||
fullname = prefix + name
|
fullname = prefix + name
|
||||||
# class name is given again in the signature
|
# class name is given again in the signature
|
||||||
prefix = prefix[len(classname):].lstrip('.')
|
prefix = prefix[len(classname) :].lstrip('.')
|
||||||
elif prefix:
|
elif prefix:
|
||||||
# class name is given in the signature, but different
|
# class name is given in the signature, but different
|
||||||
# (shouldn't happen)
|
# (shouldn't happen)
|
||||||
fullname = classname + '.' + prefix + name
|
fullname = f'{classname}.{prefix}{name}'
|
||||||
else:
|
else:
|
||||||
# class name is not given in the signature
|
# class name is not given in the signature
|
||||||
fullname = classname + '.' + name
|
fullname = f'{classname}.{name}'
|
||||||
else:
|
else:
|
||||||
add_module = True
|
add_module = True
|
||||||
if prefix:
|
if prefix:
|
||||||
@ -237,9 +285,11 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
signode['class'] = classname
|
signode['class'] = classname
|
||||||
signode['fullname'] = fullname
|
signode['fullname'] = fullname
|
||||||
|
|
||||||
max_len = (self.env.config.python_maximum_signature_line_length
|
max_len = (
|
||||||
or self.env.config.maximum_signature_line_length
|
self.env.config.python_maximum_signature_line_length
|
||||||
or 0)
|
or self.env.config.maximum_signature_line_length
|
||||||
|
or 0
|
||||||
|
)
|
||||||
|
|
||||||
# determine if the function arguments (without its type parameters)
|
# determine if the function arguments (without its type parameters)
|
||||||
# should be formatted on a multiline or not by removing the width of
|
# should be formatted on a multiline or not by removing the width of
|
||||||
@ -261,26 +311,31 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
sig_prefix = self.get_signature_prefix(sig)
|
sig_prefix = self.get_signature_prefix(sig)
|
||||||
if sig_prefix:
|
if sig_prefix:
|
||||||
if type(sig_prefix) is str:
|
if type(sig_prefix) is str:
|
||||||
msg = ("Python directive method get_signature_prefix()"
|
msg = (
|
||||||
" must return a list of nodes."
|
'Python directive method get_signature_prefix()'
|
||||||
f" Return value was '{sig_prefix}'.")
|
' must return a list of nodes.'
|
||||||
|
f" Return value was '{sig_prefix}'."
|
||||||
|
)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
signode += addnodes.desc_annotation(str(sig_prefix), '', *sig_prefix)
|
signode += addnodes.desc_annotation(str(sig_prefix), '', *sig_prefix)
|
||||||
|
|
||||||
if prefix:
|
if prefix:
|
||||||
signode += addnodes.desc_addname(prefix, prefix)
|
signode += addnodes.desc_addname(prefix, prefix)
|
||||||
elif modname and add_module and self.env.config.add_module_names:
|
elif modname and add_module and self.env.config.add_module_names:
|
||||||
nodetext = modname + '.'
|
nodetext = f'{modname}.'
|
||||||
signode += addnodes.desc_addname(nodetext, nodetext)
|
signode += addnodes.desc_addname(nodetext, nodetext)
|
||||||
|
|
||||||
signode += addnodes.desc_name(name, name)
|
signode += addnodes.desc_name(name, name)
|
||||||
|
|
||||||
if tp_list:
|
if tp_list:
|
||||||
try:
|
try:
|
||||||
signode += _parse_type_list(tp_list, self.env, multi_line_type_parameter_list)
|
signode += _parse_type_list(
|
||||||
|
tp_list, self.env, multi_line_type_parameter_list
|
||||||
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("could not parse tp_list (%r): %s", tp_list, exc,
|
logger.warning(
|
||||||
location=signode)
|
'could not parse tp_list (%r): %s', tp_list, exc, location=signode
|
||||||
|
)
|
||||||
|
|
||||||
if arglist:
|
if arglist:
|
||||||
try:
|
try:
|
||||||
@ -293,8 +348,9 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
|
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
|
||||||
except (NotImplementedError, ValueError) as exc:
|
except (NotImplementedError, ValueError) as exc:
|
||||||
# duplicated parameter names raise ValueError and not a SyntaxError
|
# duplicated parameter names raise ValueError and not a SyntaxError
|
||||||
logger.warning("could not parse arglist (%r): %s", arglist, exc,
|
logger.warning(
|
||||||
location=signode)
|
'could not parse arglist (%r): %s', arglist, exc, location=signode
|
||||||
|
)
|
||||||
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
|
_pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
|
||||||
else:
|
else:
|
||||||
if self.needs_arglist():
|
if self.needs_arglist():
|
||||||
@ -307,9 +363,9 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
|
|
||||||
anno = self.options.get('annotation')
|
anno = self.options.get('annotation')
|
||||||
if anno:
|
if anno:
|
||||||
signode += addnodes.desc_annotation(' ' + anno, '',
|
signode += addnodes.desc_annotation(
|
||||||
addnodes.desc_sig_space(),
|
f' {anno}', '', addnodes.desc_sig_space(), nodes.Text(anno)
|
||||||
nodes.Text(anno))
|
)
|
||||||
|
|
||||||
return fullname, prefix
|
return fullname, prefix
|
||||||
|
|
||||||
@ -329,10 +385,11 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
msg = 'must be implemented in subclasses'
|
msg = 'must be implemented in subclasses'
|
||||||
raise NotImplementedError(msg)
|
raise NotImplementedError(msg)
|
||||||
|
|
||||||
def add_target_and_index(self, name_cls: tuple[str, str], sig: str,
|
def add_target_and_index(
|
||||||
signode: desc_signature) -> None:
|
self, name_cls: tuple[str, str], sig: str, signode: desc_signature
|
||||||
|
) -> None:
|
||||||
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
||||||
fullname = (modname + '.' if modname else '') + name_cls[0]
|
fullname = (f'{modname}.' if modname else '') + name_cls[0]
|
||||||
node_id = make_id(self.env, self.state.document, '', fullname)
|
node_id = make_id(self.env, self.state.document, '', fullname)
|
||||||
signode['ids'].append(node_id)
|
signode['ids'].append(node_id)
|
||||||
self.state.document.note_explicit_target(signode)
|
self.state.document.note_explicit_target(signode)
|
||||||
@ -342,13 +399,20 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
|
|
||||||
canonical_name = self.options.get('canonical')
|
canonical_name = self.options.get('canonical')
|
||||||
if canonical_name:
|
if canonical_name:
|
||||||
domain.note_object(canonical_name, self.objtype, node_id, aliased=True,
|
domain.note_object(
|
||||||
location=signode)
|
canonical_name, self.objtype, node_id, aliased=True, location=signode
|
||||||
|
)
|
||||||
|
|
||||||
if 'no-index-entry' not in self.options:
|
if 'no-index-entry' not in self.options:
|
||||||
indextext = self.get_index_text(modname, name_cls)
|
indextext = self.get_index_text(modname, name_cls)
|
||||||
if indextext:
|
if indextext:
|
||||||
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
|
self.indexnode['entries'].append((
|
||||||
|
'single',
|
||||||
|
indextext,
|
||||||
|
node_id,
|
||||||
|
'',
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
|
||||||
def before_content(self) -> None:
|
def before_content(self) -> None:
|
||||||
"""Handle object nesting before content
|
"""Handle object nesting before content
|
||||||
@ -398,8 +462,7 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|||||||
with contextlib.suppress(IndexError):
|
with contextlib.suppress(IndexError):
|
||||||
classes.pop()
|
classes.pop()
|
||||||
|
|
||||||
self.env.ref_context['py:class'] = (classes[-1] if len(classes) > 0
|
self.env.ref_context['py:class'] = classes[-1] if len(classes) > 0 else None
|
||||||
else None)
|
|
||||||
if 'module' in self.options:
|
if 'module' in self.options:
|
||||||
modules = self.env.ref_context.setdefault('py:modules', [])
|
modules = self.env.ref_context.setdefault('py:modules', [])
|
||||||
if modules:
|
if modules:
|
||||||
|
Loading…
Reference in New Issue
Block a user