Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/blar_graph/graph_construction/core/base_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Warning 🐽 Code Smell

Class Bloat 🕳️

BaseParser has 29 methods, exceeding recommended maintainability thresholds.

Issue explanation

Class BaseParser contains 29 methods, violating SRP and low cohesion, increasing complexity, coupling, and reducing testability and maintainability.

  • Extract import path resolution into a dedicated ImportResolver class
  • Extract function call decomposition into a CallDecomposer class
  • Extract node id generation and post‐processing into a NodeProcessor class
  • Refactor BaseParser into a facade that composes these responsibilities
  • Define clear interfaces for query properties to adhere to open/closed principle
  • Improve cohesion by grouping related methods and reducing class size
Recommended refactor

Recommended refactor 🛠️

class BaseParser:
    language: str
    wildcard: str
    extension: str
    import_path_separator: str
    global_graph_info: GlobalGraphInfo

    def __init__(
        self,
        language: str,
        wildcard: str,
        extension: str,
        import_path_separator: str = ".",
        global_graph_info: GlobalGraphInfo = {"alias": {}},
    ):
        # Code replaced for brevity, see node: 472a2729128fe3031b414e6402720f21

    @staticmethod
    def generate_node_id(path: str, company_id: str):
        # Concatenate path and signature
       # Code replaced for brevity, see node: 3108556b4bfb57efaedf37714335242b
    @staticmethod
    def is_package(path: str) -> bool:
        # Code replaced for brevity, see node: d977042796cec74c7224dfa4bed01e17
    def _post_process_node(self, node: dict, global_graph_info: GlobalGraphInfo):
        # Code replaced for brevity, see node: 7ae3569429ffa85f4a5df094622fe0d2
    def _get_lines_range(self, file_contents, start_byte, end_byte):
        # Code replaced for brevity, see node: 3f303e32db06e98a0624305e717922ed
    def get_node_path(self, node: BaseNode):
        # Code replaced for brevity, see node: 738d42ee74f4c781902328b4c1049e45
    def _get_parent_level(self, node: BaseNode, global_graph_info: GlobalGraphInfo, level: int):
        # Code replaced for brevity, see node: 6825304ade59103def5603aae9a2f925
    def _get_function_calls(self, node: Node, assignments_dict: dict) -> list[str]:
        # Code replaced for brevity, see node: 34e3b5830e85011375c340a3f4ea8a58
    def _get_inheritances(self, node: Node) -> list[str]:
        # Code replaced for brevity, see node: a95a59aeec41713f9fac254efcffa2aa
    def __process_node__(
        self,
        node: TextNode,
        file_path: str,
        file_node_id: str,
        global_graph_info: GlobalGraphInfo,
        assignment_dict: dict,
        document: Document,
        level: int,
    ):
        # Code replaced for brevity, see node: 2e3bae9a23b4b728b3f3f0aa6efd8044
    def remove_extensions(self, file_path):
        # Code replaced for brevity, see node: a4bf964dfce601cd08920cecc9f74170
    def _decompose_function_call(self, call_node: Node, language: Language, parser: Parser):
        # Code replaced for brevity, see node: 08916d1a8aeea7a0ae36a0547a940d32
    def resolve_relative_import_path(self, import_statement, current_file_path, project_root):
        # Code replaced for brevity, see node: 257e9e3543a9657e62ae5ec66471f40e
    def replace_alias_in_import(self, import_statement, project_root):
        # Code replaced for brevity, see node: 225919dda9d235bbc6aaa5eb2189fc3c
    def resolve_import_path(self, import_statement, current_file_directory, project_root):
        # Code replaced for brevity, see node: 3f8ae3d665ec3aaa43de9f31fe0343ac
    def find_module_path(self, module_name, start_dir, project_root):
        # Code replaced for brevity, see node: c05f385d4264ab76b7381d24304d0765
    def check_path_exists(self, path):
        # Code replaced for brevity, see node: e722f80b3a33426de3b0f27c2451a6d9
    def _remove_non_ascii(self, text):
        # Code replaced for brevity, see node: cc90647765990745b40ae8b2d35f6384
    def parse(self, file_path: str, root_path: str, global_graph_info: GlobalGraphInfo, level: int):
        # Code replaced for brevity, see node: 2eba0f794ad6c9ad4923535f31577eff
    @abstractmethod
    def parse_file(self, file_path: str, root_path: str, global_graph_info: GlobalGraphInfo, level: int):
        # Code replaced for brevity, see node: 71c6b8cd46e421d2cf4e6045f3c869ba
    @abstractmethod
    def _get_imports(self, path: str, file_node_id: str, root_path: str) -> dict:
        # Code replaced for brevity, see node: c8057ec9d31d1e65ab380f068ef8c68d
    @property
    @abstractmethod
    def decompose_call_query(self) -> str:
        # Code replaced for brevity, see node: 4a46b6a1edab0b91b04f2a970a41c6c7
    @property
    @abstractmethod
    def assignment_query(self) -> str:
        # Code replaced for brevity, see node: 51d948e5166b9895b65339aeec809dcd
    @property
    @abstractmethod
    def function_call_query(self) -> str:
        # Code replaced for brevity, see node: 2e0ba2166a9522623bb80076215e3476
    @property
    @abstractmethod
    def inheritances_query(self) -> str:
        # Code replaced for brevity, see node: 4aee1f5ac55451b6cdfc6ba486f2590f
    @property
    @abstractmethod
    def self_syntax(self) -> str:
        # Code replaced for brevity, see node: 2b3405f3bc67362ae7432ca541c8f596
    @property
    @abstractmethod
    def scopes_names(self) -> dict[str, List[str]]:
        # Code replaced for brevity, see node: 43b42527de52d16ddc4d8f927dce1d05
    @property
    @abstractmethod
    def relation_types_map(self) -> dict[str, str]:
        # Code replaced for brevity, see node: 2fbd4c3734aebdb3bbd59ccce5bf4de5
    @property
    def signature_identifiers(self) -> dict[str, _SignatureCaptureOptions]:
        # Code replaced for brevity, see node: f10488edbbc98766bfa6a801795a3465


