objects
This module defines the documented objects classes.
- the generic
Object
class - the
Module
class - the
Class
class - the
Method
class - the
Function
class - the
Attribute
class
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 inc
- 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."""