utils.py

#

Unclassified utils

import inspect
import logging
import re
import sys
from modulefinder import Module
from typing import Any, Callable, List, Optional, Sequence, Tuple, Union, cast

from pydoc_fork.custom_types import TypeLike
from pydoc_fork.module_utils import locate

LOGGER = logging.getLogger(__name__)
#

Given an object or a path to an object, get the object and its name.

def resolve(thing: Union[str, Any], forceload: int = 0) -> Tuple[Any, Any]:
#

\

    if isinstance(thing, str):
        the_object = locate(thing, forceload)
        if the_object is None:
            raise ImportError(
#
No Python documentation found for %r."""
                % thing
            )
        return the_object, thing

    name = getattr(thing, "__name__", None)
    if isinstance(name, str):
        return thing, name
    else:
        return thing, str(thing) # HACK
#DIVIDER
def describe(thing: TypeLike) -> str:
    """Produce a short description of the given thing."""
    if inspect.ismodule(thing):
        if thing.__name__ in sys.builtin_module_names:
            return "built-in module " + thing.__name__
        if hasattr(thing, "__path__"):
            return "package " + thing.__name__
        return "module " + thing.__name__
    if inspect.isbuiltin(thing):
        return "built-in function " + thing.__name__
    if inspect.isgetsetdescriptor(thing):
        return f"getset descriptor {thing.__objclass__.__module__}.{thing.__objclass__.__name__}.{thing.__name__}"
    if inspect.ismemberdescriptor(thing):
        return f"member descriptor {thing.__objclass__.__module__}.{thing.__objclass__.__name__}.{thing.__name__}"
    if inspect.isclass(thing):
        return "class " + thing.__name__
    if inspect.isfunction(thing):
        return "function " + thing.__name__
    if inspect.ismethod(thing):
        return "method " + thing.__name__
    return type(thing).__name__
#DIVIDER
def _findclass(func: TypeLike) -> Optional[Module]:
#DIVIDER
    cls = sys.modules.get(func.__module__)
    if cls is None:
        return None
    for name in func.__qualname__.split(".")[:-1]:
        cls = getattr(cls, name)
    if not inspect.isclass(cls):
        return None
    return cls
#DIVIDER
def _finddoc(obj: TypeLike) -> Optional[str]:
#DIVIDER
    if inspect.ismethod(obj):
        name = obj.__func__.__name__
        self = obj.__self__
        if (
            inspect.isclass(self)
            and getattr(getattr(self, name, None), "__func__") is obj.__func__  # noqa
        ):
#DIVIDER
            cls = self
        else:
            cls = self.__class__
    elif inspect.isfunction(obj):
        name = obj.__name__
        cls = _findclass(obj)
        if cls is None or getattr(cls, name) is not obj:
            return None
    elif inspect.isbuiltin(obj):
        name = obj.__name__
        self = obj.__self__
        if inspect.isclass(self) and self.__qualname__ + "." + name == obj.__qualname__:
#DIVIDER
            cls = self
        else:
            cls = self.__class__
#DIVIDER
    elif isinstance(obj, property):
        func = obj.fget
        name = func.__name__
        cls = _findclass(cast(TypeLike, func))
        if cls is None or getattr(cls, name) is not obj:
            return None
    elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
        name = obj.__name__
        cls = obj.__objclass__
        if getattr(cls, name) is not obj:
            return None
        if inspect.ismemberdescriptor(obj):
            slots = getattr(cls, "__slots__", None)
            if isinstance(slots, dict) and name in slots:
                return slots[name]
    else:
        return None
    for base in cls.__mro__:
        try:
            doc = _getowndoc(getattr(base, name))
        except AttributeError:
            continue
        if doc is not None:
            return doc
    return None
#DIVIDER
def _getowndoc(obj: TypeLike) -> str:
#DIVIDER
    inherited from its class."""
    try:
        doc = object.__getattribute__(obj, "__doc__")
        if doc is None:
            return ""  # null safety
        if obj is not type:
            typedoc = type(obj).__doc__
            if isinstance(typedoc, str) and typedoc == doc:
                return ""  # null safety
        return cast(str, doc)
    except AttributeError:
        return ""  # null safety
#
def _getdoc(the_object: TypeLike) -> str:
    """
    Get the documentation string for an object.

    All tabs are expanded to spaces.  To clean up docstrings that are
    indented to line up with blocks of code, any whitespace than can be
    uniformly removed from the second line onwards is removed.
#DIVIDER
    result = _getdoc(the_object) or inspect.getcomments(the_object)
    return result and re.sub("^ *\n", "", result.rstrip()) or ""
#DIVIDER
#     """Split a doc string into a synopsis line (if any) and the rest."""
#DIVIDER

#DIVIDER
def classname(the_object: TypeLike, modname: str) -> str:
    """Get a class name and qualify it with a module name if necessary."""
    name = the_object.__name__
    if the_object.__module__ != modname:
        name = the_object.__module__ + "." + name
    return name
#DIVIDER
def isdata(the_object: Any) -> bool:
#DIVIDER
    return not (
        inspect.ismodule(the_object)
        or inspect.isclass(the_object)
        or inspect.isroutine(the_object)
        or inspect.isframe(the_object)
        or inspect.istraceback(the_object)
        or inspect.iscode(the_object)
    )
#DIVIDER
def _is_bound_method(the_function: object) -> bool:
#DIVIDER
    if inspect.ismethod(the_function):
        return True
    if inspect.isbuiltin(the_function):
        self = getattr(the_function, "__self__", None)
        return not (inspect.ismodule(self) or (self is None))
    return False
#DIVIDER
#     """all methods"""
#DIVIDER

#DIVIDER
def _split_list(
    the_sequence: Sequence[Any], predicate: Callable[[Any], Any]
) -> Tuple[List[Any], List[Any]]:
    """Split sequence s via predicate, and return pair ([true], [false]).

    The return value is a 2-tuple of lists,
        ([x for x in s if predicate(x)],
         [x for x in s if not predicate(x)])
#

Find a Class

    if name in {
        "__author__",
        "__builtins__",
        "__cached__",
        "__credits__",
        "__date__",
        "__doc__",
        "__file__",
        "__spec__",
        "__loader__",
        "__module__",
        "__name__",
        "__package__",
        "__path__",
        "__qualname__",
        "__slots__",
        "__version__",
    }:
        return False
#
    if name.startswith("__") and name.endswith("__"):
        return True
#

Find doc string

    if name.startswith("_") and hasattr(obj, "_fields"):
        return True
    if all_things is not None:
#
        return name in all_things
    return not name.startswith("_")
#

classmethod

def classify_class_attrs(the_object: TypeLike) -> List[Tuple[str, str, type, object]]:
#

classmethod

    results = []
    for (name, kind, cls, value) in inspect.classify_class_attrs(
        cast(type, the_object)
    ):
        if inspect.isdatadescriptor(value):
            kind = "data descriptor"
            if isinstance(value, property) and value.fset is None:
                kind = "readonly property"
        results.append((name, kind, cls, value))
    return results
#

Should be tested before isdatadescriptor().

def sort_attributes(attrs: List[Any], the_object: Union[TypeLike, type]) -> None:
#

Get the documentation string for an object if it is not

    fields = getattr(the_object, "_fields", [])
    try:
        field_order = {name: i - len(fields) for (i, name) in enumerate(fields)}
    except TypeError:
        field_order = {}
#
    def keyfunc(attr: List[Any]) -> Tuple[Any, Any]:
#
doc = _getowndoc(the_object)
if doc is None:

try: doc = _finddoc(the_object) except (AttributeError, TypeError): return “” # null safety if not isinstance(doc, str): return “” # null safety return inspect.cleandoc(doc)

def getdoc(the_object: TypeLike) -> str: Get the doc string or comments for an object.

        return (field_order.get(attr[0], 0), attr[0])

    attrs.sort(key=keyfunc)
#
#

def splitdoc(doc: str) -> Tuple[str, str]:

#     """Get the one-line summary out of a module file."""
#
lines = doc.strip().split("\n")
if len(lines) == 1:
    return lines[0], ""
if len(lines) >= 2 and not lines[1].rstrip():
    return lines[0], "\n".join(lines[2:])
return "", "\n".join(lines)
#
#

Check if an object is of a type that probably means it’s data.

#
#
Returns True if fn is a bound method, regardless of whether
fn was implemented in Python or in C.
#
#

def all_methods(cl: type) -> Dict[str, Any]:

#
methods = {}
for key, value in inspect.getmembers(cl, inspect.isroutine):
    methods[key] = 1
for base in cl.__bases__:
    methods.update(all_methods(base))  # all your base are belong to us
for key in methods.keys():
    methods[key] = getattr(cl, key)
return methods
#
yes = []
# pylint: disable=invalid-name
no = []
for x in the_sequence:

if predicate(x): yes.append(x) else: no.append(x) return yes, no

def visiblename( name: str, all_things: Optional[List[str]] = None, obj: Optional[Any] = None ) -> bool: Decide whether to show documentation on a variable.

#

Certain special names are redundant or internal. XXX Remove initializing?

#

Private names are hidden, but special names are displayed.

#

Namedtuples have public fields and methods with a single leading underscore

#

only document that which the programmer exported in all

#

Wrap inspect.classify_class_attrs, with fixup for data descriptors.

#
#

Sort the attrs list in-place by _fields and then alphabetically by name

#

This allows data descriptors to be ordered according to a _fields attribute if present.

#

Sorting function

#
#

def source_synopsis(file: TextIO) -> str: line = file.readline() while line[:1] == “#” or not line.strip(): line = file.readline() if not line: break line = line.strip() if line[:4] == ‘r”“”’: line = line[1:] if line[:3] == ‘“”“’: line = line[3:] if line[-1:] == “\“: line = line[:-1] while not line.strip(): line = file.readline() if not line: break result = line.split(‘“”“’)[0].strip() else: result = “” # null safety return result

#

def synopsis( filename: str, cache: Dict[str, Any] = {} # noqa - the mutability is on purpose!!! ) -> Optional[str]:

#
mtime = os.stat(filename).st_mtime
lastupdate, result = cache.get(filename, (None, None))
if lastupdate is None or lastupdate < mtime:
    # Look for binary suffixes first, falling back to source.
    if filename.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
        loader_cls = importlib.machinery.SourcelessFileLoader
    elif filename.endswith(tuple(importlib.machinery.EXTENSION_SUFFIXES)):
        loader_cls = importlib.machinery.ExtensionFileLoader
    else:
        loader_cls = None
    # Now handle the choice.
    if loader_cls is None:
        # Must be a source file.
        try:
            file = tokenize.open(filename)
        except OSError:
            # module can't be opened, so skip it
            return None
        # text modules can be directly examined
        with file:
            result = source_synopsis(file)
    else:
        # Must be a binary module, which has to be imported.
        loader = loader_cls("__temp__", filename)
        # XXX We probably don't need to pass in the loader here.
        spec = importlib.util.spec_from_file_location(
            "__temp__", filename, loader=loader
        )
        try:
            module = importlib._bootstrap._load(spec)
        # pylint: disable=broad-except
        except BaseException:
            return None
        del sys.modules["__temp__"]
        result = module.__doc__.splitlines()[0] if module.__doc__ else None
    # Cache the result.
    cache[filename] = (mtime, result)
return cast(str, result)  # hope this is a str?