Skip to content

objects

This module defines the documented objects classes.

Note that properties are considered attributes, because they are used like such.

It also defines a convenient Source class to represent source code.

Attribute (Object) ¤

A class to store information about an attribute.

It accepts an additional attr_type argument at instantiation.

Source code in pytkdocs/objects.py
class Attribute(Object):
    """
    A class to store information about an attribute.

    It accepts an additional `attr_type` argument at instantiation.
    """

    possible_name_properties: List[ApplicableNameProperty] = [NAME_SPECIAL, NAME_CLASS_PRIVATE, NAME_PRIVATE]

    def __init__(self, *args, attr_type=None, **kwargs):
        """
        Initialize the object.

        Arguments:
            *args: Arguments passed to the parent class Initialize the object.
            attr_type: The attribute type.
            **kwargs: Arguments passed to the parent class Initialize the object.
        """
        super().__init__(*args, **kwargs)
        self.type = attr_type

possible_name_properties: List[Tuple[str, Callable[[str], bool]]] ¤

The properties that we can apply to the object based on its name.

The applicable properties vary from one subclass of Object to another.

__init__(self, *args, *, attr_type=None, **kwargs) special ¤

Initialize the object.

Parameters:

Name Type Description Default
*args

Arguments passed to the parent class Initialize the object.

()
attr_type

The attribute type.

None
**kwargs

Arguments passed to the parent class Initialize the object.

{}
Source code in pytkdocs/objects.py
def __init__(self, *args, attr_type=None, **kwargs):
    """
    Initialize the object.

    Arguments:
        *args: Arguments passed to the parent class Initialize the object.
        attr_type: The attribute type.
        **kwargs: Arguments passed to the parent class Initialize the object.
    """
    super().__init__(*args, **kwargs)
    self.type = attr_type

Class (Object) ¤

A class to store information about a class.

Source code in pytkdocs/objects.py
class Class(Object):
    """A class to store information about a class."""

    possible_name_properties: List[ApplicableNameProperty] = [NAME_PRIVATE]

    def __init__(self, *args, bases: List[str] = None, **kwargs):
        """
        Initialize the object.

        Arguments:
            *args: Arguments passed to the parent class Initialize the object.
            bases: The base classes (dotted paths).
            **kwargs: Arguments passed to the parent class Initialize the object.
        """
        super().__init__(*args, **kwargs)
        self.bases = bases or ["object"]

possible_name_properties: List[Tuple[str, Callable[[str], bool]]] ¤

The properties that we can apply to the object based on its name.

The applicable properties vary from one subclass of Object to another.

__init__(self, *args, *, bases=None, **kwargs) special ¤

Initialize the object.

Parameters:

Name Type Description Default
*args

Arguments passed to the parent class Initialize the object.

()
bases List[str]

The base classes (dotted paths).

None
**kwargs

Arguments passed to the parent class Initialize the object.

{}
Source code in pytkdocs/objects.py
def __init__(self, *args, bases: List[str] = None, **kwargs):
    """
    Initialize the object.

    Arguments:
        *args: Arguments passed to the parent class Initialize the object.
        bases: The base classes (dotted paths).
        **kwargs: Arguments passed to the parent class Initialize the object.
    """
    super().__init__(*args, **kwargs)
    self.bases = bases or ["object"]

Function (Object) ¤

A class to store information about a function.

It accepts an additional signature argument at instantiation.

Source code in pytkdocs/objects.py
class Function(Object):
    """
    A class to store information about a function.

    It accepts an additional `signature` argument at instantiation.
    """

    possible_name_properties: List[ApplicableNameProperty] = [NAME_PRIVATE]

    def __init__(self, *args, signature=None, **kwargs):
        """
        Initialize the object.

        Arguments:
            *args: Arguments passed to the parent class Initialize the object.
            signature: The function signature.
            **kwargs: Arguments passed to the parent class Initialize the object.
        """
        super().__init__(*args, **kwargs)
        self.signature = signature

