diff --git a/README.md b/README.md index 2ae7529b..2bc0fecc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ This repo introduces a method to represent a local code repository as a graph structure. The objective is to allow an LLM to traverse this graph to understand the code logic and flow. Providing the LLM with the power to debug, refactor, and optimize queries. +# Supported Languages + +- Python +- JavaScript +- TypeScript +- Ruby +- Go +- C# + # Example This graph was generated from the code in this repository. diff --git a/blarify/code_hierarchy/languages/FoundRelationshipScope.py b/blarify/code_hierarchy/languages/FoundRelationshipScope.py index 1a33ddb7..806242a4 100644 --- a/blarify/code_hierarchy/languages/FoundRelationshipScope.py +++ b/blarify/code_hierarchy/languages/FoundRelationshipScope.py @@ -1,13 +1,15 @@ from dataclasses import dataclass -from tree_sitter import Node -from blarify.graph.relationship.relationship_type import RelationshipType -from typing import Optional +from typing import Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from tree_sitter import Node + from blarify.graph.relationship.relationship_type import RelationshipType @dataclass class FoundRelationshipScope: - node_in_scope: Optional[Node] - relationship_type: RelationshipType + node_in_scope: Optional["Node"] + relationship_type: "RelationshipType" def __str__(self) -> str: return f"FoundRelationshipScope({self.node_in_scope}, {self.relationship_type})" diff --git a/blarify/code_hierarchy/languages/__init__.py b/blarify/code_hierarchy/languages/__init__.py index 95207dd3..ce3036a8 100644 --- a/blarify/code_hierarchy/languages/__init__.py +++ b/blarify/code_hierarchy/languages/__init__.py @@ -4,3 +4,5 @@ from .typescript_definitions import TypescriptDefinitions from .ruby_definitions import RubyDefinitions from .fallback_definitions import FallbackDefinitions +from .csharp_definitions import CsharpDefinitions +from .go_definitions import GoDefinitions diff --git a/blarify/code_hierarchy/languages/csharp_definitions.py b/blarify/code_hierarchy/languages/csharp_definitions.py new file mode 100644 index 00000000..84c0d1b8 --- /dev/null +++ b/blarify/code_hierarchy/languages/csharp_definitions.py @@ -0,0 +1,80 @@ +from blarify.code_hierarchy.languages.FoundRelationshipScope import FoundRelationshipScope +from .language_definitions import LanguageDefinitions +from blarify.graph.relationship import RelationshipType + +import tree_sitter_c_sharp as tscsharp +from tree_sitter import Language, Parser + +from typing import Optional, Set, Dict + +from blarify.graph.node import NodeLabels +from tree_sitter import Node +from blarify.graph.node import Node as GraphNode + + +class CsharpDefinitions(LanguageDefinitions): + def get_language_name() -> str: + return "csharp" + + def get_parsers_for_extensions() -> Dict[str, Parser]: + return { + ".cs": Parser(Language(tscsharp.language())), + } + + def should_create_node(node: Node) -> bool: + return LanguageDefinitions._should_create_node_base_implementation( + node, + [ + "method_declaration", + "class_declaration", + "interface_declaration", + "constructor_declaration", + "record_declaration", + ], + ) + + def get_identifier_node(node: Node) -> Node: + return LanguageDefinitions._get_identifier_node_base_implementation(node) + + def get_body_node(node: Node) -> Node: + return LanguageDefinitions._get_body_node_base_implementation(node) + + def get_relationship_type(node: GraphNode, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + return CsharpDefinitions._find_relationship_type( + node_label=node.label, + node_in_point_reference=node_in_point_reference, + ) + + def get_node_label_from_type(type: str) -> NodeLabels: + return { + "class_declaration": NodeLabels.CLASS, + "method_declaration": NodeLabels.FUNCTION, + "interface_declaration": NodeLabels.CLASS, + "constructor_declaration": NodeLabels.FUNCTION, + "record_declaration": NodeLabels.CLASS, + }[type] + + def get_language_file_extensions() -> Set[str]: + return {".cs"} + + def _find_relationship_type(node_label: str, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + relationship_types = CsharpDefinitions._get_relationship_types_by_label() + relevant_relationship_types = relationship_types.get(node_label, {}) + + return LanguageDefinitions._traverse_and_find_relationships( + node_in_point_reference, relevant_relationship_types + ) + + def _get_relationship_types_by_label() -> dict[str, RelationshipType]: + return { + NodeLabels.CLASS: { + "object_creation_expression": RelationshipType.INSTANTIATES, + "using_directive": RelationshipType.IMPORTS, + "variable_declaration": RelationshipType.TYPES, + "parameter": RelationshipType.TYPES, + "base_list": RelationshipType.INHERITS, + }, + NodeLabels.FUNCTION: { + "invocation_expression": RelationshipType.CALLS, + }, + } diff --git a/blarify/code_hierarchy/languages/go_definitions.py b/blarify/code_hierarchy/languages/go_definitions.py new file mode 100644 index 00000000..8d72c6ab --- /dev/null +++ b/blarify/code_hierarchy/languages/go_definitions.py @@ -0,0 +1,82 @@ +from blarify.code_hierarchy.languages.FoundRelationshipScope import FoundRelationshipScope +from .language_definitions import LanguageDefinitions +from blarify.graph.relationship import RelationshipType + +import tree_sitter_go as tsgo +from tree_sitter import Language, Parser + +from typing import Optional, Set, Dict + +from blarify.graph.node import NodeLabels +from tree_sitter import Node +from blarify.graph.node import Node as GraphNode + + +class GoDefinitions(LanguageDefinitions): + def get_language_name() -> str: + return "go" + + def get_parsers_for_extensions() -> Dict[str, Parser]: + return { + ".go": Parser(Language(tsgo.language())), + } + + def should_create_node(node: Node) -> bool: + return LanguageDefinitions._should_create_node_base_implementation( + node, + ["type_declaration", "method_declaration", "function_declaration"], + ) + + def get_identifier_node(node: Node) -> Node: + print("NODE IDENTIFIER", node.type) + if node.type == "type_declaration": + for child in node.named_children: + if child.type == "type_spec" or "type_alias": + node = child + + return LanguageDefinitions._get_identifier_node_base_implementation(node) + + def get_body_node(node: Node) -> Node: + if node.type == "type_declaration": + for child in node.named_children: + if child.type == "type_spec": + node = child + + return LanguageDefinitions._get_body_node_base_implementation(node) + + def get_relationship_type(node: GraphNode, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + return GoDefinitions._find_relationship_type( + node_label=node.label, + node_in_point_reference=node_in_point_reference, + ) + + def get_node_label_from_type(type: str) -> NodeLabels: + return { + "type_declaration": NodeLabels.CLASS, + "method_declaration": NodeLabels.FUNCTION, + "function_declaration": NodeLabels.FUNCTION, + }[type] + + def get_language_file_extensions() -> Set[str]: + return {".go"} + + def _find_relationship_type(node_label: str, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + relationship_types = GoDefinitions._get_relationship_types_by_label() + relevant_relationship_types = relationship_types.get(node_label, {}) + + return LanguageDefinitions._traverse_and_find_relationships( + node_in_point_reference, relevant_relationship_types + ) + + def _get_relationship_types_by_label() -> dict[str, RelationshipType]: + return { + NodeLabels.CLASS: { + "import_declaration": RelationshipType.IMPORTS, + "field_declaration": RelationshipType.TYPES, + "composite_literal": RelationshipType.INSTANTIATES, + }, + NodeLabels.FUNCTION: { + "import_declaration": RelationshipType.IMPORTS, + "call_expression": RelationshipType.CALLS, + }, + } diff --git a/blarify/code_hierarchy/languages/language_definitions.py b/blarify/code_hierarchy/languages/language_definitions.py index 8ea5f4db..17dd90fc 100644 --- a/blarify/code_hierarchy/languages/language_definitions.py +++ b/blarify/code_hierarchy/languages/language_definitions.py @@ -48,10 +48,8 @@ def get_identifier_node(node: Node) -> Node: def _get_identifier_node_base_implementation(node: Node) -> Node: if identifier := node.child_by_field_name("name"): return identifier - - raise IdentifierNodeNotFound( - f"No identifier node found for node type {node.type} at {node.start_point} - {node.end_point}" - ) + error = f"No identifier node found for node type {node.type} at {node.start_point} - {node.end_point}" + raise IdentifierNodeNotFound(error) @staticmethod @abstractmethod diff --git a/blarify/code_references/__init__.py b/blarify/code_references/__init__.py index 59a1ccec..48d85c0a 100644 --- a/blarify/code_references/__init__.py +++ b/blarify/code_references/__init__.py @@ -1,3 +1 @@ -from .lsp_caller import LspCaller from .lsp_helper import LspQueryHelper, FileExtensionNotSupported -from .implemented_lsp import ImplementedLsp diff --git a/blarify/code_references/implemented_lsp.py b/blarify/code_references/implemented_lsp.py deleted file mode 100644 index 9c0c901d..00000000 --- a/blarify/code_references/implemented_lsp.py +++ /dev/null @@ -1,7 +0,0 @@ -from enum import Enum - - -class ImplementedLsp(Enum): - JEDI_LANGUAGE_SERVER = "jedi-language-server" - TYPESCRIPT_LANGUAGE_SERVER = "typescript-language-server" - SOLARGRAPH = "solargraph" diff --git a/blarify/code_references/lsp_caller.py b/blarify/code_references/lsp_caller.py deleted file mode 100644 index ebd67f66..00000000 --- a/blarify/code_references/lsp_caller.py +++ /dev/null @@ -1,251 +0,0 @@ -import time -from websockets import ConnectionClosedError -import websockets.sync.client as ws -import json -from blarify.logger import Logger -import logging - -logger = logging.getLogger(__name__) - -LANGUAGES_ID_MAP = { - ".tsx": "typescriptreact", - ".ts": "typescript", - ".jsx": "javascriptreact", - ".js": "javascript", -} - - -class LspCaller: - root_uri: str - host: str - port: int - websocket: ws.ClientConnection - unmatched_responses: dict - lsp_server_name: str - connection_retries: int - - def __init__( - self, root_uri: str, host="localhost", port=5000, log=False, lsp_server_name="", connection_retries: int = 3 - ): - self.host = host or "localhost" - self.port = port or 5000 - self.root_uri = root_uri - self.websocket = None - self.unmatched_responses = {} - self.lsp_server_name = lsp_server_name - self.connection_retries = connection_retries - - self._id = 0 - - @property - def id(self) -> int: - self._id += 1 - return self._id - - def connect(self, retries: int = 0) -> None: - uri = f"ws://{self.host}:{self.port}" - uri += self._get_query_params() - try: - self.websocket = ws.connect(uri) - except ConnectionRefusedError: - logger.info(f"Connection refused to {uri}") - logger.info(f"Retrying {retries}/{self.connection_retries}") - if retries < self.connection_retries: - time.sleep(1) - self.connect(retries + 1) - - def _get_query_params(self) -> str: - if self.lsp_server_name: - return f"?name={self.lsp_server_name}" - return "" - - def initialize(self) -> None: - initialize_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "initialize", - "params": { - "processId": None, - "rootUri": self.root_uri, - "capabilities": {}, - "initializationOptions": { - "completion": {"disableSnippets": False, "resolveEagerly": False, "ignorePatterns": []}, - "diagnostics": {"enable": False, "didOpen": True, "didChange": False, "didSave": False}, - "hover": { - "enable": False, - }, - }, - }, - } - self.send_request(initialize_request) - - def send_request(self, request: dict) -> dict: - request_id = request["id"] - - # Send the request - self.websocket.send(json.dumps(request)) - - return self.get_response(request_id, request) - - def send_notification(self, notification: dict) -> None: - self.websocket.send(json.dumps(notification)) - - def get_response(self, request_id: str, request: dict) -> dict: - # Check the response cache first - if request_id in self.unmatched_responses: - return self.unmatched_responses.pop(request_id) - - retries = 0 - while retries <= self.connection_retries: - try: - response = self.websocket.recv() - response = json.loads(response) - - if response.get("method") == "window/logMessage": - self.log(response) - - response_id = response.get("id") - - if response_id == request_id: - return response - else: - self.unmatched_responses[response_id] = response - except ConnectionClosedError: - retries += 1 - logger.info(f"Connection lost. Retrying ({retries}/{self.connection_retries})...") - self.connect() - self.websocket.send(json.dumps(request)) - if retries > self.connection_retries: - raise ConnectionClosedError("Failed to reconnect after maximum retries.") - - def get_document_symbols(self, document_uri: str) -> dict: - document_symbol_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/documentSymbol", - "params": {"textDocument": {"uri": document_uri}}, - } - return self.send_request(document_symbol_request).get("result") - - def get_definition(self, document_uri: str, position: dict) -> dict: - definition_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/definition", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - }, - } - - result = self.send_request(definition_request).get("result") - if result: - return result[0] - return None - - def get_declaration(self, document_uri: str, position: dict) -> dict: - declaration_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/declaration", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - }, - } - result = self.send_request(declaration_request).get("result") - if result: - return result[0] - return None - - def get_references(self, document_uri: str, position: dict) -> dict: - reference_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/references", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - "context": {"includeDeclaration": False}, - "workDoneToken": 1, - }, - } - return self.send_request(reference_request).get("result") - - def get_selection_range(self, document_uri: str, position: dict) -> dict: - selection_range_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/selectionRange", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - }, - } - return self.send_request(selection_range_request).get("result") - - def get_document_link(self, document_uri: str) -> dict: - document_link_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/documentLink", - "params": {"textDocument": {"uri": document_uri}}, - } - return self.send_request(document_link_request).get("result") - - def did_open(self, document_uri: str, text: str, extension: str) -> None: - language_id = LANGUAGES_ID_MAP.get(extension, "javascript") - did_open_notification = { - "jsonrpc": "2.0", - "id": 400, - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": document_uri, - "languageId": language_id, - "version": 1, - "text": text, - }, - }, - } - self.send_notification(did_open_notification) - - def shutdown_exit_close(self) -> None: - try: - self.shutdown() - self.exit() - self.close() - except ConnectionClosedError: - pass - except Exception as e: - logger.info(f"Error closing connection {e}") - - def shutdown(self) -> None: - shutdown_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "shutdown", - "params": None, - } - self.send_request(shutdown_request) - - def exit(self) -> None: - exit_request = {"jsonrpc": "2.0", "method": "exit"} - self.websocket.send(json.dumps(exit_request)) - - def close(self) -> None: - self.websocket.close() - - def map_reference(self, reference: dict) -> dict: - return { - "uri": reference["uri"], - "range": reference["range"], - } - - def log(self, message: dict) -> None: - if self.log: - self.pretty_print(message) - - def pretty_print(self, message: dict) -> None: - ## print formatted json - Logger.log(json.dumps(message, indent=2)) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index f9af6db1..74f63cb8 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -1,16 +1,27 @@ from typing import Optional -from .lsp_caller import LspCaller + +from multilspy import SyncLanguageServer + +from blarify.utils.path_calculator import PathCalculator + from .types.Reference import Reference from blarify.graph.node import DefinitionNode -from .implemented_lsp import ImplementedLsp from blarify.code_hierarchy.languages import ( PythonDefinitions, JavascriptDefinitions, RubyDefinitions, TypescriptDefinitions, LanguageDefinitions, + CsharpDefinitions, + GoDefinitions, ) -from collections import ChainMap + +from multilspy.multilspy_config import MultilspyConfig +from multilspy.multilspy_logger import MultilspyLogger + +import logging + +logger = logging.getLogger(__name__) class FileExtensionNotSupported(Exception): @@ -19,64 +30,129 @@ class FileExtensionNotSupported(Exception): class LspQueryHelper: root_uri: str - lsp_callers: dict[ImplementedLsp, LspCaller] + language_to_lsp_server: dict[str, SyncLanguageServer] + + LSP_USAGES = 0 def __init__(self, root_uri: str, host: Optional[str] = None, port: Optional[int] = None): self.root_uri = root_uri - self.lsp_callers = self._create_lsp_callers(host=host, port=port) - self.extension_to_lsp_server = self._create_extensions_to_lsp_servers() - - def _create_lsp_callers(self, host: str, port: int) -> dict[ImplementedLsp, LspCaller]: - return { - lsp_server: LspCaller(root_uri=self.root_uri, lsp_server_name=lsp_server.value, host=host, port=port) - for lsp_server in ImplementedLsp - } - - def _create_extensions_to_lsp_servers(self): - return ChainMap( - self._create_extension_to_lsp_servers(PythonDefinitions, ImplementedLsp.JEDI_LANGUAGE_SERVER), - self._create_extension_to_lsp_servers(JavascriptDefinitions, ImplementedLsp.TYPESCRIPT_LANGUAGE_SERVER), - self._create_extension_to_lsp_servers(TypescriptDefinitions, ImplementedLsp.TYPESCRIPT_LANGUAGE_SERVER), - self._create_extension_to_lsp_servers(RubyDefinitions, ImplementedLsp.SOLARGRAPH), - ) + self.entered_lsp_servers = {} + self.language_to_lsp_server = {} - def _create_extension_to_lsp_servers(self, language_definitions: LanguageDefinitions, lsp_server: ImplementedLsp): - return {extension: lsp_server for extension in language_definitions.get_language_file_extensions()} + def _get_language_definition_for_extension(self, extension: str) -> LanguageDefinitions: + if extension in PythonDefinitions.get_language_file_extensions(): + return PythonDefinitions + elif extension in JavascriptDefinitions.get_language_file_extensions(): + return JavascriptDefinitions + elif extension in TypescriptDefinitions.get_language_file_extensions(): + return TypescriptDefinitions + elif extension in RubyDefinitions.get_language_file_extensions(): + return RubyDefinitions + elif extension in CsharpDefinitions.get_language_file_extensions(): + return CsharpDefinitions + elif extension in GoDefinitions.get_language_file_extensions(): + return GoDefinitions + else: + raise FileExtensionNotSupported(f'File extension "{extension}" is not supported)') + + def _create_lsp_server(self, language_definitions: LanguageDefinitions): + language = language_definitions.get_language_name() + + config = MultilspyConfig.from_dict({"code_language": language}) + + logger = MultilspyLogger() + lsp = SyncLanguageServer.create(config, logger, PathCalculator.uri_to_path(self.root_uri)) + return lsp def start(self) -> None: - for lsp_caller in self.lsp_callers.values(): - lsp_caller.connect() - lsp_caller.initialize() + """ + DEPRECATED, LSP servers are started on demand + """ - def initialize_directory(self, file) -> None: - lsp_caller = self.get_lsp_caller_for_extension(file.extension) - lsp_caller.did_open(document_uri=file.uri_path, text=self._read_file(file.path), extension=file.extension) + def _get_or_create_lsp_server(self, extension): + language_definitions = self._get_language_definition_for_extension(extension) + language = language_definitions.get_language_name() - def get_lsp_caller_for_extension(self, extension: str) -> LspCaller: - try: - return self.lsp_callers[self.extension_to_lsp_server[extension]] - except KeyError: - raise FileExtensionNotSupported(f'File extension "{extension}" is not supported') + if language in self.language_to_lsp_server: + return self.language_to_lsp_server[language] + else: + new_lsp = self._create_lsp_server(language_definitions) + self.language_to_lsp_server[language] = new_lsp + self._initialize_lsp_server(language, new_lsp) + return new_lsp - def _read_file(self, file_path: str) -> str: - try: - with open(file_path, "r") as file: - return file.read() - except UnicodeDecodeError: - return "" + def _initialize_lsp_server(self, language, lsp): + context = lsp.start_server() + context.__enter__() + self.entered_lsp_servers[language] = context + + def initialize_directory(self, file) -> None: + """ + DEPRECATED, LSP servers are started on demand + """ def get_paths_where_node_is_referenced(self, node: DefinitionNode) -> list[Reference]: - lsp_caller = self.get_lsp_caller_for_extension(node.extension) - references = lsp_caller.get_references(node.path, node.definition_range.start_dict) + server = self._get_or_create_lsp_server(node.extension) + references = self._request_references_with_fallback(node, server) + if not references: return [] + return [Reference(reference) for reference in references] + def _request_references_with_fallback(self, node, lsp): + try: + references = lsp.request_references( + file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), + line=node.definition_range.start_dict["line"], + column=node.definition_range.start_dict["character"], + ) + + except (TimeoutError, ConnectionResetError) as e: + logger.warning(f"Error requesting references: {e}, attempting to restart LSP server") + + self._restart_lsp_for_extension(node) + lsp = self._get_or_create_lsp_server(node.extension) + references = lsp.request_references( + file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), + line=node.definition_range.start_dict["line"], + column=node.definition_range.start_dict["character"], + ) + + return references + + def _restart_lsp_for_extension(self, node): + language_definitions = self._get_language_definition_for_extension(node.extension) + + new_lsp = self._create_lsp_server(language_definitions) + + logger.warning("Restarting LSP server") + try: + self._initialize_lsp_server(language_definitions.get_language_name(), new_lsp) + self.language_to_lsp_server[language_definitions.get_language_name()] = new_lsp + logger.warning("LSP server restarted") + except ConnectionResetError: + logger.error("Connection reset error") + def get_definition_path_for_reference(self, reference: Reference) -> str: - lsp_caller = self.get_lsp_caller_for_extension(".py") - definition = lsp_caller.get_definition(reference.uri, reference.start_dict) - return definition["uri"] if definition else "" + lsp_caller = self._get_or_create_lsp_server(".py") + definitions = lsp_caller.request_definition( + file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=reference.uri), + line=reference.range.start.line, + column=reference.range.start.character, + ) + + if not definitions: + return "" + + return definitions[0]["uri"] def shutdown_exit_close(self) -> None: - for lsp_caller in self.lsp_callers.values(): - lsp_caller.shutdown_exit_close() + for lsp in self.entered_lsp_servers.values(): + try: + lsp.__exit__(None, None, None) + except Exception as e: + logger.error(f"Error shutting down LSP: {e}") + self.entered_lsp_servers = {} + self.language_to_lsp_server = {} + logger.info("LSP servers have been shut down") diff --git a/blarify/db_managers/neo4j_manager.py b/blarify/db_managers/neo4j_manager.py index 85771345..e3c67d4f 100644 --- a/blarify/db_managers/neo4j_manager.py +++ b/blarify/db_managers/neo4j_manager.py @@ -12,14 +12,22 @@ class Neo4jManager: - entityId: str - repoId: str + entity_id: str + repo_id: str driver: Driver - def __init__(self, repoId: str = None, entityId: str = None, max_connections: int = 50, create_index=False): - uri = os.getenv("NEO4J_URI") - user = os.getenv("NEO4J_USERNAME") - password = os.getenv("NEO4J_PASSWORD") + def __init__( + self, + repo_id: str = None, + entity_id: str = None, + max_connections: int = 50, + uri: str = None, + user: str = None, + password: str = None, + ): + uri = uri or os.getenv("NEO4J_URI") + user = user or os.getenv("NEO4J_USERNAME") + password = password or os.getenv("NEO4J_PASSWORD") retries = 3 for attempt in range(retries): @@ -32,270 +40,28 @@ def __init__(self, repoId: str = None, entityId: str = None, max_connections: in else: raise e - if create_index: - self.create_indexes_and_constraints() - self.repoId = repoId if repoId is not None else "default_repo" - self.entityId = entityId if entityId is not None else "default_user" - - def create_indexes_and_constraints(self): - self.create_function_name_index() - self.create_node_id_index() - self.create_entityId_index() - self.create_unique_constraint() + self.repo_id = repo_id if repo_id is not None else "default_repo" + self.entity_id = entity_id if entity_id is not None else "default_user" - def query(self, query: str, query_params: dict = {}, result_format: str = "data"): - with self.driver.session() as session: - result = session.run(query, query_params) - if result_format == "graph": - return result.graph() - return result.data() + def close(self): + # Close the connection to the database + self.driver.close() def save_graph(self, nodes: List[Any], edges: List[Any]): self.create_nodes(nodes) self.create_edges(edges) - def create_function_name_index(self): - # Creates a fulltext index on the name and path properties of the nodes - with self.driver.session() as session: - node_query = """ - CREATE FULLTEXT INDEX functionNames IF NOT EXISTS FOR (n:CLASS|FUNCTION|FILE) ON EACH [n.name, n.path, n.node_id] - """ - session.run(node_query) - - def create_node_id_index(self): - with self.driver.session() as session: - node_query = """ - CREATE INDEX node_id_NODE IF NOT EXISTS FOR (n:NODE) ON (n.node_id) - """ - session.run(node_query) - - def create_entityId_index(self): - with self.driver.session() as session: - user_query = """ - CREATE INDEX entityId_INDEX IF NOT EXISTS FOR (n:NODE) ON (n.entityId) - """ - session.run(user_query) - - def create_unique_constraint(self): - with self.driver.session() as session: - constraint_query = """ - CREATE CONSTRAINT user_node_unique IF NOT EXISTS FOR (n:NODE) - REQUIRE (n.entityId, n.node_id) IS UNIQUE - """ - session.run(constraint_query) - - def close(self): - # Close the connection to the database - self.driver.close() - def create_nodes(self, nodeList: List[Any]): # Function to create nodes in the Neo4j database with self.driver.session() as session: - session.write_transaction(self._create_nodes_txn, nodeList, 10, repoId=self.repoId, entityId=self.entityId) + session.write_transaction( + self._create_nodes_txn, nodeList, 100, repoId=self.repo_id, entityId=self.entity_id + ) def create_edges(self, edgesList: List[Any]): # Function to create edges between nodes in the Neo4j database with self.driver.session() as session: - session.write_transaction(self._create_edges_txn, edgesList, 10, entityId=self.entityId) - - def format_query(self, query: str): - # Function to format the query to be used in the fulltext index - special_characters = [ - "+", - "-", - "&&", - "||", - "!", - "(", - ")", - "{", - "}", - "[", - "]", - "^", - '"', - "~", - "*", - "?", - ":", - "\\", - "/", - ] - for character in special_characters: - query = query.replace(character, f"\\{character}") - return query - - def get_node_by_id(self, node_id: str): - query = """ - MATCH (n) - WHERE n.node_id = $node_id - return n - """ - with self.driver.session() as session: - result = session.run(query, {"node_id": node_id}) - node = result.data()[0]["n"] - neighbours = self.get_1_hop_neighbours_and_relations(node["node_id"]) - node_result = { - "node_id": node.get("node_id"), - "node_name": node.get("name"), - "file_path": node.get("file_path"), - "start_line": node.get("start_line"), - "end_line": node.get("end_line"), - "text": node.get("text"), - } - return node_result, neighbours - - def get_whole_graph(self, result_format: str = "data"): - query = "match (n {repoId: $repoId})-[r]-(m) return n, m, r" - with self.driver.session() as session: - result = session.run(query, repoId=self.repoId) - if result_format == "graph": - return result.graph() - return result.data() - - def get_all_user_nodes(self, result_format: str = "data"): - query = "match (n {entityId: $entityId})-[r]-(m) return n, m, r" - with self.driver.session() as session: - result = session.run(query, entityId=self.entityId) - if result_format == "graph": - return result.graph() - return result.data() - - def search_code(self, query: str): - # Function to get code from the Neo4j database based on a keyword query - formatted_query = self.format_query(query) - node_query = """ - CALL db.index.fulltext.queryNodes("functionNames", $formatted_query) YIELD node, score - where node.repoId = $repoId - RETURN node.node_id, node.name, node.file_path, score - """ - with self.driver.session() as session: - result = session.run(node_query, formatted_query=f"*{formatted_query}", repoId=self.repoId) - data_result = result.data() - if data_result is None: - result = session.run(node_query, formatted_query=formatted_query, repoId=self.repoId) - data_result = result.data() - data_result = [ - { - "node_id": record["node.node_id"], - "node_name": record["node.name"], - "file_path": record["node.file_path"], - "score": record["score"], - } - for record in data_result - ] - return data_result - - def get_code(self, query: str): - # Function to get code from the Neo4j database based on a keyword query - formatted_query = self.format_query(query) - node_query = """ - CALL db.index.fulltext.queryNodes("functionNames", $formatted_query) YIELD node, score - where node.repoId = $repoId - RETURN node.text, node.node_id, node.name, node.file_path, node.start_line, node.end_line, score - """ - with self.driver.session() as session: - result = session.run(node_query, formatted_query=f"*{formatted_query}", repoId=self.repoId) - first_result = result.peek() - if first_result is None: - result = session.run(node_query, formatted_query=formatted_query, repoId=self.repoId) - first_result = result.peek() - if first_result is None: - return None, None - neighbours = self.get_1_hop_neighbours_and_relations(first_result["node.node_id"]) - return first_result, neighbours - - def get_1_hop_neighbours_and_relations(self, node_id: str): - with self.driver.session() as session: - result = session.run( - """ - MATCH (p {node_id: $node_id})-[r]->(p2) - RETURN - type(r) as relationship_type, - p2.node_id AS node_id, - p2.name AS node_name, - labels(p2) AS node_type - """, - node_id=node_id, - ) - data = result.data() - nodes_info = [ - { - "node_id": record["node_id"], - "node_name": record["node_name"], - "node_type": record["node_type"], - "relationship_type": record["relationship_type"], - } - for record in data - ] - return nodes_info - - def get_n_hop_neighbours(self, node_id: str, num_hops: int): - # Function to get code from the Neo4j database based on a keyword query - with self.driver.session() as session: - result = session.run( - """ - MATCH (p {node_id: $node_id}) - CALL apoc.neighbors.byhop(p, ">", $num_hops) - YIELD nodes - UNWIND nodes AS all_nodes - RETURN all_nodes.node_id AS node_id, all_nodes.name AS function_name, labels(all_nodes) AS labels - """, - node_id=node_id, - num_hops=num_hops, - ) - data = result.data() - # Construct list of objects containing node_id and function_name - nodes_info = [ - { - "node_id": record["node_id"], - "function_name": record["function_name"], - "labels": record["labels"], - } - for record in data - ] - return nodes_info - - def get_incoming_neighbours( - self, node_id: str | None = None, path: str | None = None, relationship_types: list | None = None - ): - with self.driver.session() as session: - if path: - query = f""" - MATCH p1 {{ path:{path} }}<-[r]-(p2) - """ - elif node_id: - query = """ - MATCH (p {node_id: $node_id})<-[r]-(p2) - """ - - if relationship_types: - if len(relationship_types) == 1: - query += f"WHERE type(r) = '{relationship_types[0]}'" - else: - relationship_types_str = ", ".join([f"'{r}'" for r in relationship_types]) - query += f"WHERE type(r) IN [{relationship_types_str}]" - - query += """ - RETURN - type(r) as relationship_type, - p2.node_id AS node_id, - p2.name AS node_name, - labels(p2) AS node_type - """ - - result = session.run(query, node_id=node_id) - data = result.data() - nodes_info = [ - { - "node_id": record["node_id"], - "node_name": record["node_name"], - "node_type": record["node_type"], - "relationship_type": record["relationship_type"], - } - for record in data - ] - return nodes_info + session.write_transaction(self._create_edges_txn, edgesList, 100, entityId=self.entity_id) @staticmethod def _create_nodes_txn(tx, nodeList: List[Any], batch_size: int, repoId: str, entityId: str): diff --git a/blarify/examples/graph_builder.py b/blarify/examples/graph_builder.py new file mode 100644 index 00000000..e2c026a8 --- /dev/null +++ b/blarify/examples/graph_builder.py @@ -0,0 +1,33 @@ +from blarify.prebuilt.graph_builder import GraphBuilder +from blarify.db_managers.neo4j_manager import Neo4jManager + +import dotenv +import os + + +def build(root_path: str = None): + graph_builder = GraphBuilder(root_path=root_path, extensions_to_skip=[".json"], names_to_skip=["__pycache__"]) + graph = graph_builder.build() + + relationships = graph.get_relationships_as_objects() + nodes = graph.get_nodes_as_objects() + + save_to_neo4j(relationships, nodes) + + +def save_to_neo4j(relationships, nodes): + graph_manager = Neo4jManager(repo_id="repo", entity_id="organization") + + print(f"Saving graph with {len(nodes)} nodes and {len(relationships)} relationships") + graph_manager.save_graph(nodes, relationships) + graph_manager.close() + + +if __name__ == "__main__": + import logging + + logging.basicConfig(level=logging.INFO) + + dotenv.load_dotenv() + root_path = os.getenv("ROOT_PATH") + build(root_path=root_path) diff --git a/blarify/logger.py b/blarify/logger.py index 5754ac43..50c44c11 100644 --- a/blarify/logger.py +++ b/blarify/logger.py @@ -8,4 +8,4 @@ class Logger: @staticmethod def log(message: str) -> None: # if os.getenv("DEBUG"): - print(message) + logger.info(message) diff --git a/blarify/main.py b/blarify/main.py index fa8d359f..c945e977 100644 --- a/blarify/main.py +++ b/blarify/main.py @@ -11,6 +11,12 @@ import dotenv import os +import logging + +URI = os.getenv("NEO4J_URI") +USER = os.getenv("NEO4J_USERNAME") +PASSWORD = os.getenv("NEO4J_PASSWORD") + def main(root_path: str = None, blarignore_path: str = None): lsp_query_helper = LspQueryHelper(root_uri=root_path) @@ -18,8 +24,7 @@ def main(root_path: str = None, blarignore_path: str = None): lsp_query_helper.start() project_files_iterator = ProjectFilesIterator( - root_path=root_path, - blarignore_path=blarignore_path, + root_path=root_path, blarignore_path=blarignore_path, extensions_to_skip=[".json", ".xml"] ) ProjectFileStats(project_files_iterator).print(limit=10) @@ -30,9 +35,7 @@ def main(root_path: str = None, blarignore_path: str = None): entity_id = "test" graph_manager = Neo4jManager(repoId, entity_id) - graph_creator = ProjectGraphCreator( - root_path, lsp_query_helper, project_files_iterator, GraphEnvironment("dev", "MAIN", root_path) - ) + graph_creator = ProjectGraphCreator(root_path, lsp_query_helper, project_files_iterator) graph = graph_creator.build() @@ -151,16 +154,20 @@ def main_diff_with_previous( nodes = graph.get_nodes_as_objects() print(f"Saving graph with {len(nodes)} nodes and {len(relationships)} relationships") + + # batch create nodes and relationships + graph_manager.save_graph(nodes, relationships) graph_manager.close() lsp_query_helper.shutdown_exit_close() if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) dotenv.load_dotenv() root_path = os.getenv("ROOT_PATH") blarignore_path = os.getenv("BLARIGNORE_PATH") - # main(root_path=root_path, blarignore_path=blarignore_path) + main(root_path=root_path, blarignore_path=blarignore_path) # main_diff( # file_diffs=[ # FileDiff( @@ -186,32 +193,39 @@ def main_diff_with_previous( print("Updating") # main_update( # updated_files=[ - # UpdatedFile( - # path="file:///home/juan/devel/blar/git-webhook-tester/app/test/main.py", - # ), + # # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/schemas.py"), + # # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/models.py"), + # ], + # root_uri=root_path, + # blarignore_path=blarignore_path, + # ) + # main_update( + # updated_files=[ + # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/schemas.py"), + # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/models.py"), # ], # root_uri=root_path, # blarignore_path=blarignore_path, # ) - main_diff_with_previous( - file_diffs=[ - FileDiff( - path="file:///home/juan/devel/blar/blar-qa/blar/agents/tasks.py", - diff_text="diff+++", - change_type=ChangeType.MODIFIED, - ), - ], - root_uri=root_path, - blarignore_path=blarignore_path, - previous_node_states=[ - PreviousNodeState( - "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_task", - open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), - ), - PreviousNodeState( - "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_taski", - open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), - ), - ], - ) + # main_diff_with_previous( + # file_diffs=[ + # FileDiff( + # path="file:///home/juan/devel/blar/blar-qa/blar/agents/tasks.py", + # diff_text="diff+++", + # change_type=ChangeType.MODIFIED, + # ), + # ], + # root_uri=root_path, + # blarignore_path=blarignore_path, + # previous_node_states=[ + # PreviousNodeState( + # "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_task", + # open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), + # ), + # PreviousNodeState( + # "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_taski", + # open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), + # ), + # ], + # ) diff --git a/blarify/prebuilt/__init__.py b/blarify/prebuilt/__init__.py new file mode 100644 index 00000000..13644e62 --- /dev/null +++ b/blarify/prebuilt/__init__.py @@ -0,0 +1,3 @@ +from blarify.prebuilt.graph_builder import GraphBuilder + +__all__ = ["GraphBuilder"] diff --git a/blarify/prebuilt/graph_builder.py b/blarify/prebuilt/graph_builder.py new file mode 100644 index 00000000..13271297 --- /dev/null +++ b/blarify/prebuilt/graph_builder.py @@ -0,0 +1,64 @@ +from blarify.code_references.lsp_helper import LspQueryHelper +from blarify.graph.graph import Graph +from blarify.project_file_explorer.project_files_iterator import ProjectFilesIterator +from blarify.project_graph_creator import ProjectGraphCreator + + +class GraphBuilder: + def __init__( + self, + root_path: str, + extensions_to_skip: list[str] = None, + names_to_skip: list[str] = None, + only_hierarchy: bool = False, + ): + """ + A class responsible for constructing a graph representation of a project's codebase. + + Args: + root_path: Root directory path of the project to analyze + extensions_to_skip: File extensions to exclude from analysis (e.g., ['.md', '.txt']) + names_to_skip: Filenames/directory names to exclude from analysis (e.g., ['venv', 'tests']) + + Example: + builder = GraphBuilder( + "/path/to/project", + extensions_to_skip=[".json"], + names_to_skip=["__pycache__"] + ) + project_graph = builder.build() + + """ + + self.root_path = root_path + self.extensions_to_skip = extensions_to_skip or [] + self.names_to_skip = names_to_skip or [] + + self.repo_id = "REPO" + self.entity_id = "BLARIFY" + self.only_hierarchy = only_hierarchy + + def build(self) -> Graph: + lsp_query_helper = self._get_started_lsp_query_helper() + project_files_iterator = self._get_project_files_iterator() + + graph_creator = ProjectGraphCreator(self.root_path, lsp_query_helper, project_files_iterator) + + if self.only_hierarchy: + graph = graph_creator.build_hierarchy_only() + else: + graph = graph_creator.build() + + lsp_query_helper.shutdown_exit_close() + + return graph + + def _get_project_files_iterator(self): + return ProjectFilesIterator( + root_path=self.root_path, extensions_to_skip=self.extensions_to_skip, names_to_skip=self.names_to_skip + ) + + def _get_started_lsp_query_helper(self): + lsp_query_helper = LspQueryHelper(root_uri=self.root_path) + lsp_query_helper.start() + return lsp_query_helper diff --git a/blarify/project_file_explorer/project_files_iterator.py b/blarify/project_file_explorer/project_files_iterator.py index 43999bbb..83c7fd75 100644 --- a/blarify/project_file_explorer/project_files_iterator.py +++ b/blarify/project_file_explorer/project_files_iterator.py @@ -8,6 +8,7 @@ class ProjectFilesIterator: root_path: str paths_to_skip: List[str] names_to_skip: List[str] + extensions_to_skip: List[str] max_file_size_mb: int def __init__( @@ -15,12 +16,14 @@ def __init__( root_path: str, paths_to_skip: List[str] = None, names_to_skip: List[str] = None, + extensions_to_skip: List[str] = None, blarignore_path: str = None, max_file_size_mb: int = 10, ): self.paths_to_skip = paths_to_skip or [] self.root_path = root_path self.names_to_skip = names_to_skip or [] + self.extensions_to_skip = extensions_to_skip or [] self.max_file_size_mb = max_file_size_mb if blarignore_path: @@ -83,7 +86,9 @@ def _should_skip(self, path: str) -> bool: is_file_size_too_big = os.path.getsize(path) > self._mb_to_bytes(self.max_file_size_mb) - return is_basename_in_names_to_skip or is_path_in_paths_to_skip or is_file_size_too_big + is_extension_to_skip = any(path.endswith(extension) for extension in self.extensions_to_skip) + + return is_basename_in_names_to_skip or is_path_in_paths_to_skip or is_file_size_too_big or is_extension_to_skip def _mb_to_bytes(self, mb: int) -> int: return 1024 * 1024 * mb diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index b9e83039..594a04a3 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -1,4 +1,5 @@ import time +from blarify.code_hierarchy.languages.go_definitions import GoDefinitions from blarify.code_references import LspQueryHelper, FileExtensionNotSupported from blarify.project_file_explorer import ProjectFilesIterator from blarify.graph.node import NodeLabels, NodeFactory @@ -11,16 +12,21 @@ TypescriptDefinitions, FallbackDefinitions, RubyDefinitions, + CsharpDefinitions, ) from typing import List, TYPE_CHECKING from blarify.logger import Logger +from blarify.graph.graph_environment import GraphEnvironment +import logging + +logger = logging.getLogger(__name__) + if TYPE_CHECKING: from blarify.graph.node import FolderNode from blarify.project_file_explorer import File, Folder from blarify.graph.node import Node, FileNode from blarify.graph.relationship import Relationship - from blarify.graph.graph_environment import GraphEnvironment class ProjectGraphCreator: @@ -35,6 +41,8 @@ class ProjectGraphCreator: ".ts": TypescriptDefinitions, ".tsx": TypescriptDefinitions, ".rb": RubyDefinitions, + ".cs": CsharpDefinitions, + ".go": GoDefinitions, } def __init__( @@ -47,7 +55,7 @@ def __init__( self.root_path = root_path self.lsp_query_helper = lsp_query_helper self.project_files_iterator = project_files_iterator - self.graph_environment = graph_environment + self.graph_environment = graph_environment or GraphEnvironment("blarify", "repo", self.root_path) self.graph = Graph() @@ -55,9 +63,9 @@ def build(self) -> Graph: self.create_code_hierarchy() # TODO: Implement a better way to wait for the lsp to finish - Logger.log("Waiting for LSP to finish") - time.sleep(15) - Logger.log("LSP finished") + logger.info("Waiting for LSP to finish") + # time.sleep(15) + logger.info("LSP finished") self.create_relationships_from_references_for_files() return self.graph @@ -74,7 +82,7 @@ def create_code_hierarchy(self): end_time = time.time() execution_time = end_time - start_time - Logger.log(f"Execution time of create_code_hierarchy: {execution_time:.2f} seconds") + logger.info(f"Execution time of create_code_hierarchy: {execution_time:.2f} seconds") def process_folder(self, folder: "Folder") -> None: folder_node = self.add_or_get_folder_node(folder) @@ -190,8 +198,13 @@ def _log_if_multiple_of_x(self, index: int, x: int, text: str) -> None: if index % x == 0: Logger.log(text) - def create_node_relationships(self, node: "Node", tree_sitter_helper: TreeSitterHelper) -> List["Relationship"]: + def create_node_relationships( + self, + node: "Node", + tree_sitter_helper: TreeSitterHelper, + ) -> List["Relationship"]: references = self.lsp_query_helper.get_paths_where_node_is_referenced(node) + relationships = RelationshipCreator.create_relationships_from_paths_where_node_is_referenced( references=references, node=node, graph=self.graph, tree_sitter_helper=tree_sitter_helper ) diff --git a/blarify/project_graph_diff_creator.py b/blarify/project_graph_diff_creator.py index f4a0a264..33a1281b 100644 --- a/blarify/project_graph_diff_creator.py +++ b/blarify/project_graph_diff_creator.py @@ -143,14 +143,10 @@ def _mark_deleted_nodes_with_label(self, previous_node_states: List[PreviousNode for previous_node in previous_node_states: equivalent_node: DefinitionNode = self.graph.get_node_by_relative_id(previous_node.relative_id) if not equivalent_node: - print(f"Node not found: {previous_node.relative_id}") deleted_node = NodeFactory.create_deleted_node( graph_environment=self.pr_environment, ) - print(f"Creating deleted node: {deleted_node.hashed_id}") - print(deleted_node.as_object()) - self.graph.add_node(deleted_node) self.deleted_nodes_added_paths.append(deleted_node.path) diff --git a/blarify/utils/path_calculator.py b/blarify/utils/path_calculator.py index aa6ed707..c814bb90 100644 --- a/blarify/utils/path_calculator.py +++ b/blarify/utils/path_calculator.py @@ -23,3 +23,10 @@ def compute_relative_path_with_prefix(pure_path: str, root_path: str) -> str: @staticmethod def get_parent_folder_path(file_path): return "/".join(file_path.split("/")[:-1]) + + @staticmethod + def get_relative_path_from_uri(root_uri: str, uri: str) -> str: + root_path = PathCalculator.uri_to_path(root_uri) + path = PathCalculator.uri_to_path(uri) + + return os.path.relpath(path, root_path) diff --git a/docs/quickstart.md b/docs/quickstart.md index 188087aa..751fd241 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -4,21 +4,14 @@ Welcome to Blarify! This guide will help you get started using Blarify to visual ## Prerequisites -- Python (>=3.10,<=3.12.8) -- [lsp-ws-proxy](https://github.com/qualified/lsp-ws-proxy) +- Python (>=3.10,<=3.14) - A graph database instance (we recommend using [FalkorDB](https://falkordb.com/) or [AuraDB](https://neo4j.com/product/auradb/)) -and one or more of the following: - -- solargraph (for Ruby support) -- jedi-language-server (for Python support) -- typescript-language-server (for TypeScript/JavaScript support) - ## Installation First set up your virtual environment: ```bash -python3.11 -m venv .venv +python -m venv .venv source .venv/bin/activate ``` @@ -28,47 +21,30 @@ git clone git@github.com:blarApp/blarify.git pip install blarify/ ``` -Start the lsp-ws-proxy with the language servers you want to use on port 5000: -``` -./lsp-ws-proxy/target/debug/lsp-ws-proxy --listen 5000 -- solargraph stdio -- jedi-language-server -- typescript-language-server --stdio -``` - ## Usage ```python PATH_TO_YOUR_PROJECT = "/path/to/your/project/" ``` -Start the lsp query helper, this will be used to query the language server for information about your codebase +Import GraphBuilder from the prebuilt module ```python -lsp_query_helper = LspQueryHelper(root_uri=PATH_TO_YOUR_PROJECT) -lsp_query_helper.start() +from blarify.prebuilt.graph_builder import GraphBuilder ``` +Create the graph builder +You can skip files or directories by passing them in the extensions_to_skip or names_to_skip parameters -ProjectFilesIterator will iterate over all the files in your project, you can skip files or directories by passing them in the names_to_skip parameter ```python -project_files_iterator = ProjectFilesIterator( - root_path=PATH_TO_YOUR_PROJECT, - names_to_skip=[".git", ".idea", ".vscode", "__pycache__", ".pytest_cache"], -) +graph_builder = GraphBuilder(root_path=PATH_TO_YOUR_PROJECT, extensions_to_skip=[".json"], names_to_skip=["__pycache__"]) ``` -If you are using Ruby, remove the Gemfile from the project to avoid problems with the language server +Build the graph ```python -FileRemover.soft_delete_if_exists(PATH_TO_YOUR_PROJECT, "Gemfile") -``` - -Create the graph creator and build -```python -graph_creator = ProjectGraphCreator( - PATH_TO_YOUR_PROJECT, lsp_query_helper, project_files_iterator -) - -graph = graph_creator.build() +graph = graph_builder.build() ``` This will return a graph object that contains all the nodes and relationships in your codebase @@ -115,25 +91,53 @@ this will return a list of dictionaries with the following structure } ``` -Close the lsp query helper and graph manager - +Example using Neo4j ```python +relationships = graph.get_relationships_as_objects() +nodes = graph.get_nodes_as_objects() + +# This assumes you have the following environment variables set: +# NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD +graph_manager = Neo4jManager(repo_id="repo", entity_id="organization") + +graph_manager.save_graph(nodes, relationships) graph_manager.close() -lsp_query_helper.shutdown_exit_close() + ``` +## Complete Example -Example using Neo4j ```python -# Set up the graph manager, this will be used to save the graph to neo4j -repoId = "name_of_your_repo" -entity_id = "owner_of_the_repo" -graph_manager = Neo4jManager(repoId, entity_id) +# Taken from blarify/examples/graph_builder.py +from blarify.prebuilt.graph_builder import GraphBuilder +from blarify.db_managers.neo4j_manager import Neo4jManager -relationships = graph.get_relationships_as_objects() -nodes = graph.get_nodes_as_objects() +import dotenv +import os -graph_manager.save_graph(nodes, relationships) + +def build(root_path: str = None): + graph_builder = GraphBuilder(root_path=root_path, extensions_to_skip=[".json"], names_to_skip=["__pycache__"]) + graph = graph_builder.build() + + relationships = graph.get_relationships_as_objects() + nodes = graph.get_nodes_as_objects() + + save_to_neo4j(relationships, nodes) + + +def save_to_neo4j(relationships, nodes): + graph_manager = Neo4jManager(repo_id="repo", entity_id="organization") + + print(f"Saving graph with {len(nodes)} nodes and {len(relationships)} relationships") + graph_manager.save_graph(nodes, relationships) + graph_manager.close() + + +if __name__ == "__main__": + dotenv.load_dotenv() + root_path = os.getenv("ROOT_PATH") + build(root_path=root_path) ``` diff --git a/log b/log deleted file mode 100644 index c24c6e1d..00000000 --- a/log +++ /dev/null @@ -1,187 +0,0 @@ -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] Resolving TypeScript version from path "/usr/lib/node_modules/typescript/lib/tsserver.js"...'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] Resolving TypeScript version from path \"/usr/lib/node_modules/typescript/lib/tsserver.js\"..." - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] Reading version from package.json at "/usr/lib/node_modules/typescript/package.json"'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] Reading version from package.json at \"/usr/lib/node_modules/typescript/package.json\"" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] Resolved TypeScript version to "5.6.3"'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] Resolved TypeScript version to \"5.6.3\"" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 3, 'message': 'Using Typescript version (bundled) 5.6.3 from path "/usr/lib/node_modules/typescript/lib/tsserver.js"'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 3, - "message": "Using Typescript version (bundled) 5.6.3 from path \"/usr/lib/node_modules/typescript/lib/tsserver.js\"" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] [tsclient] Starting tsserver'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] [tsclient] Starting tsserver" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] [tsclient] Starting tsserver'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] [tsclient] Starting tsserver" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] onInitialize result {\n "capabilities": {\n "textDocumentSync": 2,\n "completionProvider": {\n "triggerCharacters": [\n ".",\n "\\"",\n "\'",\n "/",\n "@",\n "<"\n ],\n "resolveProvider": true\n },\n "codeActionProvider": true,\n "codeLensProvider": {\n "resolveProvider": true\n },\n "definitionProvider": true,\n "documentFormattingProvider": true,\n "documentRangeFormattingProvider": true,\n "documentHighlightProvider": true,\n "documentSymbolProvider": true,\n "executeCommandProvider": {\n "commands": [\n "_typescript.applyWorkspaceEdit",\n "_typescript.applyCodeAction",\n "_typescript.applyRefactoring",\n "_typescript.configurePlugin",\n "_typescript.organizeImports",\n "_typescript.applyRenameFile",\n "_typescript.goToSourceDefinition"\n ]\n },\n "hoverProvider": true,\n "inlayHintProvider": true,\n "linkedEditingRangeProvider": false,\n "renameProvider": true,\n "referencesProvider": true,\n "selectionRangeProvider": true,\n "signatureHelpProvider": {\n "triggerCharacters": [\n "(",\n ",",\n "<"\n ],\n "retriggerCharacters": [\n ")"\n ]\n },\n "workspaceSymbolProvider": true,\n "implementationProvider": true,\n "typeDefinitionProvider": true,\n "foldingRangeProvider": true,\n "semanticTokensProvider": {\n "documentSelector": null,\n "legend": {\n "tokenTypes": [\n "class",\n "enum",\n "interface",\n "namespace",\n "typeParameter",\n "type",\n "parameter",\n "variable",\n "enumMember",\n "property",\n "function",\n "member"\n ],\n "tokenModifiers": [\n "declaration",\n "static",\n "async",\n "readonly",\n "defaultLibrary",\n "local"\n ]\n },\n "full": true,\n "range": true\n },\n "workspace": {\n "fileOperations": {\n "willRename": {\n "filters": [\n {\n "scheme": "file",\n "pattern": {\n "glob": "**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}",\n "matches": "file"\n }\n },\n {\n "scheme": "file",\n "pattern": {\n "glob": "**",\n "matches": "folder"\n }\n }\n ]\n }\n }\n }\n }\n}'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] onInitialize result {\n \"capabilities\": {\n \"textDocumentSync\": 2,\n \"completionProvider\": {\n \"triggerCharacters\": [\n \".\",\n \"\\\"\",\n \"'\",\n \"/\",\n \"@\",\n \"<\"\n ],\n \"resolveProvider\": true\n },\n \"codeActionProvider\": true,\n \"codeLensProvider\": {\n \"resolveProvider\": true\n },\n \"definitionProvider\": true,\n \"documentFormattingProvider\": true,\n \"documentRangeFormattingProvider\": true,\n \"documentHighlightProvider\": true,\n \"documentSymbolProvider\": true,\n \"executeCommandProvider\": {\n \"commands\": [\n \"_typescript.applyWorkspaceEdit\",\n \"_typescript.applyCodeAction\",\n \"_typescript.applyRefactoring\",\n \"_typescript.configurePlugin\",\n \"_typescript.organizeImports\",\n \"_typescript.applyRenameFile\",\n \"_typescript.goToSourceDefinition\"\n ]\n },\n \"hoverProvider\": true,\n \"inlayHintProvider\": true,\n \"linkedEditingRangeProvider\": false,\n \"renameProvider\": true,\n \"referencesProvider\": true,\n \"selectionRangeProvider\": true,\n \"signatureHelpProvider\": {\n \"triggerCharacters\": [\n \"(\",\n \",\",\n \"<\"\n ],\n \"retriggerCharacters\": [\n \")\"\n ]\n },\n \"workspaceSymbolProvider\": true,\n \"implementationProvider\": true,\n \"typeDefinitionProvider\": true,\n \"foldingRangeProvider\": true,\n \"semanticTokensProvider\": {\n \"documentSelector\": null,\n \"legend\": {\n \"tokenTypes\": [\n \"class\",\n \"enum\",\n \"interface\",\n \"namespace\",\n \"typeParameter\",\n \"type\",\n \"parameter\",\n \"variable\",\n \"enumMember\",\n \"property\",\n \"function\",\n \"member\"\n ],\n \"tokenModifiers\": [\n \"declaration\",\n \"static\",\n \"async\",\n \"readonly\",\n \"defaultLibrary\",\n \"local\"\n ]\n },\n \"full\": true,\n \"range\": true\n },\n \"workspace\": {\n \"fileOperations\": {\n \"willRename\": {\n \"filters\": [\n {\n \"scheme\": \"file\",\n \"pattern\": {\n \"glob\": \"**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}\",\n \"matches\": \"file\"\n }\n },\n {\n \"scheme\": \"file\",\n \"pattern\": {\n \"glob\": \"**\",\n \"matches\": \"folder\"\n }\n }\n ]\n }\n }\n }\n }\n}" - } -} -{'jsonrpc': '2.0', 'id': 1, 'result': {'capabilities': {'textDocumentSync': 2, 'completionProvider': {'triggerCharacters': ['.', '"', "'", '/', '@', '<'], 'resolveProvider': True}, 'codeActionProvider': True, 'codeLensProvider': {'resolveProvider': True}, 'definitionProvider': True, 'documentFormattingProvider': True, 'documentRangeFormattingProvider': True, 'documentHighlightProvider': True, 'documentSymbolProvider': True, 'executeCommandProvider': {'commands': ['_typescript.applyWorkspaceEdit', '_typescript.applyCodeAction', '_typescript.applyRefactoring', '_typescript.configurePlugin', '_typescript.organizeImports', '_typescript.applyRenameFile', '_typescript.goToSourceDefinition']}, 'hoverProvider': True, 'inlayHintProvider': True, 'linkedEditingRangeProvider': False, 'renameProvider': True, 'referencesProvider': True, 'selectionRangeProvider': True, 'signatureHelpProvider': {'triggerCharacters': ['(', ',', '<'], 'retriggerCharacters': [')']}, 'workspaceSymbolProvider': True, 'implementationProvider': True, 'typeDefinitionProvider': True, 'foldingRangeProvider': True, 'semanticTokensProvider': {'documentSelector': None, 'legend': {'tokenTypes': ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'enumMember', 'property', 'function', 'member'], 'tokenModifiers': ['declaration', 'static', 'async', 'readonly', 'defaultLibrary', 'local']}, 'full': True, 'range': True}, 'workspace': {'fileOperations': {'willRename': {'filters': [{'scheme': 'file', 'pattern': {'glob': '**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'matches': 'file'}}, {'scheme': 'file', 'pattern': {'glob': '**', 'matches': 'folder'}}]}}}}}} -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/server.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/mikro-orm.config.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/index.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/book.controller.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/index.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/author.controller.spec.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/book.controller.spec.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/author.controller.js -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js" - } -} -{'jsonrpc': '2.0', 'id': 2, 'result': []} -No references found for Publisher -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js" - } -} -{'jsonrpc': '2.0', 'id': 3, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js" - } -} -{'jsonrpc': '2.0', 'id': 4, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js" - } -} -{'jsonrpc': '2.0', 'id': 5, 'result': []} -No references found for Book -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js" - } -} -{'jsonrpc': '2.0', 'id': 6, 'result': []} -No references found for BaseEntity -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js" - } -} -{'jsonrpc': '2.0', 'id': 7, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js" - } -} -{'jsonrpc': '2.0', 'id': 8, 'result': []} -No references found for BookTag -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js" - } -} -{'jsonrpc': '2.0', 'id': 9, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js" - } -} -{'jsonrpc': '2.0', 'id': 10, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js" - } -} -{'jsonrpc': '2.0', 'id': 11, 'result': []} -No references found for Author diff --git a/poetry.lock b/poetry.lock index 19e57967..989caa97 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "asyncio" @@ -6,6 +6,7 @@ version = "3.4.3" description = "reference implementation of PEP 3156" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"}, {file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"}, @@ -13,15 +14,296 @@ files = [ {file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"}, ] +[[package]] +name = "attrs" +version = "25.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, + {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, +] + +[package.extras] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] + +[[package]] +name = "cattrs" +version = "24.1.2" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "cattrs-24.1.2-py3-none-any.whl", hash = "sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0"}, + {file = "cattrs-24.1.2.tar.gz", hash = "sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85"}, +] + +[package.dependencies] +attrs = ">=23.1.0" +exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""} + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] +orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2025.1.31" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +] + +[[package]] +name = "docstring-to-markdown" +version = "0.15" +description = "On the fly conversion of Python docstrings to markdown" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "docstring-to-markdown-0.15.tar.gz", hash = "sha256:e146114d9c50c181b1d25505054a8d0f7a476837f0da2c19f07e06eaed52b73d"}, + {file = "docstring_to_markdown-0.15-py3-none-any.whl", hash = "sha256:27afb3faedba81e34c33521c32bbd258d7fbb79eedf7d29bc4e81080e854aec0"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "jedi" +version = "0.19.2" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, +] + +[package.dependencies] +parso = ">=0.8.4,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] + +[[package]] +name = "jedi-language-server" +version = "0.41.1" +description = "A language server for Jedi!" +optional = false +python-versions = ">=3.8,<4.0" +groups = ["main"] +files = [ + {file = "jedi_language_server-0.41.1-py3-none-any.whl", hash = "sha256:ca9b3e7f48b70f0988d85ffde4f01dd1ab94c8e0f69e8c6424e6657117b44f91"}, + {file = "jedi_language_server-0.41.1.tar.gz", hash = "sha256:3f15ca5cc28e728564f7d63583e171b418025582447ce023512e3f2b2d71ebae"}, +] + +[package.dependencies] +cattrs = ">=23.1.2" +docstring-to-markdown = "==0.*" +jedi = ">=0.19.0,<0.20.0" +lsprotocol = ">=2022.0.0a9" +pygls = ">=1.0.1,<2.0.0" + +[[package]] +name = "lsprotocol" +version = "2023.0.1" +description = "Python implementation of the Language Server Protocol." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "lsprotocol-2023.0.1-py3-none-any.whl", hash = "sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2"}, + {file = "lsprotocol-2023.0.1.tar.gz", hash = "sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d"}, +] + +[package.dependencies] +attrs = ">=21.3.0" +cattrs = "!=23.2.1" + +[[package]] +name = "multilspy" +version = "0.0.12" +description = "A language-agnostic LSP client in Python, with a library interface. Intended to be used to build applications around language servers. Currently multilspy supports language servers for Python, Rust, Java, Go, JavaScript, Ruby and C#. Originally appeared as part of Monitor-Guided Decoding (https://github.com/microsoft/monitors4codegen)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [] +develop = false + +[package.dependencies] +jedi-language-server = "0.41.1" +pydantic = ">=1.10.5,<2" +requests = "2.32.3" + +[package.source] +type = "git" +url = "https://github.com/blarApp/multilspy.git" +reference = "HEAD" +resolved_reference = "2d5a3906bcdea76b70799cf79739394b7eb27e91" + [[package]] name = "neo4j" -version = "5.27.0" +version = "5.28.1" description = "Neo4j Bolt driver for Python" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "neo4j-5.27.0-py3-none-any.whl", hash = "sha256:929c14b9e5341267324eca170b39d1798b032bffacc26a0529eacaf678ae483f"}, - {file = "neo4j-5.27.0.tar.gz", hash = "sha256:f82ee807cd15b178898d83f41a66372e11719a25dd487fd7bea48fd4b7323765"}, + {file = "neo4j-5.28.1-py3-none-any.whl", hash = "sha256:6755ef9e5f4e14b403aef1138fb6315b120631a0075c138b5ddb2a06b87b09fd"}, + {file = "neo4j-5.28.1.tar.gz", hash = "sha256:ae8e37a1d895099062c75bc359b2cce62099baac7be768d0eba7180c1298e214"}, ] [package.dependencies] @@ -32,12 +314,115 @@ numpy = ["numpy (>=1.7.0,<3.0.0)"] pandas = ["numpy (>=1.7.0,<3.0.0)", "pandas (>=1.1.0,<3.0.0)"] pyarrow = ["pyarrow (>=1.0.0)"] +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pydantic" +version = "1.10.21" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pydantic-1.10.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:245e486e0fec53ec2366df9cf1cba36e0bbf066af7cd9c974bbbd9ba10e1e586"}, + {file = "pydantic-1.10.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c54f8d4c151c1de784c5b93dfbb872067e3414619e10e21e695f7bb84d1d1fd"}, + {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b64708009cfabd9c2211295144ff455ec7ceb4c4fb45a07a804309598f36187"}, + {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a148410fa0e971ba333358d11a6dea7b48e063de127c2b09ece9d1c1137dde4"}, + {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:36ceadef055af06e7756eb4b871cdc9e5a27bdc06a45c820cd94b443de019bbf"}, + {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0501e1d12df6ab1211b8cad52d2f7b2cd81f8e8e776d39aa5e71e2998d0379f"}, + {file = "pydantic-1.10.21-cp310-cp310-win_amd64.whl", hash = "sha256:c261127c275d7bce50b26b26c7d8427dcb5c4803e840e913f8d9df3f99dca55f"}, + {file = "pydantic-1.10.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b6350b68566bb6b164fb06a3772e878887f3c857c46c0c534788081cb48adf4"}, + {file = "pydantic-1.10.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:935b19fdcde236f4fbf691959fa5c3e2b6951fff132964e869e57c70f2ad1ba3"}, + {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6a04efdcd25486b27f24c1648d5adc1633ad8b4506d0e96e5367f075ed2e0b"}, + {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1ba253eb5af8d89864073e6ce8e6c8dec5f49920cff61f38f5c3383e38b1c9f"}, + {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:57f0101e6c97b411f287a0b7cf5ebc4e5d3b18254bf926f45a11615d29475793"}, + {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e85834f0370d737c77a386ce505c21b06bfe7086c1c568b70e15a568d9670d"}, + {file = "pydantic-1.10.21-cp311-cp311-win_amd64.whl", hash = "sha256:6a497bc66b3374b7d105763d1d3de76d949287bf28969bff4656206ab8a53aa9"}, + {file = "pydantic-1.10.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ed4a5f13cf160d64aa331ab9017af81f3481cd9fd0e49f1d707b57fe1b9f3ae"}, + {file = "pydantic-1.10.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b7693bb6ed3fbe250e222f9415abb73111bb09b73ab90d2d4d53f6390e0ccc1"}, + {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185d5f1dff1fead51766da9b2de4f3dc3b8fca39e59383c273f34a6ae254e3e2"}, + {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38e6d35cf7cd1727822c79e324fa0677e1a08c88a34f56695101f5ad4d5e20e5"}, + {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1d7c332685eafacb64a1a7645b409a166eb7537f23142d26895746f628a3149b"}, + {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c9b782db6f993a36092480eeaab8ba0609f786041b01f39c7c52252bda6d85f"}, + {file = "pydantic-1.10.21-cp312-cp312-win_amd64.whl", hash = "sha256:7ce64d23d4e71d9698492479505674c5c5b92cda02b07c91dfc13633b2eef805"}, + {file = "pydantic-1.10.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0067935d35044950be781933ab91b9a708eaff124bf860fa2f70aeb1c4be7212"}, + {file = "pydantic-1.10.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e8148c2ce4894ce7e5a4925d9d3fdce429fb0e821b5a8783573f3611933a251"}, + {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4973232c98b9b44c78b1233693e5e1938add5af18042f031737e1214455f9b8"}, + {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:662bf5ce3c9b1cef32a32a2f4debe00d2f4839fefbebe1d6956e681122a9c839"}, + {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98737c3ab5a2f8a85f2326eebcd214510f898881a290a7939a45ec294743c875"}, + {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0bb58bbe65a43483d49f66b6c8474424d551a3fbe8a7796c42da314bac712738"}, + {file = "pydantic-1.10.21-cp313-cp313-win_amd64.whl", hash = "sha256:e622314542fb48542c09c7bd1ac51d71c5632dd3c92dc82ede6da233f55f4848"}, + {file = "pydantic-1.10.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d356aa5b18ef5a24d8081f5c5beb67c0a2a6ff2a953ee38d65a2aa96526b274f"}, + {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08caa8c0468172d27c669abfe9e7d96a8b1655ec0833753e117061febaaadef5"}, + {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c677aa39ec737fec932feb68e4a2abe142682f2885558402602cd9746a1c92e8"}, + {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:79577cc045d3442c4e845df53df9f9202546e2ba54954c057d253fc17cd16cb1"}, + {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b6b73ab347284719f818acb14f7cd80696c6fdf1bd34feee1955d7a72d2e64ce"}, + {file = "pydantic-1.10.21-cp37-cp37m-win_amd64.whl", hash = "sha256:46cffa24891b06269e12f7e1ec50b73f0c9ab4ce71c2caa4ccf1fb36845e1ff7"}, + {file = "pydantic-1.10.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:298d6f765e3c9825dfa78f24c1efd29af91c3ab1b763e1fd26ae4d9e1749e5c8"}, + {file = "pydantic-1.10.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2f4a2305f15eff68f874766d982114ac89468f1c2c0b97640e719cf1a078374"}, + {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b263b60c519354afb3a60107d20470dd5250b3ce54c08753f6975c406d949b"}, + {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e23a97a6c2f2db88995496db9387cd1727acdacc85835ba8619dce826c0b11a6"}, + {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:3c96fed246ccc1acb2df032ff642459e4ae18b315ecbab4d95c95cfa292e8517"}, + {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b92893ebefc0151474f682e7debb6ab38552ce56a90e39a8834734c81f37c8a9"}, + {file = "pydantic-1.10.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8460bc256bf0de821839aea6794bb38a4c0fbd48f949ea51093f6edce0be459"}, + {file = "pydantic-1.10.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d387940f0f1a0adb3c44481aa379122d06df8486cc8f652a7b3b0caf08435f7"}, + {file = "pydantic-1.10.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:266ecfc384861d7b0b9c214788ddff75a2ea123aa756bcca6b2a1175edeca0fe"}, + {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61da798c05a06a362a2f8c5e3ff0341743e2818d0f530eaac0d6898f1b187f1f"}, + {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a621742da75ce272d64ea57bd7651ee2a115fa67c0f11d66d9dcfc18c2f1b106"}, + {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9e3e4000cd54ef455694b8be9111ea20f66a686fc155feda1ecacf2322b115da"}, + {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f198c8206640f4c0ef5a76b779241efb1380a300d88b1bce9bfe95a6362e674d"}, + {file = "pydantic-1.10.21-cp39-cp39-win_amd64.whl", hash = "sha256:e7f0cda108b36a30c8fc882e4fc5b7eec8ef584aa43aa43694c6a7b274fb2b56"}, + {file = "pydantic-1.10.21-py3-none-any.whl", hash = "sha256:db70c920cba9d05c69ad4a9e7f8e9e83011abb2c6490e561de9ae24aee44925c"}, + {file = "pydantic-1.10.21.tar.gz", hash = "sha256:64b48e2b609a6c22178a56c408ee1215a7206077ecb8a193e2fda31858b2362a"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pygls" +version = "1.3.1" +description = "A pythonic generic language server (pronounced like 'pie glass')" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pygls-1.3.1-py3-none-any.whl", hash = "sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e"}, + {file = "pygls-1.3.1.tar.gz", hash = "sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018"}, +] + +[package.dependencies] +cattrs = ">=23.1.2" +lsprotocol = "2023.0.1" + +[package.extras] +ws = ["websockets (>=11.0.3)"] + [[package]] name = "python-dotenv" version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -48,21 +433,45 @@ cli = ["click (>=5.0)"] [[package]] name = "pytz" -version = "2024.2" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + [[package]] name = "tree-sitter" version = "0.23.2" description = "Python bindings to the Tree-sitter parsing library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree-sitter-0.23.2.tar.gz", hash = "sha256:66bae8dd47f1fed7bdef816115146d3a41c39b5c482d7bad36d9ba1def088450"}, {file = "tree_sitter-0.23.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3a937f5d8727bc1c74c4bf2a9d1c25ace049e8628273016ad0d45914ae904e10"}, @@ -111,12 +520,55 @@ files = [ docs = ["sphinx (>=7.3,<8.0)", "sphinx-book-theme"] tests = ["tree-sitter-html (>=0.23.0)", "tree-sitter-javascript (>=0.23.0)", "tree-sitter-json (>=0.23.0)", "tree-sitter-python (>=0.23.0)", "tree-sitter-rust (>=0.23.0)"] +[[package]] +name = "tree-sitter-c-sharp" +version = "0.23.1" +description = "C# grammar for tree-sitter" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5"}, + {file = "tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb"}, +] + +[package.extras] +core = ["tree-sitter (>=0.22,<1.0)"] + +[[package]] +name = "tree-sitter-go" +version = "0.23.4" +description = "Go grammar for tree-sitter" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "tree_sitter_go-0.23.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c9320f87a05cd47fa0f627b9329bbc09b7ed90de8fe4f5882aed318d6e19962d"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:914e63d16b36ab0e4f52b031e574b82d17d0bbfecca138ae83e887a1cf5b71ac"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:330ecbb38d6ea4ef41eba2d473056889705e64f6a51c2fb613de05b1bcb5ba22"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd14d23056ae980debfccc0db67d0a168da03792ca2968b1b5dd58ce288084e7"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c3b40912487fdb78c4028860dd79493a521ffca0104f209849823358db3618a0"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-win_amd64.whl", hash = "sha256:ae4b231cad2ef76401d33617879cda6321c4d0853f7fd98cb5654c50a218effb"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-win_arm64.whl", hash = "sha256:2ac907362a3c347145dc1da0858248546500a323de90d2cb76d2a3fdbfc8da25"}, + {file = "tree_sitter_go-0.23.4.tar.gz", hash = "sha256:0ebff99820657066bec21690623a14c74d9e57a903f95f0837be112ddadf1a52"}, +] + +[package.extras] +core = ["tree-sitter (>=0.22,<1.0)"] + [[package]] name = "tree-sitter-javascript" version = "0.23.1" description = "JavaScript grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_javascript-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6ca583dad4bd79d3053c310b9f7208cd597fd85f9947e4ab2294658bb5c11e35"}, {file = "tree_sitter_javascript-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:94100e491a6a247aa4d14caf61230c171b6376c863039b6d9cd71255c2d815ec"}, @@ -137,6 +589,7 @@ version = "0.23.6" description = "Python grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_python-0.23.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:28fbec8f74eeb2b30292d97715e60fac9ccf8a8091ce19b9d93e9b580ed280fb"}, {file = "tree_sitter_python-0.23.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:680b710051b144fedf61c95197db0094f2245e82551bf7f0c501356333571f7a"}, @@ -157,6 +610,7 @@ version = "0.23.1" description = "Ruby grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_ruby-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:39f391322d2210843f07081182dbf00f8f69cfbfa4687b9575cac6d324bae443"}, {file = "tree_sitter_ruby-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:aa4ee7433bd42fac22e2dad4a3c0f332292ecf482e610316828c711a0bb7f794"}, @@ -177,6 +631,7 @@ version = "0.23.2" description = "TypeScript and TSX grammars for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478"}, {file = "tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8"}, @@ -191,12 +646,43 @@ files = [ [package.extras] core = ["tree-sitter (>=0.23,<1.0)"] +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + [[package]] name = "websockets" version = "13.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, @@ -287,6 +773,6 @@ files = [ ] [metadata] -lock-version = "2.0" -python-versions = ">=3.10,<=3.12.8" -content-hash = "a9c7603534dd4253b0674e07c5f0d98a7c52061374857fcef1b0317a762a2f31" +lock-version = "2.1" +python-versions = ">=3.10,<=3.14" +content-hash = "5a7403ca78dbdb36d7185e498e2223008abf732669128bc7498c7f510f155dd5" diff --git a/pyproject.toml b/pyproject.toml index 2af8e133..b695b6e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ license = "MIT" packages = [{include = "blarify"}] [tool.poetry.dependencies] -python = ">=3.10,<=3.13" +python = ">=3.10,<=3.14" asyncio = "^3.4.3" websockets = "^13.1" neo4j = "^5.25.0" @@ -20,6 +20,9 @@ tree-sitter-python = "^0.23.2" tree-sitter-ruby = "^0.23.0" tree-sitter-javascript = "^0.23.0" tree-sitter-typescript = "^0.23.2" +tree-sitter-c-sharp = "^0.23.1" +multilspy = {git = "https://github.com/blarApp/multilspy.git"} +tree-sitter-go = "^0.23.4" [build-system]