diff --git a/hyperon_das_atomdb/__init__.py b/hyperon_das_atomdb/__init__.py index 6d1b1457..8d87a24e 100644 --- a/hyperon_das_atomdb/__init__.py +++ b/hyperon_das_atomdb/__init__.py @@ -1,3 +1,7 @@ +"""This module initializes the AtomDB package and imports key components.""" + +# pylint: disable=wrong-import-position + import sys if sys.version_info < (3, 10): diff --git a/hyperon_das_atomdb/adapters/__init__.py b/hyperon_das_atomdb/adapters/__init__.py index 499f2db3..a520b445 100644 --- a/hyperon_das_atomdb/adapters/__init__.py +++ b/hyperon_das_atomdb/adapters/__init__.py @@ -1,3 +1,5 @@ +"""This module imports the InMemoryDB and RedisMongoDB classes and defines the public API.""" + from .ram_only import InMemoryDB from .redis_mongo_db import RedisMongoDB diff --git a/hyperon_das_atomdb/adapters/ram_only.py b/hyperon_das_atomdb/adapters/ram_only.py index af0be409..c8c640c9 100644 --- a/hyperon_das_atomdb/adapters/ram_only.py +++ b/hyperon_das_atomdb/adapters/ram_only.py @@ -13,7 +13,7 @@ from hyperon_das_atomdb.exceptions import AtomDoesNotExist, InvalidOperationException from hyperon_das_atomdb.logger import logger from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher -from hyperon_das_atomdb.utils.patterns import build_patern_keys +from hyperon_das_atomdb.utils.patterns import build_pattern_keys @dataclass @@ -150,7 +150,7 @@ def _delete_templates(self, link_document: dict, targets_hash: List[str]) -> Non template_named_type.remove((link_document[FieldNames.ID_HASH], tuple(targets_hash))) def _add_patterns(self, named_type_hash: str, key: str, targets_hash: List[str]) -> None: - pattern_keys = build_patern_keys([named_type_hash, *targets_hash]) + pattern_keys = build_pattern_keys([named_type_hash, *targets_hash]) for pattern_key in pattern_keys: pattern_key_hash = self.db.patterns.get(pattern_key) @@ -160,7 +160,7 @@ def _add_patterns(self, named_type_hash: str, key: str, targets_hash: List[str]) self.db.patterns[pattern_key] = {(key, tuple(targets_hash))} def _delete_patterns(self, link_document: dict, targets_hash: List[str]) -> None: - pattern_keys = build_patern_keys([link_document[FieldNames.TYPE_NAME_HASH], *targets_hash]) + pattern_keys = build_pattern_keys([link_document[FieldNames.TYPE_NAME_HASH], *targets_hash]) for pattern_key in pattern_keys: pattern = self.db.patterns.get(pattern_key, set()) if len(pattern) > 0: diff --git a/hyperon_das_atomdb/exceptions.py b/hyperon_das_atomdb/exceptions.py index c2483094..90cd0fcf 100644 --- a/hyperon_das_atomdb/exceptions.py +++ b/hyperon_das_atomdb/exceptions.py @@ -1,6 +1,9 @@ -class BaseException(Exception): +"""Custom exceptions for Atom DB""" + + +class AtomDbBaseException(Exception): """ - Base class to exceptions + Base class for Atom DB exceptions """ def __init__(self, message: str, details: str = ""): @@ -10,33 +13,33 @@ def __init__(self, message: str, details: str = ""): super().__init__(self.message, self.details) -class ConnectionMongoDBException(BaseException): - ... # pragma no cover +class ConnectionMongoDBException(AtomDbBaseException): + """Exception raised for errors in the connection to MongoDB.""" -class AtomDoesNotExist(BaseException): - ... # pragma no cover +class AtomDoesNotExist(AtomDbBaseException): + """Exception raised when an atom does not exist.""" -class AddNodeException(BaseException): - ... # pragma no cover +class AddNodeException(AtomDbBaseException): + """Exception raised when adding a node fails.""" -class AddLinkException(BaseException): - ... # pragma no cover +class AddLinkException(AtomDbBaseException): + """Exception raised when adding a link fails.""" -class InvalidOperationException(BaseException): - ... # pragma no cover +class InvalidOperationException(AtomDbBaseException): + """Exception raised for invalid operations.""" -class RetryException(BaseException): - ... # pragma no cover +class RetryException(AtomDbBaseException): + """Exception raised for retryable errors.""" -class InvalidAtomDB(BaseException): - ... # pragma no cover +class InvalidAtomDB(AtomDbBaseException): + """Exception raised for invalid Atom DB operations.""" -class InvalidSQL(BaseException): - ... # pragma no cover +class InvalidSQL(AtomDbBaseException): + """Exception raised for invalid SQL operations.""" diff --git a/hyperon_das_atomdb/index.py b/hyperon_das_atomdb/index.py index 6e9740de..62e1929f 100644 --- a/hyperon_das_atomdb/index.py +++ b/hyperon_das_atomdb/index.py @@ -1,40 +1,46 @@ +"""This module contains the abstract class for index creation and management.""" + from abc import ABC, abstractmethod -from typing import Any, Dict, List, Tuple +from typing import Any from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher class Index(ABC): + """Abstract class for index creation and management.""" + @staticmethod - def generate_index_id(field: str, conditionals: Dict[str, Any]) -> str: + def generate_index_id(field: str, conditionals: dict[str, Any]) -> str: """Generates an index ID based on the field name. Args: field (str): The field name. + conditionals (dict[str, Any]): The conditionals. Returns: str: The index ID. """ + # TODO(angelo,andre): remove '_' from `ExpressionHasher._compute_hash` method? + # pylint: disable=protected-access return ExpressionHasher._compute_hash(f'{field}{conditionals}') @abstractmethod def create( self, atom_type: str, - fields: List[str], + fields: list[str], **kwargs, - ) -> Tuple[str, Any]: + ) -> tuple[str, Any]: """Creates an index on the given field. Args: atom_type (str): Atom's type - fields (List[str]): The fields to create the index on. + fields (list[str]): The fields to create the index on. Returns: - Tuple[str, Any]: Returns the index id and the index properties dict + tuple[str, Any]: Returns the index id and the index properties dict """ - ... # pragma: no cover @abstractmethod def index_exists(self, index_id: str) -> bool: @@ -46,4 +52,3 @@ def index_exists(self, index_id: str) -> bool: Returns: bool: True if the index exists, False otherwise. """ - ... # pragma: no cover diff --git a/hyperon_das_atomdb/logger.py b/hyperon_das_atomdb/logger.py index 9f5ebb0c..d1e4495a 100644 --- a/hyperon_das_atomdb/logger.py +++ b/hyperon_das_atomdb/logger.py @@ -1,45 +1,98 @@ +""" +This module provides a singleton Logger class for logging messages to a file with a specific +format and level. + +The Logger class ensures that only one instance of the logger is created and provides methods +for logging messages at different levels (debug, info, warning, error). The log messages are +written to a file specified by LOG_FILE_NAME with a logging level defined by LOGGING_LEVEL. +""" + import logging LOG_FILE_NAME = "/tmp/das.log" LOGGING_LEVEL = logging.INFO +# pylint: disable=logging-not-lazy + class Logger: + """Singleton Logger class for logging messages to a file.""" + __instance = None @staticmethod - def get_instance(): + def get_instance() -> "Logger": + """Get the singleton instance of the Logger class.""" if Logger.__instance is None: return Logger() return Logger.__instance def __init__(self): + """Initializes the Logger instance and sets up the logging configuration. + + Raises: + Exception: If an attempt is made to re-instantiate the Logger. + """ + if Logger.__instance is not None: + # TODO(angelo,andre): raise a more specific type of exception? + # pylint: disable=broad-exception-raised raise Exception("Invalid re-instantiation of Logger") - else: - logging.basicConfig( - filename=LOG_FILE_NAME, - level=LOGGING_LEVEL, - format="%(asctime)s %(levelname)-8s %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - Logger.__instance = self - - def _prefix(self): + + logging.basicConfig( + filename=LOG_FILE_NAME, + level=LOGGING_LEVEL, + format="%(asctime)s %(levelname)-8s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + Logger.__instance = self + + @staticmethod + def _prefix() -> str: + """Returns a prefix for log messages. + + Returns: + str: The prefix for log messages. + """ return "" - def debug(self, msg): + def debug(self, msg: str) -> None: + """Logs a debug message. + + Args: + msg (str): The message to log. + """ logging.debug(self._prefix() + msg) - def info(self, msg): + def info(self, msg: str) -> None: + """Logs an info message. + + Args: + msg (str): The message to log. + """ logging.info(self._prefix() + msg) - def warning(self, msg): + def warning(self, msg: str) -> None: + """Logs a warning message. + + Args: + msg (str): The message to log. + """ logging.warning(self._prefix() + msg) - def error(self, msg): + def error(self, msg: str) -> None: + """Logs an error message. + + Args: + msg (str): The message to log. + """ logging.error(self._prefix() + msg) -def logger(): +def logger() -> Logger: + """Get the singleton instance of the Logger class. + + Returns: + Logger: The singleton instance of the Logger class. + """ return Logger.get_instance() diff --git a/hyperon_das_atomdb/utils/expression_hasher.py b/hyperon_das_atomdb/utils/expression_hasher.py index 8222d8fa..d48d6ea8 100644 --- a/hyperon_das_atomdb/utils/expression_hasher.py +++ b/hyperon_das_atomdb/utils/expression_hasher.py @@ -1,5 +1,13 @@ +""" +This module provides utility functions for hashing expressions and generating unique identifiers. + +It includes classes for computing hashes of various types of expressions, such as named types, +terminals, and composite expressions. The module uses the MD5 hashing algorithm to generate +hashes and provides methods for creating composite hashes from lists of elements. +""" + from hashlib import md5 -from typing import Any, List +from typing import Any class ExpressionHasher: @@ -20,7 +28,7 @@ def terminal_hash(named_type: str, terminal_name: str) -> str: ) @staticmethod - def expression_hash(named_type_hash: str, elements: List[str]) -> str: + def expression_hash(named_type_hash: str, elements: list[str]) -> str: return ExpressionHasher.composite_hash([named_type_hash, *elements]) @staticmethod @@ -43,7 +51,7 @@ def composite_hash(hash_base: Any) -> str: class StringExpressionHasher: @staticmethod def _compute_hash(text: str) -> str: - return str + return str # TODO(angelo,andre): this seems wrong @staticmethod def named_type_hash(name: str) -> str: @@ -54,11 +62,11 @@ def terminal_hash(named_type: str, terminal_name: str) -> str: return f"<{named_type}: {terminal_name}>" @staticmethod - def expression_hash(named_type_hash: str, elements: List[str]) -> str: + def expression_hash(named_type_hash: str, elements: list[str]) -> str: return f"<{named_type_hash}: {elements}>" @staticmethod - def composite_hash(hash_list: List[str]) -> str: + def composite_hash(hash_list: list[str]) -> str: if len(hash_list) == 1: return hash_list[0] return f"{hash_list}" diff --git a/hyperon_das_atomdb/utils/patterns.py b/hyperon_das_atomdb/utils/patterns.py index 4d32a455..40191430 100644 --- a/hyperon_das_atomdb/utils/patterns.py +++ b/hyperon_das_atomdb/utils/patterns.py @@ -1,18 +1,36 @@ -from typing import List +""" +This module provides utility functions for generating binary matrices and manipulating them. + +It includes functions to generate binary matrices of a given size, multiply binary matrices +by string matrices, and build pattern keys using a list of hashes. These utilities are useful +for various operations involving binary and string data manipulation. +""" from hyperon_das_atomdb.database import WILDCARD from hyperon_das_atomdb.utils.expression_hasher import ExpressionHasher +# TODO(angelo,andre): delete this commented function? # def generate_binary_matrix(numbers: int) -> list: # """This function is more efficient if numbers are greater than 5""" # return list(itertools.product([0, 1], repeat=numbers)) -def generate_binary_matrix(numbers: int) -> list: +def generate_binary_matrix(numbers: int) -> list[list[int]]: + """ + Generate a binary matrix of the given size. + + Args: + numbers (int): The size of the binary matrix to generate. If numbers + is less than or equal to 0, returns a matrix with an empty list. + + Returns: + list[list[int]]: A binary matrix represented as a list of lists, where + each sublist is a row in the matrix. + """ if numbers <= 0: return [[]] smaller_matrix = generate_binary_matrix(numbers - 1) - new_matrix = [] + new_matrix: list[list[int]] = [] for matrix in smaller_matrix: new_matrix.append(matrix + [0]) new_matrix.append(matrix + [1]) @@ -20,9 +38,22 @@ def generate_binary_matrix(numbers: int) -> list: def multiply_binary_matrix_by_string_matrix( - binary_matrix: List[List[int]], string_matrix: List[str] -) -> List[List[str]]: - result_matrix = [] + binary_matrix: list[list[int]], string_matrix: list[str] +) -> list[list[str]]: + """ + Multiply a binary matrix by a string matrix. + + Args: + binary_matrix (list[list[int]]): A binary matrix represented as a list + of lists, where each sublist is a row in the matrix. + string_matrix (list[str]): A list of strings to multiply with the + binary matrix. + + Returns: + list[list[str]]: A matrix represented as a list of lists, where each + sublist is a row in the resulting matrix. + """ + result_matrix: list[list[str]] = [] for binary_row in binary_matrix: result_row = [ string if bit == 1 else WILDCARD for bit, string in zip(binary_row, string_matrix) @@ -31,7 +62,16 @@ def multiply_binary_matrix_by_string_matrix( return result_matrix[:-1] -def build_patern_keys(hash_list: List[str]) -> List[str]: +def build_pattern_keys(hash_list: list[str]) -> list[str]: + """ + Build pattern keys using a list of hashes. + + Args: + hash_list (list[str]): A list of hash strings to build pattern keys from. + + Returns: + list[str]: A list of pattern keys generated from the hash list. + """ binary_matrix = generate_binary_matrix(len(hash_list)) result_matrix = multiply_binary_matrix_by_string_matrix(binary_matrix, hash_list) keys = [ diff --git a/hyperon_das_atomdb/utils/settings.py b/hyperon_das_atomdb/utils/settings.py index c6ce2abf..14458df5 100644 --- a/hyperon_das_atomdb/utils/settings.py +++ b/hyperon_das_atomdb/utils/settings.py @@ -1,3 +1,10 @@ +""" +This module loads configuration settings from a .env file using the dotenv library. + +It reads the key-value pairs from the .env file and stores them in a dictionary named `config`. +These settings can be used throughout the application to configure various parameters. +""" + from dotenv import dotenv_values config = dotenv_values('.env')