possible_name_properties: List[Tuple[str, Callable[[str], bool]]] ¤

The properties that we can apply to the object based on its name.

The applicable properties vary from one subclass of Object to another.

__init__(self, *args, *, signature=None, **kwargs) special ¤

Initialize the object.

Parameters:

Name Type Description Default
*args

Arguments passed to the parent class Initialize the object.

()
signature

The function signature.

None
**kwargs

Arguments passed to the parent class Initialize the object.

{}
Source code in pytkdocs/objects.py
def __init__(self, *args, signature=None, **kwargs):
    """
    Initialize the object.

    Arguments:
        *args: Arguments passed to the parent class Initialize the object.
        signature: The function signature.
        **kwargs: Arguments passed to the parent class Initialize the object.
    """
    super().__init__(*args, **kwargs)
    self.signature = signature

Method (Object) ¤

A class to store information about a method.

It accepts an additional signature argument at instantiation.

Source code in pytkdocs/objects.py
class Method(Object):
    """
    A class to store information about a method.

    It accepts an additional `signature` argument at instantiation.
    """

    possible_name_properties: List[ApplicableNameProperty] = [NAME_SPECIAL, NAME_PRIVATE]

    def __init__(self, *args, signature=None, **kwargs):
        """
        Initialize the object.

        Arguments:
            *args: Arguments passed to the parent class Initialize the object.
            signature: The function signature.
            **kwargs: Arguments passed to the parent class Initialize the object.
        """
        super().__init__(*args, **kwargs)
        self.signature = signature

possible_name_properties: List[Tuple[str, Callable[[str], bool]]] ¤

The properties that we can apply to the object based on its name.

The applicable properties vary from one subclass of Object to another.

__init__(self, *args, *, signature=None, **kwargs) special ¤

Initialize the object.

Parameters:

Name Type Description Default
*args

Arguments passed to the parent class Initialize the object.

()
signature

The function signature.

None
**kwargs

Arguments passed to the parent class Initialize the object.

{}
Source code in pytkdocs/objects.py
def __init__(self, *args, signature=None, **kwargs):
    """
    Initialize the object.

    Arguments:
        *args: Arguments passed to the parent class Initialize the object.
        signature: The function signature.
        **kwargs: Arguments passed to the parent class Initialize the object.
    """
    super().__init__(*args, **kwargs)
    self.signature = signature

Module (Object) ¤

A class to store information about a module.

Source code in pytkdocs/objects.py
class Module(Object):
    """A class to store information about a module."""

    possible_name_properties: List[ApplicableNameProperty] = [NAME_SPECIAL, NAME_PRIVATE]

    @property
    def file_name(self) -> str:
        """
        Return the base name of the module file, without the extension.

        Returns:
            The module file's base name.
        """
        return os.path.splitext(os.path.basename(self.file_path))[0]

    @property
    def name_to_check(self) -> str:  # noqa: D102
        return self.file_name

file_name: str property readonly ¤

Return the base name of the module file, without the extension.

Returns:

Type Description
str

The module file's base name.

name_to_check: str property readonly ¤

Return the attribute to check against name-properties regular expressions (private, class-private, special).

Returns:

Type Description
str

The attribute to check (its name).

possible_name_properties: List[Tuple[str, Callable[[str], bool]]] ¤

The properties that we can apply to the object based on its name.

The applicable properties vary from one subclass of Object to another.

Object ¤

A base class to store information about a Python object.

Each instance additionally stores references to its children, grouped by category.