# ImportResolver: handles all import path resolution logic
class ImportResolver:
    def resolve_relative_import_path(self, import_statement, current_file_path, project_root):
        # Logic...
    def replace_alias_in_import(self, import_statement, project_root):
        # Logic...
    def resolve_import_path(self, import_statement, current_file_directory, project_root):
        # Logic...
    def find_module_path(self, module_name, start_dir, project_root):
        # Logic...
    def check_path_exists(self, path):
        # Logic...

# CallDecomposer: handles function calls and signature decomposition
class CallDecomposer:
    def _get_function_calls(self, node: Node, assignments_dict: dict) -> list[str]:
        # Logic...
    def _decompose_function_call(self, call_node: Node, language: Language, parser: Parser):
        # Logic...

# NodeProcessor: handles node id, post‐processing, ranges, paths, inheritances
class NodeProcessor:
    @staticmethod
    def generate_node_id(path: str, company_id: str):
        # Logic...
    @staticmethod
    def is_package(path: str) -> bool:
        # Logic...
    def _post_process_node(self, node: dict, global_graph_info: GlobalGraphInfo):
        # Logic...
    def _get_lines_range(self, file_contents, start_byte, end_byte):
        # Logic...
    def get_node_path(self, node: BaseNode):
        # Logic...
    def _get_parent_level(self, node: BaseNode, global_graph_info: GlobalGraphInfo, level: int):
        # Logic...
    def _get_inheritances(self, node: Node) -> list[str]:
        # Logic...

# Refactored BaseParser using composition
class BaseParserFacade:
    def __init__(self, language: str, wildcard: str, extension: str, import_path_separator: str = ".", global_graph_info: GlobalGraphInfo = {"alias": {}}):
        self.language = language
        self.wildcard = wildcard
        self.extension = extension
        self.import_path_separator = import_path_separator
        self.global_graph_info = global_graph_info
        self.import_resolver = ImportResolver()
        self.call_decomposer = CallDecomposer()
        self.node_processor = NodeProcessor()

    def parse(self, file_path: str, root_path: str, global_graph_info: GlobalGraphInfo, level: int):
        # Logic...

    @abstractmethod
    def parse_file(self, file_path: str, root_path: str, global_graph_info: GlobalGraphInfo, level: int):
        # Logic...

    @abstractmethod
    def _get_imports(self, path: str, file_node_id: str, root_path: str) -> dict:
        # Logic...

    @property
    @abstractmethod
    def decompose_call_query(self) -> str:
        # Logic...

    @property
    @abstractmethod
    def assignment_query(self) -> str:
        # Logic...

    @property
    @abstractmethod
    def function_call_query(self) -> str:
        # Logic...

    @property
    @abstractmethod
    def inheritances_query(self) -> str:
        # Logic...

    @property
    @abstractmethod
    def self_syntax(self) -> str:
        # Logic...

    @property
    @abstractmethod
    def scopes_names(self) -> dict[str, List[str]]:
        # Logic...

    @property
    @abstractmethod
    def relation_types_map(self) -> dict[str, str]:
        # Logic...

    @property
    def signature_identifiers(self) -> dict[str, _SignatureCaptureOptions]:
        # Logic...
*Don't forget to react with a 👍 or 👎 to the comments made by Blar to help us improve.*


class BaseParser(ABC):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Error 🐛 Bug

BaseParser no longer enforces its abstract methods and is directly instantiable.

Issue Explanation
  • Class BaseParser removed ABC inheritance in berrazuriz1/code-base-agent/src/blar_graph/graph_construction/core/base_parser.py.
  • @abstractmethod decorators no longer prevent instantiation without ABCMeta.
  • Direct instantiation of BaseParser succeeds but any call to parse_file will hit an unimplemented method and raise a runtime error.
class BaseParser:
    ...
    @abstractmethod
    def parse_file(self, file_path: str, root_path: str, global_graph_info: GlobalGraphInfo, level: int):
        # Code replaced for brevity

Reply if you have any questions or let me know if I missed something.

Don't forget to react with a 👍 or 👎 to the comments made by Blar to help us improve.

class BaseParser:
language: str
wildcard: str
extension: str
Expand Down