diff options
author | James <50501825+Gobot1234@users.noreply.github.com> | 2021-08-02 02:32:14 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-02 02:32:14 +0300 |
commit | 596ad5523de2caa5b83a16bd61f99fca1a8d65b0 (patch) | |
tree | 64cdb8a61050af0f18f3c36d551f2ccc8533b3b2 /sphinx/util | |
parent | 423b10a84843a629b96b6893a08819a1e07927af (diff) | |
parent | d788e78a5e2f339cc5e5c5fd5d8431e1991e568f (diff) |
Merge branch '4.x' into 4.x
Diffstat (limited to 'sphinx/util')
-rw-r--r-- | sphinx/util/__init__.py | 4 | ||||
-rw-r--r-- | sphinx/util/cfamily.py | 2 | ||||
-rw-r--r-- | sphinx/util/console.py | 2 | ||||
-rw-r--r-- | sphinx/util/docutils.py | 8 | ||||
-rw-r--r-- | sphinx/util/inspect.py | 26 | ||||
-rw-r--r-- | sphinx/util/inventory.py | 2 | ||||
-rw-r--r-- | sphinx/util/logging.py | 16 | ||||
-rw-r--r-- | sphinx/util/matching.py | 6 | ||||
-rw-r--r-- | sphinx/util/nodes.py | 10 | ||||
-rw-r--r-- | sphinx/util/typing.py | 27 |
10 files changed, 65 insertions, 38 deletions
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 99ed8dca7..384925157 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -59,7 +59,7 @@ def docname_join(basedocname: str, docname: str) -> str: def path_stabilize(filepath: str) -> str: - "normalize path separater and unicode string" + "Normalize path separator and unicode string" newpath = filepath.replace(os.path.sep, SEP) return unicodedata.normalize('NFC', newpath) @@ -435,7 +435,7 @@ def split_full_qualified_name(name: str) -> Tuple[Optional[str], str]: A "full" qualified name means a string containing both module name and qualified name. - .. note:: This function imports module actually to check the exisitence. + .. note:: This function actually imports the module to check its existence. Therefore you need to mock 3rd party modules if needed before calling this function. """ diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index 8d0896624..3aa22c625 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -216,7 +216,7 @@ class DefinitionError(Exception): class BaseParser: def __init__(self, definition: str, *, - location: Union[nodes.Node, Tuple[str, int]], + location: Union[nodes.Node, Tuple[str, int], str], config: "Config") -> None: self.definition = definition.strip() self.location = location # for warnings diff --git a/sphinx/util/console.py b/sphinx/util/console.py index 0c8008371..0fb6cc672 100644 --- a/sphinx/util/console.py +++ b/sphinx/util/console.py @@ -25,7 +25,7 @@ codes: Dict[str, str] = {} def terminal_safe(s: str) -> str: - """safely encode a string for printing to the terminal.""" + """Safely encode a string for printing to the terminal.""" return s.encode('ascii', 'backslashreplace').decode('ascii') diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index c2e12e152..5a2732027 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -339,6 +339,10 @@ class SphinxDirective(Directive): """Set source and line number to the node.""" node.source, node.line = self.get_source_info() + def get_location(self) -> str: + """Get current location info for logging.""" + return ':'.join(str(s) for s in self.get_source_info()) + class SphinxRole: """A base class for Sphinx roles. @@ -401,6 +405,10 @@ class SphinxRole: def set_source_info(self, node: Node, lineno: int = None) -> None: node.source, node.line = self.get_source_info(lineno) + def get_location(self) -> str: + """Get current location info for logging.""" + return ':'.join(str(s) for s in self.get_source_info()) + class ReferenceRole(SphinxRole): """A base class for reference roles. diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 23dd9e930..2bb900bd7 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -211,12 +211,15 @@ def getslots(obj: Any) -> Optional[Dict]: def isNewType(obj: Any) -> bool: """Check the if object is a kind of NewType.""" - __module__ = safe_getattr(obj, '__module__', None) - __qualname__ = safe_getattr(obj, '__qualname__', None) - if __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type': - return True + if sys.version_info >= (3, 10): + return isinstance(obj, typing.NewType) else: - return False + __module__ = safe_getattr(obj, '__module__', None) + __qualname__ = safe_getattr(obj, '__qualname__', None) + if __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type': + return True + else: + return False def isenumclass(x: Any) -> bool: @@ -245,12 +248,17 @@ def ispartial(obj: Any) -> bool: return isinstance(obj, (partial, partialmethod)) -def isclassmethod(obj: Any) -> bool: +def isclassmethod(obj: Any, cls: Any = None, name: str = None) -> bool: """Check if the object is classmethod.""" if isinstance(obj, classmethod): return True elif inspect.ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__): return True + elif cls and name: + for basecls in getmro(cls): + meth = basecls.__dict__.get(name) + if meth: + return isclassmethod(meth) return False @@ -837,6 +845,12 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr, * inherited docstring * inherited decorated methods """ + if cls and name and isclassmethod(obj, cls, name): + for basecls in getmro(cls): + meth = basecls.__dict__.get(name) + if meth: + return getdoc(meth.__func__) + doc = attrgetter(obj, '__doc__', None) if ispartial(obj) and doc == obj.__class__.__doc__: return getdoc(obj.func) diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py index f21c27323..a7a68304f 100644 --- a/sphinx/util/inventory.py +++ b/sphinx/util/inventory.py @@ -24,7 +24,7 @@ if TYPE_CHECKING: class InventoryFileReader: - """A file reader for inventory file. + """A file reader for an inventory file. This reader supports mixture of texts and compressed texts. """ diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 494f45ebb..e18d82469 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -187,7 +187,7 @@ class MemoryHandler(logging.handlers.BufferingHandler): @contextmanager def pending_warnings() -> Generator[logging.Handler, None, None]: - """Contextmanager to pend logging warnings temporary. + """Context manager to postpone logging warnings temporarily. Similar to :func:`pending_logging`. """ @@ -215,7 +215,7 @@ def pending_warnings() -> Generator[logging.Handler, None, None]: @contextmanager def suppress_logging() -> Generator[MemoryHandler, None, None]: - """Contextmanager to suppress logging all logs temporary. + """Context manager to suppress logging all logs temporarily. For example:: @@ -244,7 +244,7 @@ def suppress_logging() -> Generator[MemoryHandler, None, None]: @contextmanager def pending_logging() -> Generator[MemoryHandler, None, None]: - """Contextmanager to pend logging all logs temporary. + """Context manager to postpone logging all logs temporarily. For example:: @@ -264,7 +264,7 @@ def pending_logging() -> Generator[MemoryHandler, None, None]: @contextmanager def skip_warningiserror(skip: bool = True) -> Generator[None, None, None]: - """contextmanager to skip WarningIsErrorFilter for a while.""" + """Context manager to skip WarningIsErrorFilter temporarily.""" logger = logging.getLogger(NAMESPACE) if skip is False: @@ -284,7 +284,7 @@ def skip_warningiserror(skip: bool = True) -> Generator[None, None, None]: @contextmanager def prefixed_warnings(prefix: str) -> Generator[None, None, None]: - """Prepend prefix to all records for a while. + """Context manager to prepend prefix to all warning log records temporarily. For example:: @@ -351,7 +351,7 @@ class InfoFilter(logging.Filter): def is_suppressed_warning(type: str, subtype: str, suppress_warnings: List[str]) -> bool: - """Check the warning is suppressed or not.""" + """Check whether the warning is suppressed or not.""" if type is None: return False @@ -434,7 +434,7 @@ class DisableWarningIsErrorFilter(logging.Filter): class MessagePrefixFilter(logging.Filter): - """Prepend prefix to all records.""" + """Prepend prefix to all log records.""" def __init__(self, prefix: str) -> None: self.prefix = prefix @@ -555,7 +555,7 @@ class SafeEncodingWriter: class LastMessagesWriter: - """Stream writer which memories last 10 messages to save trackback""" + """Stream writer storing last 10 messages in memory to save trackback""" def __init__(self, app: "Sphinx", stream: IO) -> None: self.app = app diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py index f32d682f1..1e7f9883a 100644 --- a/sphinx/util/matching.py +++ b/sphinx/util/matching.py @@ -90,14 +90,16 @@ _pat_cache: Dict[str, Pattern] = {} def patmatch(name: str, pat: str) -> Optional[Match[str]]: - """Return if name matches pat. Adapted from fnmatch module.""" + """Return if name matches the regular expression (pattern) + ``pat```. Adapted from fnmatch module.""" if pat not in _pat_cache: _pat_cache[pat] = re.compile(_translate_pattern(pat)) return _pat_cache[pat].match(name) def patfilter(names: Iterable[str], pat: str) -> List[str]: - """Return the subset of the list NAMES that match PAT. + """Return the subset of the list ``names`` that match + the regular expression (pattern) ``pat``. Adapted from fnmatch module. """ diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 36db149b9..7ce0933c6 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -40,8 +40,8 @@ caption_ref_re = explicit_title_re # b/w compat alias class NodeMatcher: """A helper class for Node.traverse(). - It checks that given node is an instance of specified node-classes and it has - specified node-attributes. + It checks that the given node is an instance of the specified node-classes and + has the specified node-attributes. For example, following example searches ``reference`` node having ``refdomain`` and ``reftype`` attributes:: @@ -91,7 +91,7 @@ class NodeMatcher: def get_full_module_name(node: Node) -> str: """ - return full module dotted path like: 'docutils.nodes.paragraph' + Return full module dotted path like: 'docutils.nodes.paragraph' :param nodes.Node node: target node :return: full module dotted path @@ -588,7 +588,7 @@ NON_SMARTQUOTABLE_PARENT_NODES = ( def is_smartquotable(node: Node) -> bool: - """Check the node is smart-quotable or not.""" + """Check whether the node is smart-quotable or not.""" if isinstance(node.parent, NON_SMARTQUOTABLE_PARENT_NODES): return False elif node.parent.get('support_smartquotes', None) is False: @@ -600,7 +600,7 @@ def is_smartquotable(node: Node) -> bool: def process_only_nodes(document: Node, tags: "Tags") -> None: - """Filter ``only`` nodes which does not match *tags*.""" + """Filter ``only`` nodes which do not match *tags*.""" for node in document.traverse(addnodes.only): try: ret = tags.eval_condition(node['expr']) diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index deba6b53d..78feb6492 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -33,10 +33,10 @@ else: ref = _ForwardRef(self.arg) return ref._eval_type(globalns, localns) -if sys.version_info > (3, 10): - from types import Union as types_Union -else: - types_Union = None +try: + from types import UnionType # type: ignore # python 3.10 or above +except ImportError: + UnionType = None if False: # For type annotation @@ -86,6 +86,9 @@ def get_type_hints(obj: Any, globalns: Dict = None, localns: Dict = None) -> Dic except NameError: # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) return safe_getattr(obj, '__annotations__', {}) + except AttributeError: + # Failed to evaluate ForwardRef (maybe not runtime checkable) + return safe_getattr(obj, '__annotations__', {}) except TypeError: # Invalid object is given. But try to get __annotations__ as a fallback for # the code using type union operator (PEP 604) in python 3.9 or below. @@ -114,7 +117,7 @@ def restify(cls: Optional[Type]) -> str: return ':class:`%s`' % INVALID_BUILTIN_CLASSES[cls] elif inspect.isNewType(cls): return ':class:`%s`' % cls.__name__ - elif types_Union and isinstance(cls, types_Union): + elif UnionType and isinstance(cls, UnionType): if len(cls.__args__) > 1 and None in cls.__args__: args = ' | '.join(restify(a) for a in cls.__args__ if a) return 'Optional[%s]' % args @@ -177,17 +180,17 @@ def _restify_py37(cls: Optional[Type]) -> str: text += r"\ [%s]" % ", ".join(restify(a) for a in cls.__args__) return text - elif hasattr(cls, '__qualname__'): - if cls.__module__ == 'typing': - return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__) - else: - return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__) elif hasattr(cls, '_name'): # SpecialForm if cls.__module__ == 'typing': return ':obj:`~%s.%s`' % (cls.__module__, cls._name) else: return ':obj:`%s.%s`' % (cls.__module__, cls._name) + elif hasattr(cls, '__qualname__'): + if cls.__module__ == 'typing': + return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__) + else: + return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__) elif isinstance(cls, ForwardRef): return ':class:`%s`' % cls.__forward_arg__ else: @@ -315,7 +318,7 @@ def stringify(annotation: Any) -> str: elif annotation in INVALID_BUILTIN_CLASSES: return INVALID_BUILTIN_CLASSES[annotation] elif (getattr(annotation, '__module__', None) == 'builtins' and - hasattr(annotation, '__qualname__')): + getattr(annotation, '__qualname__', None)): if hasattr(annotation, '__args__'): # PEP 585 generic return repr(annotation) else: @@ -346,7 +349,7 @@ def _stringify_py37(annotation: Any) -> str: elif hasattr(annotation, '__origin__'): # instantiated generic provided by a user qualname = stringify(annotation.__origin__) - elif types_Union and isinstance(annotation, types_Union): # types.Union (for py3.10+) + elif UnionType and isinstance(annotation, UnionType): # types.Union (for py3.10+) qualname = 'types.Union' else: # we weren't able to extract the base type, appending arguments would |