Source code in pytkdocs/objects.py
class Object(metaclass=ABCMeta):
    """
    A base class to store information about a Python object.

    Each instance additionally stores references to its children, grouped by category.
    """

    possible_name_properties: List[ApplicableNameProperty] = []
    """
    The properties that we can apply to the object based on its name.

    The applicable properties vary from one subclass of `Object` to another.
    """

    def __init__(
        self,
        name: str,
        path: str,
        file_path: str,
        docstring: Optional[str] = "",
        properties: Optional[List[str]] = None,
        source: Optional[Source] = None,
    ) -> None:
        """
        Initialize the object.

        Arguments:
            name: The object's name.
            path: The object's dotted-path.
            file_path: The file path of the object's direct parent module.
            docstring: The object's docstring.
            properties: The object's properties.
            source: The object's source code.
        """
        self.name = name
        """The object's name."""
        self.path = path
        """The object's dotted-path."""
        self.file_path = file_path
        """The file path of the object's direct parent module."""
        self.docstring = docstring
        """The object's docstring."""
        self.docstring_sections: List[Section] = []
        """The object's docstring parsed into sections."""
        self.docstring_errors: List[str] = []
        """The errors detected while parsing the docstring."""
        self.properties = properties or []
        """The object's properties."""
        self.parent: Optional[Object] = None
        """The object's parent (another instance of a subclass of `Object`)."""
        self.source = source
        """The object's source code."""

        self._path_map = {self.path: self}
        self._parsed = False

        self.attributes: List[Attribute] = []
        """The list of all the object's attributes."""
        self.methods: List[Method] = []
        """The list of all the object's methods."""
        self.functions: List[Function] = []
        """The list of all the object's functions."""
        self.modules: List[Module] = []
        """The list of all the object's submodules."""
        self.classes: List[Class] = []
        """The list of all the object's classes."""
        self.children: List[Object] = []
        """The list of all the object's children."""

    def __str__(self) -> str:
        return self.path

    @property
    def category(self) -> str:
        """
        Return the object's category.

        Returns:
            The object's category (module, class, function, method or attribute).
        """
        return self.__class__.__name__.lower()

    @property
    def root(self) -> "Object":
        """
        Return the object's root.

        Returns:
            The object's root (top-most parent).
        """
        obj = self
        while obj.parent:
            obj = obj.parent
        return obj

    @property
    def relative_file_path(self) -> str:
        """
        Return the relative file path of the object.

        It is the relative path to the object's module,
        starting at the path of the top-most package it is contained in.

        For example:

        - package is `a`
        - package absolute path is `/abs/path/to/a`
        - module is `a.b.c`
        - object is `c` or anything defined in `c`
        - relative file path is `a/b/c.py`

        If the relative file path cannot be determined, the value returned is `""` (empty string).

        Returns:
            The path relative to the object's package.
        """
        parts = self.path.split(".")
        namespaces = [".".join(parts[:length]) for length in range(1, len(parts) + 1)]  # noqa: WPS221 (not complex)
        # Iterate through all sub namespaces including the last in case it is a module
        for namespace in namespaces:
            try:  # noqa: WPS229 (more compact)
                importlib.import_module(namespace)
                top_package = sys.modules[namespace]
            except (ModuleNotFoundError, ImportError, KeyError):
                # ImportError: Triggered if the namespace is not importable
                # ModuleNotFoundError: Triggered if the namespace is not a module
                # KeyError: Triggered if the imported package isn't referenced under the same fully qualified name
                # Namespace packages are importable, so this should work for them
                return ""

            try:  # noqa: WPS229 (more compact)
                top_package_path = Path(inspect.getabsfile(top_package)).parent
                return str(Path(self.file_path).relative_to(top_package_path.parent))
            except TypeError:
                # Triggered if getabsfile() can't be found in the case of a Namespace package
                pass  # noqa: WPS420 (passing is the only way)
            except ValueError:
                # Triggered if Path().relative_to can't find an appropriate path
                return ""

        return ""

    @property
    def name_to_check(self) -> str:
        """
        Return the attribute to check against name-properties regular expressions (private, class-private, special).

        Returns:
            The attribute to check (its name).
        """
        return self.name

    @property
    def name_properties(self) -> List[str]:
        """
        Return the object's name properties.

        Returns:
            The object's name properties (private, class-private, special).
        """
        properties = []
        for prop, predicate in self.possible_name_properties:
            if predicate(self.name_to_check):
                properties.append(prop)
        return properties

    @property
    def parent_path(self) -> str:
        """
        Return the parent's path, computed from the current path.

        The parent object path is not used: this property is used to see if an object is really related to another one,
        to add it as a child to the other. When we do that, the child doesn't even have a parent.

        Returns:
            The dotted path of the parent object.
        """
        return self.path.rsplit(".", 1)[0]

    def add_child(self, obj: "Object") -> None:  # noqa: WPS231 (not complex)
        """
        Add an object as a child of this object.

        If the child computed `parent_path` is not equal to this object's path, abort.

        Append the child to the `children` list, and to the right category list.

        Arguments:
            obj: An instance of documented object.
        """
        if obj.parent_path != self.path:
            return

        self.children.append(obj)
        if isinstance(obj, Module):
            self.modules.append(obj)
        elif isinstance(obj, Class):
            self.classes.append(obj)
        elif isinstance(obj, Function):
            self.functions.append(obj)
        elif isinstance(obj, Method):
            self.methods.append(obj)
        elif isinstance(obj, Attribute):
            # Dataclass attributes with default values will already be present in `self.attributes` as they are
            # resolved differently by the python interpreter. As they have a concrete value, they are already present
            # in the "original" class. They should be overridden with the new "dataclass" attribute coming in here
            # (having the "dataclass_field" property set)
            new_attribute_name = obj.name
            for attribute in self.attributes:
                if attribute.name == new_attribute_name:
                    self.attributes.remove(attribute)
            self.attributes.append(obj)
        obj.parent = self

        self._path_map[obj.path] = obj

    def add_children(self, children: List["Object"]) -> None:
        """
        Add a list of objects as children of this object.

        Arguments:
            children: The list of children to add.
        """
        for child in children:
            self.add_child(child)

    def parse_docstring(self, parser: Parser, **context) -> None:
        """
        Parse the docstring of this object.

        Arguments:
            parser: A parser to parse the docstrings.
            **context: Additional context to use when parsing.
        """
        if self.docstring and not self._parsed:
            sections, errors = parser.parse(self.docstring, {"obj": self, **context})
            self.docstring_sections = sections
            self.docstring_errors = errors
            self._parsed = True

    def parse_all_docstrings(self, parser: Parser) -> None:
        """
        Recursively parse the docstring of this object and its children.

        Arguments:
            parser: A parser to parse the docstrings.
        """
        self.parse_docstring(parser)
        for child in self.children:
            child.parse_all_docstrings(parser)

    @lru_cache()
    def has_contents(self) -> bool:
        """
        Tells if the object has "contents".

        An object has contents when:

        - it is the root of the object tree
        - it has a docstring
        - at least one of its children (whatever the depth) has contents

        The value is cached, so this method should be called last, when the tree doesn't change anymore.

        Returns:
            Whether this object has contents or not.
        """
        has_docstring = bool(self.docstring)
        is_root = not self.parent
        children_have_contents = any(child.has_contents() for child in self.children)
        return has_docstring or is_root or children_have_contents

