-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve test collection and output (#160)
- Loading branch information
1 parent
973c476
commit 4dc3a7e
Showing
10 changed files
with
850 additions
and
565 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,3 +133,4 @@ dmypy.json | |
*_files/ | ||
*.html | ||
.idea/ | ||
drafts/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .testsuite import load_ipython_extension # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import ast | ||
import pathlib | ||
from typing import Dict, Set | ||
|
||
|
||
class AstParser: | ||
""" | ||
Helper class for extraction of function definitions and imports. | ||
To find all reference solutions: | ||
Parse the module file using the AST module and retrieve all function definitions and imports. | ||
For each reference solution store the names of all other functions used inside of it. | ||
""" | ||
|
||
def __init__(self, module_file: pathlib.Path) -> None: | ||
self.module_file = module_file | ||
self.function_defs = {} | ||
self.function_imports = {} | ||
self.called_function_names = {} | ||
|
||
tree = ast.parse(self.module_file.read_text(encoding="utf-8")) | ||
|
||
for node in tree.body: | ||
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): | ||
self.function_defs[node.name] = node | ||
elif isinstance(node, (ast.Import, ast.ImportFrom)) and hasattr( | ||
node, "module" | ||
): | ||
for n in node.names: | ||
self.function_imports[n.name] = node.module | ||
|
||
for node in tree.body: | ||
if ( | ||
node in self.function_defs.values() | ||
and hasattr(node, "name") | ||
and node.name.startswith("reference_") | ||
): | ||
self.called_function_names[node.name] = self.retrieve_functions( | ||
{**self.function_defs, **self.function_imports}, node, {node.name} | ||
) | ||
|
||
def retrieve_functions( | ||
self, all_functions: Dict, node: object, called_functions: Set[object] | ||
) -> Set[object]: | ||
""" | ||
Recursively walk the AST tree to retrieve all function definitions in a file | ||
""" | ||
|
||
if isinstance(node, ast.AST): | ||
for n in ast.walk(node): | ||
match n: | ||
case ast.Call(ast.Name(id=name)): | ||
called_functions.add(name) | ||
if name in all_functions: | ||
called_functions = self.retrieve_functions( | ||
all_functions, all_functions[name], called_functions | ||
) | ||
for child in ast.iter_child_nodes(n): | ||
called_functions = self.retrieve_functions( | ||
all_functions, child, called_functions | ||
) | ||
|
||
return called_functions | ||
|
||
def get_solution_code(self, name: str) -> str: | ||
""" | ||
Find the respective reference solution for the executed function. | ||
Create a str containing its code and the code of all other functions used, | ||
whether coming from the same file or an imported one. | ||
""" | ||
|
||
solution_functions = self.called_function_names[f"reference_{name}"] | ||
solution_code = "" | ||
|
||
for f in solution_functions: | ||
if f in self.function_defs: | ||
solution_code += ast.unparse(self.function_defs[f]) + "\n\n" | ||
elif f in self.function_imports: | ||
function_file = pathlib.Path( | ||
f"{self.function_imports[f].replace('.', '/')}.py" | ||
) | ||
if function_file.exists(): | ||
function_file_tree = ast.parse( | ||
function_file.read_text(encoding="utf-8") | ||
) | ||
for node in function_file_tree.body: | ||
if ( | ||
isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) | ||
and node.name == f | ||
): | ||
solution_code += ast.unparse(node) + "\n\n" | ||
|
||
return solution_code |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
class FunctionNotFoundError(Exception): | ||
"""Custom exception raised when the solution code cannot be parsed""" | ||
|
||
def __init__(self) -> None: | ||
super().__init__("No functions to test defined in the cell") | ||
|
||
|
||
class InstanceNotFoundError(Exception): | ||
"""Custom exception raised when an instance cannot be found""" | ||
|
||
def __init__(self, name: str) -> None: | ||
super().__init__(f"Could not get {name} instance") | ||
|
||
|
||
class TestModuleNotFoundError(Exception): | ||
"""Custom exception raised when the test module cannot be found""" | ||
|
||
def __init__(self) -> None: | ||
super().__init__("Test module is not defined") |
Oops, something went wrong.