Source code for reducto.items

"""Contains the elements to be extracted from a source file. """

from typing import Optional, Union
import ast


[docs]class Item: """Base class for the items to be extracted from an ast parsed source file. A subclass of this Item corresponds to an ast Node. Notes ----- The nodes are inserted as an attribute instead of doing it on the initialization to simplify the unit tests. """ def __init__(self, name: str, start: int = 0, end: int = 0) -> None: """ Parameters ---------- name : str The name of the item. start : int Line where the item starts in the file. end : int Line where the item ends in the file. """ self._node: Optional[ast.AST] = None self._name = name self._start = start self._end = end self._docstrings: int = 0 self._get_docstrings_called: bool = False self._comments = 0 self._blank_lines = 0 def __repr__(self) -> str: return f"{self.__class__.__name__}({self.name}[{self.start}, {self.end}])" @property def node(self) -> ast.AST: """Returns the node itself. Returns ------- node : ast.AST """ return self._node # type: ignore[return-value] @node.setter def node(self, node_: ast.AST) -> None: self._node = node_ @property def name(self) -> str: """Name the node. Returns ------- name : str """ return self._name @property def start(self) -> int: """Line of the source file where the item starts. Returns ------- start : int """ return self._start @property def end(self) -> int: """Line of the source file where the item ends. Returns ------- end : int """ return self._end def __len__(self) -> int: """Computes the total number of lines of the function.""" return self.end - self.start def __lt__(self, other: Union["Item", int]) -> bool: """Lower than operator to allow the objects to be sorted in a list. Parameters ---------- other : Item Item or subclass of it. """ if isinstance(other, Item): other_start = other.start elif isinstance(other, int): other_start = other else: msg = ( f"Operator defined only for {self.__class__.__name__}" f" instances. You gave: {type(other)}." ) raise TypeError(msg) return self.start < other_start def __ge__(self, other: Union["Item", int]) -> bool: return not self < other def __contains__(self, item: int) -> bool: """To check if a given line is contained in the item or not. Parameters ---------- item : int Line position to check. Returns ------- contained : bool Returns True if the line is between the bounds of the functions. """ return self.start <= item <= self.end @property def docstrings(self) -> int: """Number of lines which are docstring in the item. Returns ------- docstrings : int """ return self._docstrings @docstrings.setter def docstrings(self, docs: int): self._docstrings = docs @property def comments(self) -> int: """Number of lines which are comments in the item. Returns ------- comments : int """ return self._comments @comments.setter def comments(self, cmnt: int): self._comments = cmnt @property def blank_lines(self) -> int: """Number of lines which are blank lines in the item. Returns ------- blank_lines : int """ return self._blank_lines @blank_lines.setter def blank_lines(self, blnk: int) -> None: self._blank_lines = blnk @property def source_lines(self) -> int: """Computes the total number of lines of the item. Returns ------- source_lines : int The total number of lines is the len of the function minus docstrings, comment lines and blank lines. """ return len(self) - self.docstrings - self.comments - self.blank_lines
[docs]class FunctionDef(Item): """Implementation of an ast.FunctionDef. No distinction to an AsyncFunctionDef is made. """ def __init__(self, name: str, start: int = 0, end: int = 0) -> None: super().__init__(name, start=start, end=end)
[docs] def get_docstrings(self) -> int: """Obtain the number of lines which are docstring inside the function. The method must be called once a node is already registered. Returns ------- docs : int See Also -------- get_docstring_lines """ if not self._get_docstrings_called: self.docstrings = get_docstring_lines(self.node) return self.docstrings
[docs]class MethodDef(FunctionDef): """Equivalent to a FunctionDef. Defined in case a distinction between functions and methods is implemented. NOT USED. """ def __init__(self, name: str, start: int = 0, end: int = 0) -> None: super().__init__(name, start=start, end=end)
[docs]def get_docstring_lines(node: Union[ast.Module, ast.FunctionDef, ast.AST]) -> int: r"""Obtains the number of lines which are docstrings. Uses ast.get_docstring to extract the docstrings of an ast node. Parameters ---------- node : Union[ast.Module, ast.FunctionDef] Node which may be a container of docstrings. Only defined for Module and FunctionDed. Returns ------- docstring_lines : int Number of lines in the function or module which are docstrings. Notes ----- Reminder: - '''first ''' 1 - '''second ''' 2 - ''' third''' 1 - ''' fourth ''' 1 - '''docs ''' 4 """ docs = ast.get_docstring(node) try: docstrings = len(docs.split("\n")) # type: ignore[union-attr] except AttributeError: # When there are no docstrings, returns None. docstrings = 0 return docstrings