category: str property readonly ¤

Return the object's category.

Returns:

Type Description
str

The object's category (module, class, function, method or attribute).

name_properties: List[str] property readonly ¤

Return the object's name properties.

Returns:

Type Description
List[str]

The object's name properties (private, class-private, special).

name_to_check: str property readonly ¤

Return the attribute to check against name-properties regular expressions (private, class-private, special).

Returns:

Type Description
str

The attribute to check (its name).

parent_path: str property readonly ¤

Return the parent's path, computed from the current path.

The parent object path is not used: this property is used to see if an object is really related to another one, to add it as a child to the other. When we do that, the child doesn't even have a parent.

Returns:

Type Description
str

The dotted path of the parent object.

possible_name_properties: List[Tuple[str, Callable[[str], bool]]] ¤

The properties that we can apply to the object based on its name.

The applicable properties vary from one subclass of Object to another.

relative_file_path: str property readonly ¤

Return the relative file path of the object.

It is the relative path to the object's module, starting at the path of the top-most package it is contained in.

For example:

  • package is a
  • package absolute path is /abs/path/to/a
  • module is a.b.c
  • object is c or anything defined in c
  • relative file path is a/b/c.py

If the relative file path cannot be determined, the value returned is "" (empty string).

Returns:

Type Description
str

