Skip to main content
Glama

CodeGraphContext

test_graph_indexing.py42.8 kB
import pytest import os from .conftest import SAMPLE_PROJECT_PATH # ============================================================================== # == EXPECTED RELATIONSHIPS # ============================================================================== EXPECTED_STRUCTURE = [ ("module_a.py", "foo", "Function"), ("module_a.py", "bar", "Function"), ("module_a.py", "outer", "Function"), ("module_b.py", "helper", "Function"), ("module_b.py", "process_data", "Function"), ("module_b.py", "factorial", "Function"), ("advanced_classes.py", "A", "Class"), ("advanced_classes.py", "B", "Class"), ("advanced_classes.py", "C", "Class"), ("module_a.py", "nested", "Function"), ("advanced_calls.py", "square", "Function"), ("advanced_calls.py", "calls", "Function"), ("advanced_calls.py", "Dummy", "Class"), ("advanced_classes2.py", "Base", "Class"), ("advanced_classes2.py", "Mid", "Class"), ("advanced_classes2.py", "Final", "Class"), ("advanced_classes2.py", "Mixin1", "Class"), ("advanced_classes2.py", "Mixin2", "Class"), ("advanced_classes2.py", "Combined", "Class"), ("advanced_classes2.py", "Point", "Class"), ("advanced_classes2.py", "Color", "Class"), ("advanced_classes2.py", "handle", "Function"), ("advanced_functions.py", "with_defaults", "Function"), ("advanced_functions.py", "with_args_kwargs", "Function"), ("advanced_functions.py", "higher_order", "Function"), ("advanced_functions.py", "return_function", "Function"), ("advanced_imports.py", "outer_import", "Function"), ("advanced_imports.py", "use_random", "Function"), ("async_features.py", "fetch_data", "Function"), ("async_features.py", "main", "Function"), ("callbacks_decorators.py", "executor", "Function"), ("callbacks_decorators.py", "square", "Function"), ("callbacks_decorators.py", "log_decorator", "Function"), ("callbacks_decorators.py", "hello", "Function"), ("class_instantiation.py", "A", "Class"), ("class_instantiation.py", "B", "Class"), ("class_instantiation.py", "Fluent", "Class"), ("function_chains.py", "f1", "Function"), ("function_chains.py", "f2", "Function"), ("function_chains.py", "f3", "Function"), ("function_chains.py", "make_adder", "Function"), ("generators.py", "gen_numbers", "Function"), ("generators.py", "agen_numbers", "Function"), ("generators.py", "async_with_example", "Function"), ("generators.py", "AsyncCM", "Class"), ("datatypes.py", "Point", "Class"), ("datatypes.py", "Color", "Class"), ("complex_classes.py", "Base", "Class"), ("complex_classes.py", "Child", "Class"), ("complex_classes.py", "decorator", "Function"), ("complex_classes.py", "decorated_function", "Function"), ("control_flow.py", "choose_path", "Function"), ("control_flow.py", "ternary", "Function"), ("control_flow.py", "try_except_finally", "Function"), ("control_flow.py", "conditional_inner_import", "Function"), ("control_flow.py", "env_based_import", "Function"), ("circular1.py", "func1", "Function"), ("circular2.py", "func2", "Function"), ("cli_and_dunder.py", "run", "Function"), ("comprehensions_generators.py", "double", "Function"), ("context_managers.py", "FileOpener", "Class"), ("context_managers.py", "use_file", "Function"), ("dynamic_dispatch.py", "add", "Function"), ("dynamic_dispatch.py", "sub", "Function"), ("dynamic_dispatch.py", "mul", "Function"), ("dynamic_dispatch.py", "dispatch_by_key", "Function"), ("dynamic_dispatch.py", "dispatch_by_string", "Function"), ("dynamic_dispatch.py", "partial_example", "Function"), ("dynamic_dispatch.py", "C", "Class"), ("dynamic_dispatch.py", "methodcaller_example", "Function"), ("dynamic_dispatch.py", "dynamic_import_call", "Function"), ("dynamic_imports.py", "import_optional", "Function"), ("dynamic_imports.py", "import_by___import__", "Function"), ("dynamic_imports.py", "importlib_runtime", "Function"), ("import_reexports.py", "core_function", "Function"), ("import_reexports.py", "reexport", "Function"), ("mapping_calls.py", "Dispatcher", "Class"), ("mapping_calls.py", "use_dispatcher", "Function"), ("module_c/submodule1.py", "call_helper_twice", "Function"), ("module_c/submodule2.py", "wrapper", "Function"), ("namespace_pkg/ns_module.py", "ns_func", "Function"), ("pattern_matching.py", "matcher", "Function"), ("typing_examples.py", "typed_func", "Function"), ("typing_examples.py", "union_func", "Function"), ("typing_examples.py", "dict_func", "Function"), ] EXPECTED_INHERITANCE = [ pytest.param("C", "advanced_classes.py", "A", "advanced_classes.py", id="C inherits from A"), pytest.param("C", "advanced_classes.py", "B", "advanced_classes.py", id="C inherits from B"), pytest.param("ConcreteThing", "advanced_classes.py", "AbstractThing", "advanced_classes.py", id="ConcreteThing inherits from AbstractThing"), pytest.param("Mid", "advanced_classes2.py", "Base", "advanced_classes2.py", id="Mid inherits from Base"), pytest.param("Final", "advanced_classes2.py", "Mid", "advanced_classes2.py", id="Final inherits from Mid"), pytest.param("Combined", "advanced_classes2.py", "Mixin1", "advanced_classes2.py", id="Combined inherits from Mixin1"), pytest.param("Combined", "advanced_classes2.py", "Mixin2", "advanced_classes2.py", id="Combined inherits from Mixin2"), pytest.param("B", "class_instantiation.py", "A", "class_instantiation.py", id="B inherits from A"), pytest.param("B", "class_instantiation.py", "A", "class_instantiation.py", marks=pytest.mark.skip(reason="Indexer does not support inheritance via super() calls"), id="B inherits from A via super()"), pytest.param("Child", "complex_classes.py", "Base", "complex_classes.py", id="Child inherits from Base"), ] EXPECTED_CALLS = [ pytest.param("foo", "module_a.py", None, "helper", "module_b.py", None, id="module_a.foo->module_b.helper"), pytest.param("foo", "module_a.py", None, "process_data", "module_b.py", None, id="module_a.foo->module_b.process_data"), pytest.param("factorial", "module_b.py", None, "factorial", "module_b.py", None, id="module_b.factorial->recursive"), pytest.param("calls", "advanced_calls.py", None, "square", "advanced_calls.py", None, id="advanced_calls.calls->square"), pytest.param("call_helper_twice", "module_c/submodule1.py", None, "helper", "module_b.py", None, id="submodule1.call_helper_twice->module_b.helper"), pytest.param("wrapper", "module_c/submodule2.py", None, "call_helper_twice", "module_c/submodule1.py", None, id="submodule2.wrapper->submodule1.call_helper_twice"), pytest.param("main", "async_features.py", None, "fetch_data", "async_features.py", None, id="async.main->fetch_data"), pytest.param("func1", "circular1.py", None, "func2", "circular2.py", None, id="circular1.func1->circular2.func2"), pytest.param("run", "cli_and_dunder.py", None, "with_defaults", "advanced_functions.py", None, id="cli.run->with_defaults"), pytest.param("use_dispatcher", "mapping_calls.py", None, "call", "mapping_calls.py", None, id="mapping.use_dispatcher->call"), pytest.param("calls", "advanced_calls.py", None, "method", "advanced_calls.py", "Dummy", marks=pytest.mark.skip(reason="Dynamic call with getattr is not supported"), id="advanced_calls.calls->Dummy.method"), pytest.param("both", "advanced_classes2.py", "Combined", "m1", "advanced_classes2.py", "Mixin1", id="advanced_classes2.both->m1"), pytest.param("both", "advanced_classes2.py", "Combined", "m2", "advanced_classes2.py", "Mixin2", id="advanced_classes2.both->m2"), pytest.param("executor", "callbacks_decorators.py", None, "square", "callbacks_decorators.py", None, marks=pytest.mark.skip(reason="Dynamic call passing function as argument is not supported"), id="callbacks.executor->square"), pytest.param("reexport", "import_reexports.py", None, "core_function", "import_reexports.py", None, id="reexport->core_function"), pytest.param("greet", "class_instantiation.py", "B", "greet", "class_instantiation.py", "A", marks=pytest.mark.skip(reason="super() calls are not supported yet"), id="B.greet->A.greet"), pytest.param("greet", "complex_classes.py", "Child", "greet", "complex_classes.py", "Base", marks=pytest.mark.skip(reason="super() calls are not supported yet"), id="Child.greet->Base.greet"), pytest.param("class_method", "complex_classes.py", "Child", "greet", "complex_classes.py", "Child", id="Child.class_method->Child.greet"), pytest.param("use_file", "context_managers.py", None, "__enter__", "context_managers.py", "FileOpener", marks=pytest.mark.skip(reason="Implicit context manager calls not supported"), id="use_file->FileOpener.__enter__"), pytest.param("partial_example", "dynamic_dispatch.py", None, "add", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Calls via functools.partial not supported yet"), id="partial_example->add"), pytest.param("async_with_example", "generators.py", None, "__aenter__", "generators.py", "AsyncCM", marks=pytest.mark.skip(reason="Implicit async context manager calls not supported"), id="async_with_example->AsyncCM.__aenter__"), pytest.param("call", "mapping_calls.py", "Dispatcher", "start", "mapping_calls.py", "Dispatcher", marks=pytest.mark.skip(reason="Dynamic call via dict lookup not supported"), id="Dispatcher.call->start"), pytest.param("greet", "class_instantiation.py", None, "greet", "class_instantiation.py", "A", marks=pytest.mark.skip(reason="Indexer does not capture calls to methods of instantiated objects within the same file"), id="A.greet called"), pytest.param("greet", "class_instantiation.py", None, "greet", "class_instantiation.py", "B", marks=pytest.mark.skip(reason="Indexer does not capture calls to methods of instantiated objects within the same file"), id="B.greet called"), pytest.param("step1", "class_instantiation.py", "Fluent", "step1", "class_instantiation.py", "Fluent", marks=pytest.mark.skip(reason="Indexer does not capture method chaining calls"), id="Fluent.step1 called"), pytest.param("step2", "class_instantiation.py", "Fluent", "step2", "class_instantiation.py", "Fluent", marks=pytest.mark.skip(reason="Indexer does not capture method chaining calls"), id="Fluent.step2 called"), pytest.param("step3", "class_instantiation.py", "Fluent", "step3", "class_instantiation.py", "Fluent", marks=pytest.mark.skip(reason="Indexer does not capture method chaining calls"), id="Fluent.step3 called"), pytest.param("dynamic", "class_instantiation.py", "B", "lambda", "class_instantiation.py", None, marks=pytest.mark.skip(reason="Dynamic attribute assignment and lambda calls not supported"), id="B.dynamic called"), pytest.param("add_argument", "cli_and_dunder.py", None, "add_argument", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="ArgumentParser.add_argument called"), pytest.param("parse_args", "cli_and_dunder.py", None, "parse_args", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="ArgumentParser.parse_args called"), pytest.param("print", "cli_and_dunder.py", None, "print", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="print called"), pytest.param("double", "comprehensions_generators.py", None, "double", "comprehensions_generators.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls within comprehensions/generators"), id="double called in list comprehension"), pytest.param("range", "comprehensions_generators.py", None, "range", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="range called in list comprehension"), pytest.param("double", "comprehensions_generators.py", None, "double", "comprehensions_generators.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls within comprehensions/generators"), id="double called in generator expression"), pytest.param("range", "comprehensions_generators.py", None, "range", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="range called in generator expression"), pytest.param("list", "comprehensions_generators.py", None, "list", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="list called"), pytest.param("sorted", "comprehensions_generators.py", None, "sorted", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="sorted called"), pytest.param("len", "comprehensions_generators.py", None, "len", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="len called"), pytest.param("open", "comprehensions_generators.py", None, "open", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="open called for write"), pytest.param("write", "comprehensions_generators.py", None, "write", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="write called"), pytest.param("open", "comprehensions_generators.py", None, "open", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="open called for read"), pytest.param("read", "comprehensions_generators.py", None, "read", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="read called"), pytest.param("ValueError", "control_flow.py", None, "ValueError", None, None, marks=pytest.mark.skip(reason="Built-in exception constructors not explicitly indexed"), id="ValueError called"), pytest.param("str", "control_flow.py", None, "str", None, None, marks=pytest.mark.skip(reason="Built-in type constructors not explicitly indexed"), id="str called"), pytest.param("getenv", "control_flow.py", None, "getenv", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="os.getenv called"), pytest.param("dumps", "control_flow.py", None, "dumps", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="json.dumps called"), pytest.param("namedtuple", "datatypes.py", None, "namedtuple", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="namedtuple called"), pytest.param("DISPATCH", "dynamic_dispatch.py", None, "add", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Dynamic dispatch via dictionary lookup not supported"), id="dispatch_by_key calls add dynamically"), pytest.param("DISPATCH", "dynamic_dispatch.py", None, "sub", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Dynamic dispatch via dictionary lookup not supported"), id="dispatch_by_key calls sub dynamically"), pytest.param("DISPATCH", "dynamic_dispatch.py", None, "mul", "dynamic_dispatch.py", None, marks=pytest.mark.skip(reason="Dynamic dispatch via dictionary lookup not supported"), id="dispatch_by_key calls mul dynamically"), pytest.param("get", "dynamic_dispatch.py", None, "get", None, None, marks=pytest.mark.skip(reason="Calls to built-in dictionary methods not explicitly indexed"), id="globals().get called"), pytest.param("callable", "dynamic_dispatch.py", None, "callable", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="callable called"), pytest.param("partial", "dynamic_dispatch.py", None, "partial", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="partial called"), pytest.param("methodcaller", "dynamic_dispatch.py", None, "methodcaller", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="methodcaller called"), pytest.param("method", "dynamic_dispatch.py", "C", "method", "dynamic_dispatch.py", "C", marks=pytest.mark.skip(reason="Dynamic call via operator.methodcaller not supported"), id="C.method called via methodcaller"), pytest.param("import_module", "dynamic_dispatch.py", None, "import_module", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="importlib.import_module called"), pytest.param("getattr", "dynamic_dispatch.py", None, "getattr", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="getattr called"), pytest.param("dumps", "dynamic_imports.py", None, "dumps", None, None, marks=pytest.mark.skip(reason="Calls to external library methods not fully supported"), id="json.dumps called in import_optional"), pytest.param("__import__", "dynamic_imports.py", None, "__import__", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="__import__ called"), pytest.param("getattr", "dynamic_imports.py", None, "getattr", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="getattr called in import_by___import__"), pytest.param("import_module", "dynamic_imports.py", None, "import_module", None, None, marks=pytest.mark.skip(reason="Calls to external library functions not fully supported"), id="importlib.import_module called in importlib_runtime"), pytest.param("getattr", "dynamic_imports.py", None, "getattr", None, None, marks=pytest.mark.skip(reason="Built-in function calls not explicitly indexed"), id="getattr called in importlib_runtime"), pytest.param("f3", "function_chains.py", None, "f3", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture chained function calls"), id="f3 called"), pytest.param("f2", "function_chains.py", None, "f2", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture chained function calls"), id="f2 called"), pytest.param("f1", "function_chains.py", None, "f1", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture chained function calls"), id="f1 called"), pytest.param("strip", "function_chains.py", None, "strip", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="strip called"), pytest.param("lower", "function_chains.py", None, "lower", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="lower called"), pytest.param("replace", "function_chains.py", None, "replace", None, None, marks=pytest.mark.skip(reason="Method calls on built-in types not explicitly indexed"), id="replace called"), pytest.param("make_adder", "function_chains.py", None, "make_adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to functions that return functions"), id="make_adder called"), pytest.param("adder", "function_chains.py", None, "adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to inner functions returned by other functions"), id="adder called"), pytest.param("make_adder", "function_chains.py", None, "make_adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to functions that return functions in a chain"), id="make_adder called in chain"), pytest.param("adder", "function_chains.py", None, "adder", "function_chains.py", None, marks=pytest.mark.skip(reason="Indexer does not capture calls to inner functions returned by other functions in a chain"), id="adder called in chain"), pytest.param("sqrt", "import_reexports.py", None, "sqrt", None, None, marks=pytest.mark.skip(reason="Calls to aliased imported functions not fully supported"), id="m.sqrt called"), pytest.param("Dispatcher", "mapping_calls.py", None, "Dispatcher", "mapping_calls.py", None, marks=pytest.mark.skip(reason="Indexer does not capture class constructor calls"), id="Dispatcher constructor called"), pytest.param("str", "pattern_matching.py", None, "str", None, None, marks=pytest.mark.skip(reason="Built-in type constructors not explicitly indexed"), id="str called in pattern matching"), ] EXPECTED_IMPORTS = [ pytest.param("module_a.py", "math", id="module_a imports math"), pytest.param("module_a.py", "module_b", id="module_a imports module_b"), pytest.param("advanced_imports.py", "math", id="advanced_imports imports math"), pytest.param("advanced_imports.py", "random", id="advanced_imports imports random"), pytest.param("advanced_imports.py", "sys", id="advanced_imports imports sys"), pytest.param("async_features.py", "asyncio", id="async_features imports asyncio"), pytest.param("circular1.py", "func2", id="circular1 imports func2"), pytest.param("circular2.py", "func1", id="circular2 imports func1"), pytest.param("cli_and_dunder.py", "argparse", id="cli_and_dunder imports argparse"), pytest.param("cli_and_dunder.py", "advanced_functions", id="cli_and_dunder imports advanced_functions"), pytest.param("control_flow.py", "os", id="control_flow imports os"), pytest.param("datatypes.py", "dataclass", id="datatypes imports dataclass"), pytest.param("datatypes.py", "enum", id="datatypes imports enum"), pytest.param("datatypes.py", "namedtuple", id="datatypes imports namedtuple"), pytest.param("dynamic_dispatch.py", "partial", id="dynamic_dispatch imports partial"), pytest.param("dynamic_dispatch.py", "operator", id="dynamic_dispatch imports operator"), pytest.param("dynamic_dispatch.py", "importlib", id="dynamic_dispatch imports importlib"), pytest.param("dynamic_imports.py", "importlib", id="dynamic_imports imports importlib"), pytest.param("import_reexports.py", "math", marks=pytest.mark.skip(reason="Indexer does not support aliased imports (e.g., 'import math as m')"), id="import_reexports imports math"), pytest.param("module_c/submodule1.py", "helper", id="submodule1 imports helper"), pytest.param("module_c/submodule2.py", "call_helper_twice", id="submodule2 imports call_helper_twice"), pytest.param("typing_examples.py", "List", id="typing_examples imports List"), pytest.param("typing_examples.py", "Dict", marks=pytest.mark.skip(reason="Indexer does not capture imports after ','"), id="typing_examples imports Dict"), pytest.param("typing_examples.py", "Union", marks=pytest.mark.skip(reason="Indexer does not capture imports after ','"), id="typing_examples imports Union"), pytest.param("control_flow.py", "numpy", marks=pytest.mark.skip(reason="Indexer does not capture conditional imports"), id="control_flow imports numpy (conditional)"), pytest.param("control_flow.py", "ujson", marks=pytest.mark.skip(reason="Indexer does not capture conditional imports"), id="control_flow imports ujson (conditional)"), pytest.param("control_flow.py", "json", id="control_flow imports json (conditional)"), pytest.param("dynamic_imports.py", "ujson", marks=pytest.mark.skip(reason="Indexer does not capture conditional imports"), id="dynamic_imports imports ujson (conditional)"), pytest.param("dynamic_imports.py", "json", id="dynamic_imports imports json (conditional)"), ] EXPECTED_PARAMETERS = [ pytest.param("foo", "module_a.py", "x", id="foo has parameter x"), pytest.param("helper", "module_b.py", "x", id="helper has parameter x"), pytest.param("process_data", "module_b.py", "data", id="process_data has parameter data"), pytest.param("factorial", "module_b.py", "n", id="factorial has parameter n"), pytest.param("square", "advanced_calls.py", "x", id="square has parameter x"), pytest.param("method", "advanced_calls.py", "x", id="Dummy.method has parameter x"), pytest.param("with_defaults", "advanced_functions.py", "a", id="with_defaults has parameter a"), pytest.param("with_defaults", "advanced_functions.py", "b", id="with_defaults has parameter b"), pytest.param("with_defaults", "advanced_functions.py", "c", id="with_defaults has parameter c"), pytest.param("higher_order", "advanced_functions.py", "func", id="higher_order has parameter func"), pytest.param("higher_order", "advanced_functions.py", "data", id="higher_order has parameter data"), pytest.param("return_function", "advanced_functions.py", "x", id="return_function has parameter x"), pytest.param("executor", "callbacks_decorators.py", "func", id="executor has parameter func"), pytest.param("executor", "callbacks_decorators.py", "val", id="executor has parameter val"), pytest.param("square", "callbacks_decorators.py", "x", id="square has parameter x"), pytest.param("log_decorator", "callbacks_decorators.py", "fn", id="log_decorator has parameter fn"), pytest.param("hello", "callbacks_decorators.py", "name", id="hello has parameter name"), pytest.param("greet", "class_instantiation.py", "self", id="A.greet has parameter self"), pytest.param("greet", "class_instantiation.py", "self", id="B.greet has parameter self"), pytest.param("step1", "class_instantiation.py", "self", id="Fluent.step1 has parameter self"), pytest.param("step2", "class_instantiation.py", "self", id="Fluent.step2 has parameter self"), pytest.param("step3", "class_instantiation.py", "self", id="Fluent.step3 has parameter self"), pytest.param("run", "cli_and_dunder.py", "argv", id="run has parameter argv"), pytest.param("greet", "complex_classes.py", "self", id="Base.greet has self"), pytest.param("greet", "complex_classes.py", "self", id="Child.greet has self"), pytest.param("static_method", "complex_classes.py", "x", id="Child.static_method has x"), pytest.param("class_method", "complex_classes.py", "cls", id="Child.class_method has cls"), pytest.param("class_method", "complex_classes.py", "y", id="Child.class_method has y"), pytest.param("decorator", "complex_classes.py", "func", id="decorator has func"), pytest.param("decorated_function", "complex_classes.py", "x", id="decorated_function has x"), pytest.param("double", "comprehensions_generators.py", "x", id="double has parameter x"), pytest.param("__enter__", "context_managers.py", "self", id="FileOpener.__enter__ has self"), pytest.param("__exit__", "context_managers.py", "self", id="FileOpener.__exit__ has self"), pytest.param("__exit__", "context_managers.py", "exc_type", id="FileOpener.__exit__ has exc_type"), pytest.param("__exit__", "context_managers.py", "exc_val", id="FileOpener.__exit__ has exc_val"), pytest.param("__exit__", "context_managers.py", "exc_tb", id="FileOpener.__exit__ has exc_tb"), pytest.param("choose_path", "control_flow.py", "x", id="choose_path has parameter x"), pytest.param("ternary", "control_flow.py", "x", id="ternary has parameter x"), pytest.param("try_except_finally", "control_flow.py", "x", id="try_except_finally has parameter x"), pytest.param("conditional_inner_import", "control_flow.py", "use_numpy", id="conditional_inner_import has use_numpy"), pytest.param("add", "dynamic_dispatch.py", "a", id="add has a"), pytest.param("add", "dynamic_dispatch.py", "b", id="add has b"), pytest.param("sub", "dynamic_dispatch.py", "a", id="sub has a"), pytest.param("sub", "dynamic_dispatch.py", "b", id="sub has b"), pytest.param("mul", "dynamic_dispatch.py", "a", id="mul has a"), pytest.param("mul", "dynamic_dispatch.py", "b", id="mul has b"), pytest.param("dispatch_by_key", "dynamic_dispatch.py", "name", id="dispatch_by_key has name"), pytest.param("dispatch_by_key", "dynamic_dispatch.py", "a", id="dispatch_by_key has a"), pytest.param("dispatch_by_key", "dynamic_dispatch.py", "b", id="dispatch_by_key has b"), pytest.param("method", "dynamic_dispatch.py", "x", id="C.method has x"), pytest.param("methodcaller_example", "dynamic_dispatch.py", "x", id="methodcaller_example has x"), pytest.param("import_by___import__", "dynamic_imports.py", "name", id="import_by___import__ has name"), pytest.param("importlib_runtime", "dynamic_imports.py", "name", id="importlib_runtime has name"), pytest.param("importlib_runtime", "dynamic_imports.py", "attr", id="importlib_runtime has attr"), pytest.param("f1", "function_chains.py", "x", id="f1 has x"), pytest.param("f2", "function_chains.py", "x", id="f2 has x"), pytest.param("f3", "function_chains.py", "x", id="f3 has x"), pytest.param("make_adder", "function_chains.py", "n", id="make_adder has n"), pytest.param("gen_numbers", "generators.py", "n", id="gen_numbers has n"), pytest.param("agen_numbers", "generators.py", "n", id="agen_numbers has n"), pytest.param("call", "mapping_calls.py", "cmd", id="Dispatcher.call has cmd"), pytest.param("use_dispatcher", "mapping_calls.py", "cmd", id="use_dispatcher has cmd"), pytest.param("call_helper_twice", "module_c/submodule1.py", "x", id="call_helper_twice has x"), pytest.param("wrapper", "module_c/submodule2.py", "x", id="wrapper has x"), pytest.param("matcher", "pattern_matching.py", "x", id="matcher has x"), pytest.param("typed_func", "typing_examples.py", "a", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="typed_func has a"), pytest.param("typed_func", "typing_examples.py", "b", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="typed_func has b"), pytest.param("union_func", "typing_examples.py", "x", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="union_func has x"), pytest.param("dict_func", "typing_examples.py", "d", marks=pytest.mark.skip(reason="Indexer does not support parameters with type hints"), id="dict_func has d"), pytest.param("wrapper", "complex_classes.py", "*args", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (*args)"), id="wrapper has *args"), pytest.param("wrapper", "complex_classes.py", "**kwargs", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (**kwargs)"), id="wrapper has **kwargs"), pytest.param("dispatch_by_string", "dynamic_dispatch.py", "*args", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (*args)"), id="dispatch_by_string has *args"), pytest.param("dispatch_by_string", "dynamic_dispatch.py", "**kwargs", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (**kwargs)"), id="dispatch_by_string has **kwargs"), pytest.param("dynamic_import_call", "dynamic_dispatch.py", "*args", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (*args)"), id="dynamic_import_call has *args"), pytest.param("dynamic_import_call", "dynamic_dispatch.py", "**kwargs", marks=pytest.mark.skip(reason="Indexer does not capture variadic parameters (**kwargs)"), id="dynamic_import_call has **kwargs"), pytest.param("adder", "function_chains.py", "x", id="adder has x"), pytest.param("__aenter__", "generators.py", "self", id="AsyncCM.__aenter__ has self"), pytest.param("__aexit__", "generators.py", "self", id="AsyncCM.__aexit__ has self"), pytest.param("__aexit__", "generators.py", "exc_type", id="AsyncCM.__aexit__ has exc_type"), pytest.param("__aexit__", "generators.py", "exc_val", id="AsyncCM.__aexit__ has exc_val"), pytest.param("__aexit__", "generators.py", "exc_tb", id="AsyncCM.__aexit__ has exc_tb"), pytest.param("__init__", "mapping_calls.py", "self", id="Dispatcher.__init__ has self"), pytest.param("start", "mapping_calls.py", "self", id="Dispatcher.start has self"), pytest.param("stop", "mapping_calls.py", "self", id="Dispatcher.stop has self"), pytest.param("ns_func", "namespace_pkg/ns_module.py", None, marks=pytest.mark.skip(reason="Functions with no parameters are not explicitly tested for parameter existence"), id="ns_func has no parameters"), ] EXPECTED_CLASS_METHODS = [ pytest.param("A", "advanced_classes.py", "foo", id="A contains foo"), pytest.param("B", "advanced_classes.py", "foo", id="B contains foo"), pytest.param("C", "advanced_classes.py", "bar", id="C contains bar"), pytest.param("AbstractThing", "advanced_classes.py", "do", id="AbstractThing contains do"), pytest.param("ConcreteThing", "advanced_classes.py", "do", id="ConcreteThing contains do"), pytest.param("Dummy", "advanced_calls.py", "method", id="Dummy contains method"), pytest.param("Mixin1", "advanced_classes2.py", "m1", id="Mixin1 contains m1"), pytest.param("Mixin2", "advanced_classes2.py", "m2", id="Mixin2 contains m2"), pytest.param("Combined", "advanced_classes2.py", "both", id="Combined contains both"), pytest.param("Point", "advanced_classes2.py", "magnitude", id="Point contains magnitude"), pytest.param("Color", "advanced_classes2.py", "is_primary", id="Color contains is_primary"), pytest.param("A", "class_instantiation.py", "greet", id="A contains greet"), pytest.param("B", "class_instantiation.py", "greet", id="B contains greet"), pytest.param("Fluent", "class_instantiation.py", "step1", id="Fluent contains step1"), pytest.param("Fluent", "class_instantiation.py", "step2", id="Fluent contains step2"), pytest.param("Fluent", "class_instantiation.py", "step3", id="Fluent contains step3"), pytest.param("AsyncCM", "generators.py", "__aenter__", id="AsyncCM contains __aenter__"), pytest.param("AsyncCM", "generators.py", "__aexit__", id="AsyncCM contains __aexit__"), pytest.param("Base", "complex_classes.py", "greet", id="Base contains greet"), pytest.param("Child", "complex_classes.py", "greet", id="Child contains greet"), pytest.param("Child", "complex_classes.py", "static_method", id="Child contains static_method"), pytest.param("Child", "complex_classes.py", "class_method", id="Child contains class_method"), pytest.param("FileOpener", "context_managers.py", "__enter__", id="FileOpener contains __enter__"), pytest.param("FileOpener", "context_managers.py", "__exit__", id="FileOpener contains __exit__"), pytest.param("C", "dynamic_dispatch.py", "method", id="C contains method"), pytest.param("Dispatcher", "mapping_calls.py", "__init__", id="Dispatcher contains __init__"), pytest.param("Dispatcher", "mapping_calls.py", "start", id="Dispatcher contains start"), pytest.param("Dispatcher", "mapping_calls.py", "stop", id="Dispatcher contains stop"), pytest.param("Dispatcher", "mapping_calls.py", "call", id="Dispatcher contains call"), ] EXPECTED_FUNCTION_CONTAINS = [ pytest.param("return_function", "advanced_functions.py", "inner", id="return_function contains inner"), pytest.param("log_decorator", "callbacks_decorators.py", "wrapper", id="log_decorator contains wrapper"), pytest.param("make_adder", "function_chains.py", "adder", id="make_adder contains adder"), pytest.param("decorator", "complex_classes.py", "wrapper", id="decorator contains wrapper"), ] EXPECTED_DECORATORS = [ pytest.param("decorated_function", "complex_classes.py", "decorator", "complex_classes.py", id="decorated_function decorated by decorator"), pytest.param("hello", "callbacks_decorators.py", "log_decorator", "callbacks_decorators.py", id="hello decorated by log_decorator"), ] EXPECTED_EMPTY_FILES = [ pytest.param("edge_cases/comments_only.py", id="comments_only.py is empty"), pytest.param("edge_cases/docstring_only.py", id="docstring_only.py is empty"), pytest.param("edge_cases/empty.py", id="empty.py is empty"), pytest.param("edge_cases/syntax_error.py", marks=pytest.mark.skip(reason="File with syntax error should be skipped or handled gracefully"), id="syntax_error.py is skipped"), pytest.param("module_c/__init__.py", id="module_c/__init__.py is empty"), ] # ============================================================================== # == TEST IMPLEMENTATIONS # ============================================================================== def check_query(graph, query, description): """Helper function to execute a Cypher query and assert that a match is found.""" try: result = graph.query(query) except Exception as e: pytest.fail(f"Query failed for {description} with error: {e}\nQuery was:\n{query}") assert result is not None, f"Query for {description} returned None.\nQuery was:\n{query}" assert len(result) > 0, f"Query for {description} returned no records.\nQuery was:\n{query}" assert result[0].get('count', 0) > 0, f"No match found for {description}.\nQuery was:\n{query}" @pytest.mark.parametrize("file_name, item_name, item_label", EXPECTED_STRUCTURE) def test_file_contains_item(graph, file_name, item_name, item_label): """Verifies that a File node correctly CONTAINS a Function or Class node.""" description = f"CONTAINS from [{file_name}] to [{item_name}]" abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name) query = f""" MATCH (f:File {{path: '{abs_file_path}'}})-[:CONTAINS]->(item:{item_label} {{name: '{item_name}'}}) RETURN count(*) AS count """ check_query(graph, query, description) @pytest.mark.parametrize("child_name, child_file, parent_name, parent_file", EXPECTED_INHERITANCE) def test_inheritance_relationship(graph, child_name, child_file, parent_name, parent_file): """Verifies that an INHERITS relationship exists between two classes.""" description = f"INHERITS from [{child_name}] to [{parent_name}]" child_path = os.path.join(SAMPLE_PROJECT_PATH, child_file) parent_path = os.path.join(SAMPLE_PROJECT_PATH, parent_file) query = f""" MATCH (child:Class {{name: '{child_name}', file_path: '{child_path}'}})-[:INHERITS]->(parent:Class {{name: '{parent_name}', file_path: '{parent_path}'}}) RETURN count(*) as count """ check_query(graph, query, description) @pytest.mark.parametrize("caller_name, caller_file, caller_class, callee_name, callee_file, callee_class", EXPECTED_CALLS) def test_function_call_relationship(graph, caller_name, caller_file, caller_class, callee_name, callee_file, callee_class): """Verifies that a CALLS relationship exists by checking for nodes first, then the relationship.""" caller_path = os.path.join(SAMPLE_PROJECT_PATH, caller_file) callee_path = os.path.join(SAMPLE_PROJECT_PATH, callee_file) # Build match clauses for caller and callee if caller_class: caller_match = f"(caller_class:Class {{name: '{caller_class}', file_path: '{caller_path}'}})-[:CONTAINS]->(caller:Function {{name: '{caller_name}'}})" else: caller_match = f"(caller:Function {{name: '{caller_name}', file_path: '{caller_path}'}})" if callee_class: callee_match = f"(callee_class:Class {{name: '{callee_class}', file_path: '{callee_path}'}})-[:CONTAINS]->(callee:Function {{name: '{callee_name}'}})" else: callee_match = f"(callee:Function {{name: '{callee_name}', file_path: '{callee_path}'}})" # 1. Check that the caller node exists caller_description = f"existence of caller {caller_class or 'Function'} {{name: '{caller_name}'}} in [{caller_file}]" caller_query = f""" MATCH {caller_match} RETURN count(caller) as count """ check_query(graph, caller_query, caller_description) # 2. Check that the callee node exists callee_description = f"existence of callee {callee_class or 'Function'} {{name: '{callee_name}'}} in [{callee_file}]" callee_query = f""" MATCH {callee_match} RETURN count(callee) as count """ check_query(graph, callee_query, callee_description) # 3. Check that the CALLS relationship exists between them relationship_description = f"CALLS from [{caller_name}] to [{callee_name}]" relationship_query = f""" MATCH {caller_match} MATCH {callee_match} MATCH (caller)-[:CALLS]->(callee) RETURN count(*) as count """ check_query(graph, relationship_query, relationship_description) @pytest.mark.parametrize("file_name, module_name", EXPECTED_IMPORTS) def test_import_relationship(graph, file_name, module_name): """Verifies that an IMPORTS relationship exists between a file and a module.""" description = f"IMPORTS from [{file_name}] to [{module_name}]" abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name) query = f""" MATCH (f:File {{path: '{abs_file_path}'}})-[:IMPORTS]->(m:Module {{name: '{module_name}'}}) RETURN count(*) as count """ check_query(graph, query, description) @pytest.mark.parametrize("function_name, file_name, parameter_name", EXPECTED_PARAMETERS) def test_parameter_relationship(graph, function_name, file_name, parameter_name): """Verifies that a HAS_PARAMETER relationship exists between a function and a parameter.""" description = f"HAS_PARAMETER from [{function_name}] to [{parameter_name}]" abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name) query = f""" MATCH (f:Function {{name: '{function_name}', file_path: '{abs_file_path}'}})-[:HAS_PARAMETER]->(p:Parameter {{name: '{parameter_name}'}}) RETURN count(*) as count """ check_query(graph, query, description) @pytest.mark.parametrize("class_name, file_name, method_name", EXPECTED_CLASS_METHODS) def test_class_method_relationship(graph, class_name, file_name, method_name): """Verifies that a CONTAINS relationship exists between a class and a method.""" description = f"CONTAINS from [{class_name}] to [{method_name}]" abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name) query = f""" MATCH (c:Class {{name: '{class_name}', file_path: '{abs_file_path}'}})-[:CONTAINS]->(m:Function {{name: '{method_name}'}}) RETURN count(*) as count """ check_query(graph, query, description) @pytest.mark.parametrize("outer_function_name, file_name, inner_function_name", EXPECTED_FUNCTION_CONTAINS) def test_function_contains_relationship(graph, outer_function_name, file_name, inner_function_name): """Verifies that a CONTAINS relationship exists between an outer function and an inner function.""" description = f"CONTAINS from [{outer_function_name}] to [{inner_function_name}]" abs_file_path = os.path.join(SAMPLE_PROJECT_PATH, file_name) query = f""" MATCH (outer:Function {{name: '{outer_function_name}', file_path: '{abs_file_path}'}})-[:CONTAINS]->(inner:Function {{name: '{inner_function_name}'}}) RETURN count(*) as count """ check_query(graph, query, description)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Shashankss1205/CodeGraphContext'

If you have feedback or need assistance with the MCP directory API, please join our Discord server