Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sphinx-doc/sphinx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-05-24 13:18:21 +0300
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-05-31 17:22:20 +0300
commit640bb2e586f882df305568bf99aacb151120f84f (patch)
tree94cac4b455ad75c61b3808627156c2a7b4180ab0 /sphinx
parenta59f83b6bdaf86dec6058cf72ae12eae967426c5 (diff)
pycode: Detect @overload decorators
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/pycode/__init__.py3
-rw-r--r--sphinx/pycode/parser.py34
2 files changed, 37 insertions, 0 deletions
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index 4879fb349..963680a54 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -12,6 +12,7 @@ import re
import tokenize
import warnings
from importlib import import_module
+from inspect import Signature
from io import StringIO
from os import path
from typing import Any, Dict, IO, List, Tuple, Optional
@@ -145,6 +146,7 @@ class ModuleAnalyzer:
self.annotations = None # type: Dict[Tuple[str, str], str]
self.attr_docs = None # type: Dict[Tuple[str, str], List[str]]
self.finals = None # type: List[str]
+ self.overloads = None # type: Dict[str, List[Signature]]
self.tagorder = None # type: Dict[str, int]
self.tags = None # type: Dict[str, Tuple[str, int, int]]
@@ -163,6 +165,7 @@ class ModuleAnalyzer:
self.annotations = parser.annotations
self.finals = parser.finals
+ self.overloads = parser.overloads
self.tags = parser.definitions
self.tagorder = parser.deforders
except Exception as exc:
diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py
index 3762c72cc..ec89a3616 100644
--- a/sphinx/pycode/parser.py
+++ b/sphinx/pycode/parser.py
@@ -12,12 +12,14 @@ import itertools
import re
import sys
import tokenize
+from inspect import Signature
from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING
from tokenize import COMMENT, NL
from typing import Any, Dict, List, Optional, Tuple
from sphinx.pycode.ast import ast # for py37 or older
from sphinx.pycode.ast import parse, unparse
+from sphinx.util.inspect import signature_from_ast
comment_re = re.compile('^\\s*#: ?(.*)\r?\n?$')
@@ -232,8 +234,10 @@ class VariableCommentPicker(ast.NodeVisitor):
self.previous = None # type: ast.AST
self.deforders = {} # type: Dict[str, int]
self.finals = [] # type: List[str]
+ self.overloads = {} # type: Dict[str, List[Signature]]
self.typing = None # type: str
self.typing_final = None # type: str
+ self.typing_overload = None # type: str
super().__init__()
def get_qualname_for(self, name: str) -> Optional[List[str]]:
@@ -257,6 +261,12 @@ class VariableCommentPicker(ast.NodeVisitor):
if qualname:
self.finals.append(".".join(qualname))
+ def add_overload_entry(self, func: ast.FunctionDef) -> None:
+ qualname = self.get_qualname_for(func.name)
+ if qualname:
+ overloads = self.overloads.setdefault(".".join(qualname), [])
+ overloads.append(signature_from_ast(func))
+
def add_variable_comment(self, name: str, comment: str) -> None:
qualname = self.get_qualname_for(name)
if qualname:
@@ -285,6 +295,22 @@ class VariableCommentPicker(ast.NodeVisitor):
return False
+ def is_overload(self, decorators: List[ast.expr]) -> bool:
+ overload = []
+ if self.typing:
+ overload.append('%s.overload' % self.typing)
+ if self.typing_overload:
+ overload.append(self.typing_overload)
+
+ for decorator in decorators:
+ try:
+ if unparse(decorator) in overload:
+ return True
+ except NotImplementedError:
+ pass
+
+ return False
+
def get_self(self) -> ast.arg:
"""Returns the name of first argument if in function."""
if self.current_function and self.current_function.args.args:
@@ -310,6 +336,8 @@ class VariableCommentPicker(ast.NodeVisitor):
self.typing = name.asname or name.name
elif name.name == 'typing.final':
self.typing_final = name.asname or name.name
+ elif name.name == 'typing.overload':
+ self.typing_overload = name.asname or name.name
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
"""Handles Import node and record it to definition orders."""
@@ -318,6 +346,8 @@ class VariableCommentPicker(ast.NodeVisitor):
if node.module == 'typing' and name.name == 'final':
self.typing_final = name.asname or name.name
+ elif node.module == 'typing' and name.name == 'overload':
+ self.typing_overload = name.asname or name.name
def visit_Assign(self, node: ast.Assign) -> None:
"""Handles Assign node and pick up a variable comment."""
@@ -417,6 +447,8 @@ class VariableCommentPicker(ast.NodeVisitor):
self.add_entry(node.name) # should be called before setting self.current_function
if self.is_final(node.decorator_list):
self.add_final_entry(node.name)
+ if self.is_overload(node.decorator_list):
+ self.add_overload_entry(node)
self.context.append(node.name)
self.current_function = node
for child in node.body:
@@ -518,6 +550,7 @@ class Parser:
self.deforders = {} # type: Dict[str, int]
self.definitions = {} # type: Dict[str, Tuple[str, int, int]]
self.finals = [] # type: List[str]
+ self.overloads = {} # type: Dict[str, List[Signature]]
def parse(self) -> None:
"""Parse the source code."""
@@ -533,6 +566,7 @@ class Parser:
self.comments = picker.comments
self.deforders = picker.deforders
self.finals = picker.finals
+ self.overloads = picker.overloads
def parse_definition(self) -> None:
"""Parse the location of definitions from the code."""