The path relative to the object's package.

root: Object property readonly ¤

Return the object's root.

Returns:

Type Description
Object

The object's root (top-most parent).

__init__(self, name, path, file_path, docstring='', properties=None, source=None) special ¤

Initialize the object.

Parameters:

Name Type Description Default
name str

The object's name.

required
path str

The object's dotted-path.

required
file_path str

The file path of the object's direct parent module.

required
docstring Optional[str]

The object's docstring.

''
properties Optional[List[str]]

The object's properties.

None
source Optional[pytkdocs.objects.Source]

The object's source code.

None
Source code in pytkdocs/objects.py
def __init__(
    self,
    name: str,
    path: str,
    file_path: str,
    docstring: Optional[str] = "",
    properties: Optional[List[str]] = None,
    source: Optional[Source] = None,
) -> None:
    """
    Initialize the object.

    Arguments:
        name: The object's name.
        path: The object's dotted-path.
        file_path: The file path of the object's direct parent module.
        docstring: The object's docstring.
        properties: The object's properties.
        source: The object's source code.
    """
    self.name = name
    """The object's name."""
    self.path = path
    """The object's dotted-path."""
    self.file_path = file_path
    """The file path of the object's direct parent module."""
    self.docstring = docstring
    """The object's docstring."""
    self.docstring_sections: List[Section] = []
    """The object's docstring parsed into sections."""
    self.docstring_errors: List[str] = []
    """The errors detected while parsing the docstring."""
    self.properties = properties or []
    """The object's properties."""
    self.parent: Optional[Object] = None
    """The object's parent (another instance of a subclass of `Object`)."""
    self.source = source
    """The object's source code."""

    self._path_map = {self.path: self}
    self._parsed = False

    self.attributes: List[Attribute] = []
    """The list of all the object's attributes."""
    self.methods: List[Method] = []
    """The list of all the object's methods."""
    self.functions: List[Function] = []
    """The list of all the object's functions."""
    self.modules: List[Module] = []
    """The list of all the object's submodules."""
    self.classes: List[Class] = []
    """The list of all the object's classes."""
    self.children: List[Object] = []
    """The list of all the object's children."""

add_child(self, obj) ¤

Add an object as a child of this object.

If the child computed parent_path is not equal to this object's path, abort.

Append the child to the children list, and to the right category list.

Parameters:

Name Type Description Default
obj Object

An instance of documented object.

required
Source code in pytkdocs/objects.py
def add_child(self, obj: "Object") -> None:  # noqa: WPS231 (not complex)
    """
    Add an object as a child of this object.

    If the child computed `parent_path` is not equal to this object's path, abort.

    Append the child to the `children` list, and to the right category list.

    Arguments:
        obj: An instance of documented object.
    """
    if obj.parent_path != self.path:
        return

    self.children.append(obj)
    if isinstance(obj, Module):
        self.modules.append(obj)
    elif isinstance(obj, Class):
        self.classes.append(obj)
    elif isinstance(obj, Function):
        self.functions.append(obj)
    elif isinstance(obj, Method):
        self.methods.append(obj)
    elif isinstance(obj, Attribute):
        # Dataclass attributes with default values will already be present in `self.attributes` as they are
        # resolved differently by the python interpreter. As they have a concrete value, they are already present
        # in the "original" class. They should be overridden with the new "dataclass" attribute coming in here
        # (having the "dataclass_field" property set)
        new_attribute_name = obj.name
        for attribute in self.attributes:
            if attribute.name == new_attribute_name:
                self.attributes.remove(attribute)
        self.attributes.append(obj)
    obj.parent = self

    self._path_map[obj.path] = obj

