mirror of
https://github.com/readthedocs/sphinx-autoapi
synced 2024-10-31 21:20:17 +00:00
Compare commits
2 Commits
007077a7db
...
3b037c7643
Author | SHA1 | Date | |
---|---|---|---|
|
3b037c7643 | ||
|
bc71226c3b |
@ -116,27 +116,39 @@ def get_full_basenames(node):
|
||||
yield _resolve_annotation(base)
|
||||
|
||||
|
||||
def _get_const_values(node):
|
||||
value = None
|
||||
def _get_const_value(node):
|
||||
if isinstance(node, astroid.nodes.Const):
|
||||
if isinstance(node.value, str) and "\n" in node.value:
|
||||
return '"""{0}"""'.format(node.value)
|
||||
|
||||
class NotConstException(Exception):
|
||||
pass
|
||||
|
||||
def _inner(node):
|
||||
if isinstance(node, (astroid.nodes.List, astroid.nodes.Tuple)):
|
||||
new_value = []
|
||||
for element in node.elts:
|
||||
if isinstance(element, astroid.nodes.Const):
|
||||
new_value.append(element.value)
|
||||
elif isinstance(element, (astroid.nodes.List, astroid.nodes.Tuple)):
|
||||
new_value.append(_get_const_values(element))
|
||||
else:
|
||||
break
|
||||
else:
|
||||
value = new_value
|
||||
new_value.append(_inner(element))
|
||||
|
||||
if isinstance(node, astroid.nodes.Tuple):
|
||||
value = tuple(new_value)
|
||||
elif isinstance(node, astroid.nodes.Const):
|
||||
value = node.value
|
||||
return tuple(new_value)
|
||||
|
||||
return value
|
||||
return new_value
|
||||
elif isinstance(node, astroid.nodes.Const):
|
||||
# Don't allow multi-line strings inside a data structure.
|
||||
if isinstance(node.value, str) and "\n" in node.value:
|
||||
raise NotConstException()
|
||||
|
||||
return node.value
|
||||
|
||||
raise NotConstException()
|
||||
|
||||
try:
|
||||
result = _inner(node)
|
||||
except NotConstException:
|
||||
return None
|
||||
|
||||
return repr(result)
|
||||
|
||||
|
||||
def get_assign_value(node):
|
||||
@ -149,8 +161,9 @@ def get_assign_value(node):
|
||||
to get the assignment value from.
|
||||
|
||||
Returns:
|
||||
tuple(str, object or None) or None: The name that is assigned
|
||||
to, and the value assigned to the name (if it can be converted).
|
||||
tuple(str, str or None) or None: The name that is assigned
|
||||
to, and the string representation of the value assigned to the name
|
||||
(if it can be converted).
|
||||
"""
|
||||
try:
|
||||
targets = node.targets
|
||||
@ -165,7 +178,7 @@ def get_assign_value(node):
|
||||
name = target.attrname
|
||||
else:
|
||||
return None
|
||||
return (name, _get_const_values(node.value))
|
||||
return (name, _get_const_value(node.value))
|
||||
|
||||
return None
|
||||
|
||||
|
@ -91,6 +91,8 @@ class Parser:
|
||||
value = assign_value[1]
|
||||
|
||||
annotation = _astroid_utils.get_assign_annotation(node)
|
||||
if annotation in ("TypeAlias", "typing.TypeAlias"):
|
||||
value = node.value.as_string()
|
||||
|
||||
data = {
|
||||
"type": type_,
|
||||
@ -274,6 +276,35 @@ class Parser:
|
||||
|
||||
return data
|
||||
|
||||
def parse_typealias(self, node):
|
||||
doc = ""
|
||||
doc_node = node.next_sibling()
|
||||
if isinstance(doc_node, astroid.nodes.Expr) and isinstance(
|
||||
doc_node.value, astroid.nodes.Const
|
||||
):
|
||||
doc = doc_node.value.value
|
||||
|
||||
if isinstance(node.name, astroid.nodes.AssignName):
|
||||
name = node.name.name
|
||||
elif isinstance(node.name, astroid.nodes.AssignAttr):
|
||||
name = node.name.attrname
|
||||
else:
|
||||
return []
|
||||
|
||||
data = {
|
||||
"type": "data",
|
||||
"name": name,
|
||||
"qual_name": self._get_qual_name(name),
|
||||
"full_name": self._get_full_name(name),
|
||||
"doc": _prepare_docstring(doc),
|
||||
"value": node.value.as_string(),
|
||||
"from_line_no": node.fromlineno,
|
||||
"to_line_no": node.tolineno,
|
||||
"annotation": "TypeAlias",
|
||||
}
|
||||
|
||||
return [data]
|
||||
|
||||
def parse(self, node):
|
||||
data = {}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
{% endif %}
|
||||
{% if obj.value is not none %}
|
||||
|
||||
{% if obj.value is string and obj.value.splitlines()|count > 1 %}
|
||||
{% if obj.value.splitlines()|count > 1 %}
|
||||
:value: Multiline-String
|
||||
|
||||
.. raw:: html
|
||||
@ -20,18 +20,14 @@
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
"""{{ obj.value|indent(width=6,blank=true) }}"""
|
||||
{{ obj.value|indent(width=6,blank=true) }}
|
||||
|
||||
.. raw:: html
|
||||
|
||||
</details>
|
||||
|
||||
{% else %}
|
||||
{% if obj.value is string %}
|
||||
:value: {{ "%r" % obj.value|string|truncate(100) }}
|
||||
{% else %}
|
||||
:value: {{ obj.value|string|truncate(100) }}
|
||||
{% endif %}
|
||||
:value: {{ obj.value|truncate(100) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
1
docs/changes/224.bugfix
Normal file
1
docs/changes/224.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Values are always rendered for TypeAlises and PEP-695 type aliases.
|
1
docs/changes/414.feature
Normal file
1
docs/changes/414.feature
Normal file
@ -0,0 +1 @@
|
||||
Render PEP-695 type aliases as TypeAlias assignments.
|
20
tests/python/pep695/conf.py
Normal file
20
tests/python/pep695/conf.py
Normal file
@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
templates_path = ["_templates"]
|
||||
source_suffix = ".rst"
|
||||
master_doc = "index"
|
||||
project = "pyexample"
|
||||
copyright = "2015, readthedocs"
|
||||
author = "readthedocs"
|
||||
version = "0.1"
|
||||
release = "0.1"
|
||||
language = "en"
|
||||
exclude_patterns = ["_build"]
|
||||
pygments_style = "sphinx"
|
||||
todo_include_todos = False
|
||||
html_theme = "alabaster"
|
||||
htmlhelp_basename = "pyexampledoc"
|
||||
extensions = ["sphinx.ext.intersphinx", "sphinx.ext.autodoc", "autoapi.extension"]
|
||||
intersphinx_mapping = {"python": ("https://docs.python.org/3.10", None)}
|
||||
autoapi_dirs = ["example"]
|
||||
autoapi_file_pattern = "*.py"
|
4
tests/python/pep695/example/example.py
Normal file
4
tests/python/pep695/example/example.py
Normal file
@ -0,0 +1,4 @@
|
||||
from typing import TypeAlias
|
||||
|
||||
MyTypeAliasA: TypeAlias = tuple[str, int]
|
||||
type MyTypeAliasB = tuple[str, int]
|
26
tests/python/pep695/index.rst
Normal file
26
tests/python/pep695/index.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. pyexample documentation master file, created by
|
||||
sphinx-quickstart on Fri May 29 13:34:37 2015.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to pyexample's documentation!
|
||||
=====================================
|
||||
|
||||
.. toctree::
|
||||
|
||||
autoapi/index
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
@ -25,7 +25,7 @@ class TestPythonParser:
|
||||
"""
|
||||
data = self.parse(source)[0]
|
||||
assert data["name"] == "__all__"
|
||||
assert data["value"] == ["Foo", 5.0]
|
||||
assert data["value"] == "['Foo', 5.0]"
|
||||
|
||||
def test_parses_all_multiline(self):
|
||||
source = """
|
||||
@ -35,7 +35,7 @@ class TestPythonParser:
|
||||
]
|
||||
"""
|
||||
data = self.parse(source)[0]
|
||||
assert data["value"] == ["foo", "bar"]
|
||||
assert data["value"] == "['foo', 'bar']"
|
||||
|
||||
def test_parses_name(self):
|
||||
source = "foo.bar"
|
||||
@ -43,7 +43,7 @@ class TestPythonParser:
|
||||
|
||||
def test_parses_list(self):
|
||||
name = "__all__"
|
||||
value = [1, 2, 3, 4]
|
||||
value = "[1, 2, 3, 4]"
|
||||
source = "{} = {}".format(name, value)
|
||||
data = self.parse(source)[0]
|
||||
assert data["name"] == name
|
||||
@ -51,7 +51,7 @@ class TestPythonParser:
|
||||
|
||||
def test_parses_nested_list(self):
|
||||
name = "__all__"
|
||||
value = [[1, 2], [3, 4]]
|
||||
value = "[[1, 2], [3, 4]]"
|
||||
source = "{} = {}".format(name, value)
|
||||
data = self.parse(source)[0]
|
||||
assert data["name"] == name
|
||||
|
@ -587,6 +587,34 @@ class TestPipeUnionModule:
|
||||
assert links[1].text == "None"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.version_info < (3, 12), reason="PEP-695 support requires Python >=3.12"
|
||||
)
|
||||
class TestPEP695:
|
||||
@pytest.fixture(autouse=True, scope="class")
|
||||
def built(self, builder):
|
||||
builder("pep695", warningiserror=True)
|
||||
|
||||
def test_integration(self, parse):
|
||||
example_file = parse("_build/html/autoapi/example/index.html")
|
||||
|
||||
alias = example_file.find(id="example.MyTypeAliasA")
|
||||
properties = alias.find_all(class_="property")
|
||||
assert len(properties) == 2
|
||||
annotation = properties[0].text
|
||||
assert annotation == ": TypeAlias"
|
||||
value = properties[1].text
|
||||
assert value == " = tuple[str, int]"
|
||||
|
||||
alias = example_file.find(id="example.MyTypeAliasB")
|
||||
properties = alias.find_all(class_="property")
|
||||
assert len(properties) == 2
|
||||
annotation = properties[0].text
|
||||
assert annotation == ": TypeAlias"
|
||||
value = properties[1].text
|
||||
assert value == " = tuple[str, int]"
|
||||
|
||||
|
||||
def test_napoleon_integration_loaded(builder, parse):
|
||||
confoverrides = {
|
||||
"exclude_patterns": ["manualapi.rst"],
|
||||
|
@ -92,10 +92,17 @@ class TestAstroidUtils:
|
||||
@pytest.mark.parametrize(
|
||||
("source", "expected"),
|
||||
[
|
||||
('a = "a"', ("a", "a")),
|
||||
("a = 1", ("a", 1)),
|
||||
('a = "a"', ("a", "'a'")),
|
||||
("a = 1", ("a", "1")),
|
||||
("a, b, c = (1, 2, 3)", None),
|
||||
("a = b = 1", None),
|
||||
("a = [1, 2, [3, 4]]", ("a", "[1, 2, [3, 4]]")),
|
||||
("a = [1, 2, variable[subscript]]", ("a", None)),
|
||||
('a = """multiline\nstring"""', ("a", '"""multiline\nstring"""')),
|
||||
('a = ["""multiline\nstring"""]', ("a", None)),
|
||||
("a = (1, 2, 3)", ("a", "(1, 2, 3)")),
|
||||
("a = (1, 'two', 3)", ("a", "(1, 'two', 3)")),
|
||||
("a = None", ("a", "None")),
|
||||
],
|
||||
)
|
||||
def test_can_get_assign_values(self, source, expected):
|
||||
|
Loading…
Reference in New Issue
Block a user