From f5228ade6007c67c981fe86850b6deec36715542 Mon Sep 17 00:00:00 2001 From: Felice644 Date: Thu, 19 May 2022 22:28:00 +0200 Subject: [PATCH 1/2] -Added function for checking untested classes and functions. - Related test added. --- pynblint/nb_linting.py | 29 ++++++++++++++++++++++++- pynblint/repository.py | 9 +++++++- tests/unit/test_nb_linting_functions.py | 8 +++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/pynblint/nb_linting.py b/pynblint/nb_linting.py index f27d4bd..c710a70 100644 --- a/pynblint/nb_linting.py +++ b/pynblint/nb_linting.py @@ -231,7 +231,27 @@ def non_executed_notebook(notebook: Notebook) -> bool: return notebook.non_executed -# ========== # +def function_or_class_never_tested(notebook: Notebook) -> bool: + """Check whether the notebook have function or class not tested""" + + func_class_set = set() + for node in ast.walk(notebook.ast): + if isinstance(node, ast.FunctionDef): + for name in node.name: + func_class_set.add(name.split(".")[0]) + elif isinstance(node, ast.ClassDef): + func_class_set.add(node.name.split(".")[0]) + + if len(func_class_set) > 0: + # qui dovrei inserire il contrtrollo sul boolean + # 'has_test_file' contenuto nel repository + return True + else: + return False + + # ========== # + + # CELL LEVEL # # ========== # @@ -386,6 +406,13 @@ def long_multiline_python_comment(notebook: Notebook) -> List[Cell]: "that all cells are executed.", linting_function=non_executed_notebook, ), + LintDefinition( + slug="function-or-class-not-tested", + description="the notebook contains untested functions or classes.", + recommendation="Before committing, be sure to always test your " + "functions and classes for greater code coverage.", + linting_function=function_or_class_never_tested, + ), ] cell_level_lints: List[LintDefinition] = [ diff --git a/pynblint/repository.py b/pynblint/repository.py index ae8c016..ac00c6e 100644 --- a/pynblint/repository.py +++ b/pynblint/repository.py @@ -23,6 +23,14 @@ def __init__(self, path: Path): # Extracted content self.notebooks: List[Notebook] = [] # List of Notebook objects + self.has_test_file: bool = self._has_test_file() + + def _has_test_file(self) -> bool: + final_path = os.path.join(self.path, "coverage.py") + if os.path.exists(final_path): + return True + else: + return False def retrieve_notebooks(self): @@ -114,7 +122,6 @@ class GitHubRepository(Repository): """ def __init__(self, github_url: str): - self.url = github_url # Clone the repo in a temp directory diff --git a/tests/unit/test_nb_linting_functions.py b/tests/unit/test_nb_linting_functions.py index 2cc2849..e734e8d 100644 --- a/tests/unit/test_nb_linting_functions.py +++ b/tests/unit/test_nb_linting_functions.py @@ -152,3 +152,11 @@ def test_long_filename(test_input, expected, notebooks): ) def test_invalid_python_syntax(test_input, expected, notebooks): assert nb_linting.invalid_python_syntax(notebooks[test_input]) == expected + + +@pytest.mark.parametrize( + "test_input, expected", + [("FullNotebook2.ipynb", True), ("NotebookBackupCopy.ipynb", False)], +) +def test_function_or_class_never_tested(test_input, expected, notebooks): + assert nb_linting.function_or_class_never_tested(notebooks[test_input]) == expected From de999b1d76fcd916e4e2ebf3cead45c2ddc8c096 Mon Sep 17 00:00:00 2001 From: Felice644 Date: Thu, 19 May 2022 22:35:46 +0200 Subject: [PATCH 2/2] - Added comment to the function. --- pynblint/nb_linting.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pynblint/nb_linting.py b/pynblint/nb_linting.py index c710a70..e6f5d61 100644 --- a/pynblint/nb_linting.py +++ b/pynblint/nb_linting.py @@ -232,7 +232,15 @@ def non_executed_notebook(notebook: Notebook) -> bool: def function_or_class_never_tested(notebook: Notebook) -> bool: - """Check whether the notebook have function or class not tested""" + """Check whether the notebook have function or class not tested + + Args: + notebook (Notebook): the notebook to be analyzed. + + Returns: + bool: ``True`` if the notebook contains class or function + but not the coverage file; ``False`` otherwise. + """ func_class_set = set() for node in ast.walk(notebook.ast): @@ -241,7 +249,6 @@ def function_or_class_never_tested(notebook: Notebook) -> bool: func_class_set.add(name.split(".")[0]) elif isinstance(node, ast.ClassDef): func_class_set.add(node.name.split(".")[0]) - if len(func_class_set) > 0: # qui dovrei inserire il contrtrollo sul boolean # 'has_test_file' contenuto nel repository