add_children(self, children) ¤

Add a list of objects as children of this object.

Parameters:

Name Type Description Default
children List[Object]

The list of children to add.

required
Source code in pytkdocs/objects.py
def add_children(self, children: List["Object"]) -> None:
    """
    Add a list of objects as children of this object.

    Arguments:
        children: The list of children to add.
    """
    for child in children:
        self.add_child(child)

has_contents(self) ¤

Tells if the object has "contents".

An object has contents when:

  • it is the root of the object tree
  • it has a docstring
  • at least one of its children (whatever the depth) has contents

The value is cached, so this method should be called last, when the tree doesn't change anymore.

Returns:

Type Description
bool

Whether this object has contents or not.

Source code in pytkdocs/objects.py
@lru_cache()
def has_contents(self) -> bool:
    """
    Tells if the object has "contents".

    An object has contents when:

    - it is the root of the object tree
    - it has a docstring
    - at least one of its children (whatever the depth) has contents

    The value is cached, so this method should be called last, when the tree doesn't change anymore.

    Returns:
        Whether this object has contents or not.
    """
    has_docstring = bool(self.docstring)
    is_root = not self.parent
    children_have_contents = any(child.has_contents() for child in self.children)
    return has_docstring or is_root or children_have_contents

parse_all_docstrings(self, parser) ¤

Recursively parse the docstring of this object and its children.

Parameters:

Name Type Description Default
parser Parser

A parser to parse the docstrings.

required
Source code in pytkdocs/objects.py
def parse_all_docstrings(self, parser: Parser) -> None:
    """
    Recursively parse the docstring of this object and its children.

    Arguments:
        parser: A parser to parse the docstrings.
    """
    self.parse_docstring(parser)
    for child in self.children:
        child.parse_all_docstrings(parser)

parse_docstring(self, parser, **context) ¤

Parse the docstring of this object.

Parameters:

Name Type Description Default
parser Parser

A parser to parse the docstrings.

required
**context

Additional context to use when parsing.

{}
Source code in pytkdocs/objects.py
def parse_docstring(self, parser: Parser, **context) -> None:
    """
    Parse the docstring of this object.

    Arguments:
        parser: A parser to parse the docstrings.
        **context: Additional context to use when parsing.
    """
    if self.docstring and not self._parsed:
        sections, errors = parser.parse(self.docstring, {"obj": self, **context})
        self.docstring_sections = sections
        self.docstring_errors = errors
        self._parsed = True

Source ¤

Helper class to represent source code.

It is simply used to wrap the result of inspect.getsourceslines.

Source code in pytkdocs/objects.py
class Source:
    """
    Helper class to represent source code.

    It is simply used to wrap the result of
    [`inspect.getsourceslines`](https://docs.python.org/3/library/inspect.html#inspect.getsourcelines).
    """

    def __init__(self, lines: Union[str, List[str]], line_start: int) -> None:
        """
        Initialize the object.

        Arguments:
            lines: A list of strings. The strings should have trailing newlines.
            line_start: The line number of where the code starts in the file.
        """
        if isinstance(lines, list):
            code = "".join(lines)
        else:
            code = lines
        self.code = code
        """The code, as a single string."""
        self.line_start = line_start
        """The first line number."""

__init__(self, lines, line_start) special ¤

Initialize the object.

Parameters:

Name Type Description Default
lines Union[str, List[str]]

A list of strings. The strings should have trailing newlines.

required
line_start int

The line number of where the code starts in the file.

required
Source code in pytkdocs/objects.py
def __init__(self, lines: Union[str, List[str]], line_start: int) -> None:
    """
    Initialize the object.

    Arguments:
        lines: A list of strings. The strings should have trailing newlines.
        line_start: The line number of where the code starts in the file.
    """
    if isinstance(lines, list):
        code = "".join(lines)
    else:
        code = lines
    self.code = code
    """The code, as a single string."""
    self.line_start = line_start
    """The first line number."""
Back to top