diff --git a/.idea/api-editor.iml b/.idea/api-editor.iml
index d6ebd4805..fe90391a9 100644
--- a/.idea/api-editor.iml
+++ b/.idea/api-editor.iml
@@ -2,8 +2,14 @@
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index cce1d8640..ba941c14e 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -2,6 +2,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 09adcc7f2..4776a1dbc 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/package-parser/README.md b/package-parser/README.md
index 7c6223adb..2e1dcbecc 100644
--- a/package-parser/README.md
+++ b/package-parser/README.md
@@ -59,6 +59,19 @@ optional arguments:
-m MIN, --min MIN Minimum number of usages required to keep an API element.
```
+### generate command
+
+```text
+usage: parse-package generate [-h] -a API -u USAGES -o OUT
+
+optional arguments:
+ -h, --help show this help message and exit
+ -a API, --api API File created by the 'api' command.
+ -u USAGES, --usages USAGES
+ File created by the 'usages' command.
+ -o OUT, --out OUT Output directory.
+```
+
### Example usage
1. Install Python 3.9.
diff --git a/package-parser/package-parser.iml b/package-parser/package-parser.iml
index 0763b9b57..4c2e3eaae 100644
--- a/package-parser/package-parser.iml
+++ b/package-parser/package-parser.iml
@@ -8,7 +8,13 @@
-
+
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-parser/package_parser/cli.py b/package-parser/package_parser/cli.py
index c82426cf3..8b5b50434 100644
--- a/package-parser/package_parser/cli.py
+++ b/package-parser/package_parser/cli.py
@@ -5,6 +5,7 @@
from typing import Any
from .commands.find_usages import find_usages
+from .commands.generate_annotations.generate_annotations import generate_annotations
from .commands.get_api import distribution, distribution_version, get_api
from .commands.get_dependencies import get_dependencies
from .commands.suggest_improvements import suggest_improvements
@@ -13,6 +14,7 @@
__API_COMMAND = "api"
__USAGES_COMMAND = "usages"
__IMPROVE_COMMAND = "improve"
+__GENERATE_COMMAND = "generate"
class CustomEncoder(json.JSONEncoder):
@@ -55,6 +57,9 @@ def cli() -> None:
elif args.command == __IMPROVE_COMMAND:
suggest_improvements(args.api, args.usages, args.out, args.min)
+ elif args.command == __GENERATE_COMMAND:
+ generate_annotations(args.api, args.usages, args.out)
+
def __get_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Analyze Python code.")
@@ -64,6 +69,7 @@ def __get_args() -> argparse.Namespace:
__add_api_subparser(subparsers)
__add_usages_subparser(subparsers)
__add_improve_subparser(subparsers)
+ __add_generate_subparser(subparsers)
return parser.parse_args()
@@ -141,3 +147,26 @@ def __add_improve_subparser(subparsers: _SubParsersAction) -> None:
required=False,
default=1,
)
+
+
+def __add_generate_subparser(subparsers):
+ generate_parser = subparsers.add_parser(
+ __GENERATE_COMMAND, help="Generate Annotations automatically."
+ )
+ generate_parser.add_argument(
+ "-a",
+ "--api",
+ help="File created by the 'api' command.",
+ type=argparse.FileType("r"),
+ required=True,
+ )
+ generate_parser.add_argument(
+ "-u",
+ "--usages",
+ help="File created by the 'usages' command.",
+ type=argparse.FileType("r"),
+ required=True,
+ )
+ generate_parser.add_argument(
+ "-o", "--out", help="Output directory.", type=Path, required=True
+ )
diff --git a/package-parser/package_parser/commands/generate_annotations/__init__.py b/package-parser/package_parser/commands/generate_annotations/__init__.py
index 01f2fc13b..e69de29bb 100644
--- a/package-parser/package_parser/commands/generate_annotations/__init__.py
+++ b/package-parser/package_parser/commands/generate_annotations/__init__.py
@@ -1,2 +0,0 @@
-from ._combine import write_json
-from ._generate_annotations import generate_annotations
diff --git a/package-parser/package_parser/commands/generate_annotations/_combine.py b/package-parser/package_parser/commands/generate_annotations/_combine.py
deleted file mode 100644
index cdc40e5b1..000000000
--- a/package-parser/package_parser/commands/generate_annotations/_combine.py
+++ /dev/null
@@ -1,102 +0,0 @@
-import argparse
-import json
-import os
-
-
-def write_json(output_path: str, constant_path: str, unused_path: str) -> None:
- """
- Dient zum Mergen von Unused-Dictionary und Constant-Dictionary und anschließende Erzeugen einer JSON - File,
- die das erzeugte Dictionary beinhaltet.
-
- :param output_path: Dateipfad für Output-JSON aus beiden Dictionaries(Unused und Constant)
- :param constant_path: Dateipfad zur Constant Annotation File, die eine Constant Dictionary enthält
- :param unused_path: Dateipfad zur Unused Annotation File, die eine Unused Dictionary enthält
- """
- # Platzhalter für echte Funktion
- # unused_dict = find_unused(unused_path)
-
- # entfernen, wenn Funktion aus Issue 433 fertig
- unused_dict = {
- "sklearn/sklearn.__check_build/raise_build_error": {
- "target": "sklearn/sklearn.__check_build/raise_build_error"
- }
- }
- # Platzhalter für echte Funktion
- # constant_dict = find_constant(constant_path)
-
- # entfernen, wenn Funktion aus Issue 434 fertig
- constant_dict = {
- "sklearn/sklearn._config/config_context/assume_finite": {
- "target": "sklearn/sklearn._config/config_context/assume_finite",
- "defaultType": "boolean",
- "defaultValue": True,
- },
- "sklearn/sklearn._config/config_context/working_memory": {
- "target": "sklearn/sklearn._config/config_context/working_memory",
- "defaultType": "string",
- "defaultValue": "bla",
- },
- "sklearn/sklearn._config/config_context/print_changed_only": {
- "target": "sklearn/sklearn._config/config_context/print_changed_only",
- "defaultType": "none",
- "defaultValue": None,
- },
- "sklearn/sklearn._config/config_context/display": {
- "target": "sklearn/sklearn._config/config_context/display",
- "defaultType": "number",
- "defaultValue": "3",
- },
- }
- result_dict = __combine_dictionaries(unused_dict, constant_dict)
-
- with open(os.path.join(output_path, "annotations.json"), "w") as file:
- json.dump(
- result_dict,
- file,
- indent=2,
- )
-
-
-def __combine_dictionaries(unused_dict: dict, constant_dict: dict) -> dict:
- """
- Funktion, die die Dictionaries kombiniert
- :param unused_dict : Dictionary der unused annotations
- :param constant_dict : Dictionary der constant annotations
- :return result_dict : Kombiniertes Dictionary
- """
-
- result_dict = {
- "unused": unused_dict,
- "constant": constant_dict,
- }
- return result_dict
-
-
-# sollte, sobald final eingebunden wird entfernt werden, da es nicht weiter benötigt wird, dient zZ. nur zur Anschauung
-if __name__ == "__main__":
- parser = argparse.ArgumentParser(prog="combine")
-
- # Argument 1: outputPath
- parser.add_argument(
- "outputPath",
- metavar="output-filepath",
- type=str,
- help="paste the location of the output file in here ",
- )
- # Argument 2: inputPath(Pfad einer Constant-Datei)
- parser.add_argument(
- "constantPath",
- metavar="input-filepath",
- type=str,
- help='paste the location of the "constant" file in here ',
- )
- # Argument 3: inputPath(Pfad einer Unused-Datei)
- parser.add_argument(
- "unusedPath",
- metavar="input-filepath",
- type=str,
- help='paste the location of the "unused" file in here ',
- )
- # Erzeuge kombinierte JSON-Datei jeweils aus der Constant- und der Unused-Dictionary
- args = parser.parse_args() # Argumente werden auf Nutzen als Parameter vorbereitet
- write_json(args.outputPath, args.constantPath, args.unusedPath)
diff --git a/package-parser/package_parser/commands/generate_annotations/_generate_unused_annotations.py b/package-parser/package_parser/commands/generate_annotations/_generate_unused_annotations.py
deleted file mode 100644
index 59b40a90d..000000000
--- a/package-parser/package_parser/commands/generate_annotations/_generate_unused_annotations.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import json
-import re
-from typing import Dict, List, Tuple
-
-
-def generate_unused_annotations(in_file_path: str):
- """
- Returns a Dict of unused functions or classes
-
- :param in_file_path: JSON file that contains a list of unused functions or classes
- """
-
- with open(in_file_path, "r", encoding="UTF-8") as in_file:
- data = json.load(in_file)
-
- unuseds: Dict[str, Dict[str, str]] = {}
- for name in data:
- formatted_name = format_name(name)
- unuseds[formatted_name] = {"target": formatted_name}
-
- return unuseds
-
-
-def format_name(name: str):
- if name is None:
- return None
-
- parts = re.split("\\.", name)
- newname = "sklearn/" + parts[0]
-
- if len(parts) == 1:
- return newname
-
- slash = False
- for part in parts[1:-1]:
- if not slash and re.match("^_{0,2}[A-Z]", part):
- slash = True
- if slash:
- newname += "/" + part
- else:
- newname += "." + part
-
- newname += "/" + parts[-1]
- return newname
diff --git a/package-parser/package_parser/commands/generate_annotations/_generate_annotations.py b/package-parser/package_parser/commands/generate_annotations/generate_annotations.py
similarity index 62%
rename from package-parser/package_parser/commands/generate_annotations/_generate_annotations.py
rename to package-parser/package_parser/commands/generate_annotations/generate_annotations.py
index 3a2ee1f39..7f97cb513 100644
--- a/package-parser/package_parser/commands/generate_annotations/_generate_annotations.py
+++ b/package-parser/package_parser/commands/generate_annotations/generate_annotations.py
@@ -1,21 +1,27 @@
import json
from io import TextIOWrapper
from pathlib import Path
-from typing import Any
-
-from package_parser.commands.find_usages import (
- ClassUsage,
- FunctionUsage,
- UsageStore,
- ValueUsage,
-)
+from typing import Callable
+
+from package_parser.commands.find_usages import UsageStore
from package_parser.commands.get_api import API
from package_parser.utils import parent_qname
def generate_annotations(
- api_file: TextIOWrapper, usages_file: TextIOWrapper, out_dir: Path
-):
+ api_file: TextIOWrapper, usages_file: TextIOWrapper, output_file: Path
+) -> None:
+ """
+ Generates an annotation file from the given API and UsageStore files, and writes it to the given output file.
+ Annotations that are generated are: constant, unused,
+ :param api_file: API file
+ :param usages_file: UsageStore file
+ :param output_file: Output file
+ :return: None
+ """
+ if api_file is None or usages_file is None or output_file is None:
+ raise ValueError("api_file, usages_file, and output_file must be specified.")
+
with api_file:
api_json = json.load(api_file)
api = API.from_json(api_json)
@@ -24,15 +30,143 @@ def generate_annotations(
usages_json = json.load(usages_file)
usages = UsageStore.from_json(usages_json)
- # out_dir.mkdir(parents=True, exist_ok=True)
- # base_file_name = api_file.name.replace("__api.json", "")
+ annotation_functions = [__get_unused_annotations, __get_constant_annotations]
+
+ annotations_dict = __generate_annotation_dict(api, usages, annotation_functions)
+
+ with output_file.open("w") as f:
+ json.dump(annotations_dict, f, indent=2)
+
+
+def __generate_annotation_dict(api: API, usages: UsageStore, functions: list[Callable]):
+ _preprocess_usages(usages, api)
+
+ annotations_dict: dict[str, dict[str, dict[str, str]]] = {}
+ for generate_annotation in functions:
+ annotations_dict.update(generate_annotation(usages, api))
+
+ return annotations_dict
+
+
+def __get_constant_annotations(
+ usages: UsageStore, api: API
+) -> dict[str, dict[str, dict[str, str]]]:
+ """
+ Returns all parameters that are only ever assigned a single value.
+ :param usages: UsageStore object
+ :param api: API object for usages
+ :return: {"constant": dict[str, dict[str, str]]}
+ """
+ constant = "constant"
+ constants: dict[str, dict[str, str]] = {}
+
+ for parameter_qname in list(usages.parameter_usages.keys()):
+ if len(usages.value_usages[parameter_qname].values()) == 0:
+ continue
+
+ if len(usages.value_usages[parameter_qname].keys()) == 1:
+ if usages.most_common_value(parameter_qname) is None:
+ continue
+
+ target_name = __qname_to_target_name(api, parameter_qname)
+ default_type, default_value = __get_default_type_from_value(
+ str(usages.most_common_value(parameter_qname))
+ )
+
+ constants[target_name] = {
+ "target": target_name,
+ "defaultType": default_type,
+ "defaultValue": default_value,
+ }
+
+ return {constant: constants}
+
+
+def __get_unused_annotations(
+ usages: UsageStore, api: API
+) -> dict[str, dict[str, dict[str, str]]]:
+ """
+ Returns all parameters that are never used.
+ :param usages: UsageStore object
+ :param api: API object for usages
+ :return: {"unused": dict[str, dict[str, str]]}
+ """
+ unused = "unused"
+ unuseds: dict[str, dict[str, str]] = {}
+
+ for parameter_name in list(api.parameters().keys()):
+ if (
+ parameter_name not in usages.parameter_usages
+ or len(usages.parameter_usages[parameter_name]) == 0
+ ):
+ formatted_name = __qname_to_target_name(api, parameter_name)
+ unuseds[formatted_name] = {"target": formatted_name}
+
+ for function_name in list(api.functions.keys()):
+ if (
+ function_name not in usages.function_usages
+ or len(usages.function_usages[function_name]) == 0
+ ):
+ formatted_name = __qname_to_target_name(api, function_name)
+ unuseds[formatted_name] = {"target": formatted_name}
+
+ for class_name in list(api.classes.keys()):
+ if (
+ class_name not in usages.class_usages
+ or len(usages.class_usages[class_name]) == 0
+ ):
+ formatted_name = __qname_to_target_name(api, class_name)
+ unuseds[formatted_name] = {"target": formatted_name}
+
+ return {unused: unuseds}
+
+
+def __qname_to_target_name(api: API, qname: str) -> str:
+ """
+ Formats the given name to the wanted format. This method is to be removed as soon as the UsageStore is updated to
+ use the new format.
+ :param api: API object
+ :param qname: Name pre-formatting
+ :return: Formatted name
+ """
+ if qname is None or api is None:
+ raise ValueError("qname and api must be specified.")
+
+ target_elements = qname.split(".")
+
+ package_name = api.package
+ module_name = class_name = function_name = parameter_name = ""
+
+ if ".".join(target_elements) in api.parameters().keys():
+ parameter_name = "/" + target_elements.pop()
+ if ".".join(target_elements) in api.functions.keys():
+ function_name = f"/{target_elements.pop()}"
+ if ".".join(target_elements) in api.classes.keys():
+ class_name = f"/{target_elements.pop()}"
+ if ".".join(target_elements) in api.modules.keys():
+ module_name = "/" + ".".join(target_elements)
+
+ return package_name + module_name + class_name + function_name + parameter_name
+
+
+def __get_default_type_from_value(default_value: str) -> tuple[str, str]:
+ default_value = str(default_value)[1:-1]
+
+ if default_value == "null":
+ default_type = "none"
+ elif default_value == "True" or default_value == "False":
+ default_type = "boolean"
+ elif default_value.isnumeric():
+ default_type = "number"
+ default_value = default_value
+ else:
+ default_type = "string"
+ default_value = default_value
- __preprocess_usages(usages, api)
- constant_parameters = __find_constant_parameters(usages, api)
- return constant_parameters
+ return default_type, default_value
-def __preprocess_usages(usages: UsageStore, api: API) -> None:
+def _preprocess_usages(usages: UsageStore, api: API) -> None:
__remove_internal_usages(usages, api)
__add_unused_api_elements(usages, api)
__add_implicit_usages_of_default_value(usages, api)
@@ -121,70 +255,3 @@ def __add_implicit_usages_of_default_value(usages: UsageStore, api: API) -> None
for location in locations_of_implicit_usages_of_default_value:
usages.add_value_usage(parameter_qname, default_value, location)
-
-
-def __find_constant_parameters(
- usages: UsageStore, api: API
-) -> dict[str, dict[str, str]]:
- """
- Returns all parameters that are only ever assigned a single value.
-
- :param usages: Usage store
- """
-
- result = {}
-
- for parameter_qname in list(usages.parameter_usages.keys()):
-
- if len(usages.value_usages[parameter_qname].values()) == 0:
- continue
-
- if len(usages.value_usages[parameter_qname].keys()) == 1:
- target_name = __qname_to_target_name(api, parameter_qname)
- default_type, default_value = __get_default_type_from_value(
- str(usages.most_common_value(parameter_qname))
- )
- print(target_name)
- result[target_name] = {
- "target": target_name,
- "defaultType": default_type,
- "defaultValue": default_value,
- }
-
- print(json.dumps(result))
- return result
-
-
-def __qname_to_target_name(api: API, qname: str) -> str:
- target_elements = qname.split(".")
-
- package_name = api.package
- module_name = class_name = function_name = parameter_name = ""
-
- if ".".join(target_elements) in api.parameters().keys():
- parameter_name = "/" + target_elements.pop()
- if ".".join(target_elements) in api.functions.keys():
- function_name = f"/{target_elements.pop()}"
- if ".".join(target_elements) in api.classes.keys():
- class_name = f"/{target_elements.pop()}"
- if ".".join(target_elements) in api.modules.keys():
- module_name = "/" + ".".join(target_elements)
-
- return package_name + module_name + class_name + function_name + parameter_name
-
-
-def __get_default_type_from_value(default_value: str) -> tuple[str, str]:
- default_value = str(default_value)[1:-1]
-
- if default_value == "null":
- default_type = "none"
- elif default_value == "True" or default_value == "False":
- default_type = "boolean"
- elif default_value.isnumeric():
- default_type = "number"
- default_value = default_value
- else:
- default_type = "string"
- default_value = default_value
-
- return default_type, default_value
diff --git a/package-parser/tests/commands/generate_annotations/annotations.json b/package-parser/tests/commands/generate_annotations/annotations.json
deleted file mode 100644
index 5e53ce458..000000000
--- a/package-parser/tests/commands/generate_annotations/annotations.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "unused": {
- "sklearn/sklearn.__check_build/raise_build_error": {
- "target": "sklearn/sklearn.__check_build/raise_build_error"
- }
- },
- "constant": {
- "sklearn/sklearn._config/config_context/assume_finite": {
- "target": "sklearn/sklearn._config/config_context/assume_finite",
- "defaultType": "boolean",
- "defaultValue": true
- },
- "sklearn/sklearn._config/config_context/working_memory": {
- "target": "sklearn/sklearn._config/config_context/working_memory",
- "defaultType": "string",
- "defaultValue": "bla"
- },
- "sklearn/sklearn._config/config_context/print_changed_only": {
- "target": "sklearn/sklearn._config/config_context/print_changed_only",
- "defaultType": "none",
- "defaultValue": null
- },
- "sklearn/sklearn._config/config_context/display": {
- "target": "sklearn/sklearn._config/config_context/display",
- "defaultType": "number",
- "defaultValue": "3"
- }
- }
-}
diff --git a/package-parser/tests/commands/generate_annotations/test_combine.py b/package-parser/tests/commands/generate_annotations/test_combine.py
deleted file mode 100644
index 45c901b0f..000000000
--- a/package-parser/tests/commands/generate_annotations/test_combine.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import json
-import os
-
-import package_parser.commands.generate_annotations._combine as comb
-import pytest
-
-test_unused_dict = {
- "sklearn/sklearn.__check_build/raise_build_error": {
- "target": "sklearn/sklearn.__check_build/raise_build_error"
- }
-}
-
-test_constant_dict = {
- "sklearn/sklearn._config/config_context/assume_finite": {
- "target": "sklearn/sklearn._config/config_context/assume_finite",
- "defaultType": "boolean",
- "defaultValue": True,
- },
- "sklearn/sklearn._config/config_context/working_memory": {
- "target": "sklearn/sklearn._config/config_context/working_memory",
- "defaultType": "string",
- "defaultValue": "bla",
- },
- "sklearn/sklearn._config/config_context/print_changed_only": {
- "target": "sklearn/sklearn._config/config_context/print_changed_only",
- "defaultType": "none",
- "defaultValue": None,
- },
- "sklearn/sklearn._config/config_context/display": {
- "target": "sklearn/sklearn._config/config_context/display",
- "defaultType": "number",
- "defaultValue": "3",
- },
-}
-
-test_combined_dict = {
- "unused": {
- "sklearn/sklearn.__check_build/raise_build_error": {
- "target": "sklearn/sklearn.__check_build/raise_build_error"
- }
- },
- "constant": {
- "sklearn/sklearn._config/config_context/assume_finite": {
- "target": "sklearn/sklearn._config/config_context/assume_finite",
- "defaultType": "boolean",
- "defaultValue": True,
- },
- "sklearn/sklearn._config/config_context/working_memory": {
- "target": "sklearn/sklearn._config/config_context/working_memory",
- "defaultType": "string",
- "defaultValue": "bla",
- },
- "sklearn/sklearn._config/config_context/print_changed_only": {
- "target": "sklearn/sklearn._config/config_context/print_changed_only",
- "defaultType": "none",
- "defaultValue": None,
- },
- "sklearn/sklearn._config/config_context/display": {
- "target": "sklearn/sklearn._config/config_context/display",
- "defaultType": "number",
- "defaultValue": "3",
- },
- },
-}
-
-
-@pytest.mark.parametrize(
- "test_unused, test_constant, expected",
- [
- (test_unused_dict, test_constant_dict, test_combined_dict),
- ],
-)
-def test_combine_dictionaries(test_unused, test_constant, expected):
- """
- Funktion, die feststellt ob die kombinierte JSON-Datei gleich der gefragten JSON-Datei ist oder nicht
- :param test_unused: Unused Dictionary als Parameter
- :param test_constant: Constant Dictionary als Parameter
- :param expected: gemergte JSON-Datei aus den 2 Dictionaries, die entsteht
- """
-
- eval_dict = comb.__combine_dictionaries(test_unused, test_constant)
- assert eval_dict == expected
diff --git a/package-parser/tests/commands/generate_annotations/test_determine_constant_parameters.py b/package-parser/tests/commands/generate_annotations/test_determine_constant_parameters.py
deleted file mode 100644
index 2c9807114..000000000
--- a/package-parser/tests/commands/generate_annotations/test_determine_constant_parameters.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import json
-import os
-
-import pytest
-from package_parser.commands.find_usages._model import UsageStore
-from package_parser.commands.generate_annotations._generate_annotations import (
- generate_annotations,
-)
-
-# Expected output:
-# @Unused annotations should be created for the following declarations:
-#
-# test.Unused_Class
-# test.unused_global_function
-# test.Commonly_Used_Class.unused_method
-#
-# @Constant annotations should be created for the following parameters:
-#
-# test.commonly_used_global_function.useless_required_parameter (with value "'blup'")
-# test.commonly_used_global_function.unused_optional_parameter (with value "'bla'", i.e. the default value)
-# test.commonly_used_global_function.useless_optional_parameter (with value "'bla'")
-
-
-def test_determination_of_constant_parameters():
-
- expected = {
- "test/test/commonly_used_global_function/useless_required_parameter": {
- "target": "test/test/commonly_used_global_function/useless_required_parameter",
- "defaultType": "string",
- "defaultValue": "blup",
- },
- "test/test/commonly_used_global_function/unused_optional_parameter": {
- "target": "test/test/commonly_used_global_function/unused_optional_parameter",
- "defaultType": "string",
- "defaultValue": "bla",
- },
- "test/test/commonly_used_global_function/useless_optional_parameter": {
- "target": "test/test/commonly_used_global_function/useless_optional_parameter",
- "defaultType": "string",
- "defaultValue": "bla",
- },
- }
-
- api_json_path = os.path.join(
- os.getcwd(), "tests", "data", "constant", "api_data.json"
- )
- usages_json_path = os.path.join(
- os.getcwd(), "tests", "data", "constant", "usage_data.json"
- )
-
- api_file = open(api_json_path)
- usages_file = open(usages_json_path)
-
- constant_parameters = generate_annotations(api_file, usages_file, "/.")
-
- api_file.close()
- usages_file.close()
-
- assert constant_parameters == expected
diff --git a/package-parser/tests/commands/generate_annotations/test_generate_annotations.py b/package-parser/tests/commands/generate_annotations/test_generate_annotations.py
new file mode 100644
index 000000000..82324f31b
--- /dev/null
+++ b/package-parser/tests/commands/generate_annotations/test_generate_annotations.py
@@ -0,0 +1,134 @@
+import json
+import os
+from pathlib import Path
+
+import pytest
+from package_parser.commands.find_usages import UsageStore
+from package_parser.commands.generate_annotations.generate_annotations import (
+ __get_constant_annotations,
+ __get_unused_annotations,
+ __qname_to_target_name,
+ _preprocess_usages,
+ generate_annotations,
+)
+from package_parser.commands.get_api import API
+
+UNUSED_EXPECTED = {
+ "unused": {
+ "test/test/Unused_Class": {"target": "test/test/Unused_Class"},
+ "test/test/commonly_used_global_function/unused_optional_parameter": {
+ "target": "test/test/commonly_used_global_function/unused_optional_parameter"
+ },
+ "test/test/unused_global_function": {
+ "target": "test/test/unused_global_function"
+ },
+ "test/test/unused_global_function/unused_optional_parameter": {
+ "target": "test/test/unused_global_function/unused_optional_parameter"
+ },
+ "test/test/unused_global_function/unused_required_parameter": {
+ "target": "test/test/unused_global_function/unused_required_parameter"
+ },
+ }
+}
+
+CONSTANT_EXPECTED = {
+ "constant": {
+ "test/test/commonly_used_global_function/unused_optional_parameter": {
+ "defaultType": "string",
+ "defaultValue": "bla",
+ "target": "test/test/commonly_used_global_function/unused_optional_parameter",
+ },
+ "test/test/commonly_used_global_function/useless_optional_parameter": {
+ "defaultType": "string",
+ "defaultValue": "bla",
+ "target": "test/test/commonly_used_global_function/useless_optional_parameter",
+ },
+ "test/test/commonly_used_global_function/useless_required_parameter": {
+ "defaultType": "string",
+ "defaultValue": "blup",
+ "target": "test/test/commonly_used_global_function/useless_required_parameter",
+ },
+ }
+}
+
+# Reihenfolge ist wichtig, siehe Reihenfolge von annotation_functions in generate_annotations.py
+FULL_EXPECTED = {**UNUSED_EXPECTED, **CONSTANT_EXPECTED}
+
+
+def setup():
+ api_json_path = os.path.join(os.getcwd(), "tests", "data", "api_data.json")
+ usages_json_path = os.path.join(os.getcwd(), "tests", "data", "usage_data.json")
+
+ with open(api_json_path, "r") as api_file:
+ api_json = json.load(api_file)
+ api = API.from_json(api_json)
+
+ with open(usages_json_path, "r") as usages_file:
+ usages_json = json.load(usages_file)
+ usages = UsageStore.from_json(usages_json)
+
+ return usages, api, usages_file, api_file, usages_json_path, api_json_path
+
+
+def test_format_function():
+ usages, api, usages_file, api_file, usages_json_path, api_json_path = setup()
+ assert (
+ __qname_to_target_name(api, "test.unused_global_function")
+ == "test/test/unused_global_function"
+ )
+
+
+def test_format_parameter():
+ usages, api, usages_file, api_file, usages_json_path, api_json_path = setup()
+ assert (
+ __qname_to_target_name(
+ api, "test.commonly_used_global_function.useless_required_parameter"
+ )
+ == "test/test/commonly_used_global_function/useless_required_parameter"
+ )
+
+
+def test_format_none():
+ usages, api, usages_file, api_file, usages_json_path, api_json_path = setup()
+ with pytest.raises(ValueError):
+ __qname_to_target_name(None, "test")
+ with pytest.raises(ValueError):
+ __qname_to_target_name(api, None)
+
+
+def test_get_unused():
+ usages, api, usages_file, api_file, usages_json_path, api_json_path = setup()
+ _preprocess_usages(usages, api)
+ assert __get_unused_annotations(usages, api) == UNUSED_EXPECTED
+
+
+def test_get_constant():
+ usages, api, usages_file, api_file, usages_json_path, api_json_path = setup()
+ _preprocess_usages(usages, api)
+ assert __get_constant_annotations(usages, api) == CONSTANT_EXPECTED
+
+
+def test_generate():
+ usages, api, usages_file, api_file, usages_json_path, api_json_path = setup()
+ out_file_path = os.path.join(
+ os.getcwd(), "tests", "out", "test_generate_out_file.json"
+ )
+
+ if not os.path.exists(os.path.join(os.getcwd(), "tests", "out")):
+ os.makedirs(os.path.join(os.getcwd(), "tests", "out"))
+
+ if not os.path.exists(out_file_path) or not os.path.isfile(out_file_path):
+ with open(out_file_path, "x") as out_file:
+ out_file.write("")
+
+ generate_annotations(
+ open(api_json_path, "r"), open(usages_json_path, "r"), Path(out_file_path)
+ )
+ with open(out_file_path, "r") as out_file:
+ out_json = json.load(out_file)
+ assert out_json == FULL_EXPECTED
+
+
+def test_generate_bad_path():
+ with pytest.raises(ValueError):
+ generate_annotations(None, None, None)
diff --git a/package-parser/tests/commands/generate_annotations/test_generate_unused_annotations.py b/package-parser/tests/commands/generate_annotations/test_generate_unused_annotations.py
deleted file mode 100644
index 1c6a74ae6..000000000
--- a/package-parser/tests/commands/generate_annotations/test_generate_unused_annotations.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import pytest
-from package_parser.commands.generate_annotations._generate_unused_annotations import (
- format_name,
- generate_unused_annotations,
-)
-
-EXPECTED_VALUE = {
- "sklearn/sklearn.base/_BaseEstimator/__setstate__": {
- "target": "sklearn/sklearn.base/_BaseEstimator/__setstate__"
- },
- "sklearn/sklearn.base/is_regressor": {
- "target": "sklearn/sklearn.base/is_regressor"
- },
- "sklearn/sklearn.cluster._agglomerative/linkage_tree": {
- "target": "sklearn/sklearn.cluster._agglomerative/linkage_tree"
- },
- "sklearn/sklearn.cluster._kmeans/MiniBatchKMeans/init_size_": {
- "target": "sklearn/sklearn.cluster._kmeans/MiniBatchKMeans/init_size_"
- },
-}
-
-
-def test_format_underscores():
- assert (
- format_name("sklearn.cluster._kmeans._MiniBatchKMeans.random_state_")
- == "sklearn/sklearn.cluster._kmeans/_MiniBatchKMeans/random_state_"
- )
-
-
-def test_format_uppercase():
- assert (
- format_name("sklearn.cluster._kmeans.MiniBatchKMeans.random_state_")
- == "sklearn/sklearn.cluster._kmeans/MiniBatchKMeans/random_state_"
- )
-
-
-def test_format_normal():
- assert (
- format_name("sklearn.cluster._mean_shift.get_bin_seeds")
- == "sklearn/sklearn.cluster._mean_shift/get_bin_seeds"
- )
-
-
-def test_format_one_part():
- assert format_name("test") == "sklearn/test"
-
-
-def test_format_none():
- assert format_name(None) is None
-
-
-def test_format_empty():
- assert format_name("") == "sklearn/"
-
-
-def test_generate():
- assert (
- generate_unused_annotations(
- "tests/commands/generate_annotations/unused_functions_list.json"
- )
- == EXPECTED_VALUE
- )
-
-
-def test_generate_bad_path():
- with pytest.raises(FileNotFoundError):
- generate_unused_annotations("aaaaaaaaaaaAAAAAAAAAAAA")
diff --git a/package-parser/tests/commands/generate_annotations/unused_functions_list.json b/package-parser/tests/commands/generate_annotations/unused_functions_list.json
deleted file mode 100644
index e4e850adc..000000000
--- a/package-parser/tests/commands/generate_annotations/unused_functions_list.json
+++ /dev/null
@@ -1,6 +0,0 @@
-[
- "sklearn.base._BaseEstimator.__setstate__",
- "sklearn.base.is_regressor",
- "sklearn.cluster._agglomerative.linkage_tree",
- "sklearn.cluster._kmeans.MiniBatchKMeans.init_size_"
-]
diff --git a/package-parser/tests/data/constant/api_data.json b/package-parser/tests/data/api_data.json
similarity index 72%
rename from package-parser/tests/data/constant/api_data.json
rename to package-parser/tests/data/api_data.json
index d4bca8ec1..8c09e7e42 100644
--- a/package-parser/tests/data/constant/api_data.json
+++ b/package-parser/tests/data/api_data.json
@@ -7,14 +7,57 @@
"name": "test",
"imports": [],
"from_imports": [],
- "classes": [],
+ "classes": [
+ "test.Unused_Class",
+ "test.Rarely_Used_Class",
+ "test.Commonly_Used_Class"
+ ],
"functions": [
"test.unused_global_function",
+ "test.rarely_used_global_function",
"test.commonly_used_global_function"
]
}
],
- "classes": [],
+ "classes": [
+ {
+ "name": "Unused_Class",
+ "qname": "test.Unused_Class",
+ "decorators": [],
+ "is_public": true,
+ "description": "",
+ "docstring": "",
+ "superclasses": [],
+ "source_code": "",
+ "methods": []
+ },
+ {
+ "name": "Rarely_Used_Class",
+ "qname": "test.Rarely_Used_Class",
+ "decorators": [],
+ "is_public": true,
+ "description": "This class is used rarely",
+ "docstring": "This class is used rarely",
+ "superclasses": [],
+ "source_code": "",
+ "methods": []
+ },
+ {
+ "name": "Commonly_Used_Class",
+ "qname": "test.Commonly_Used_Class",
+ "decorators": [],
+ "is_public": true,
+ "description": "This class is used commonly",
+ "docstring": "This class is used commonly",
+ "superclasses": [],
+ "source_code": "",
+ "methods": [
+ "test.Commonly_Used_Class.unused_method",
+ "test.Commonly_Used_Class.rarely_used_method",
+ "test.Commonly_Used_Class.commonly_used_method"
+ ]
+ }
+ ],
"functions": [
{
"name": "unused_global_function",
diff --git a/package-parser/tests/data/unused/api_data.json b/package-parser/tests/data/unused/api_data.json
deleted file mode 100644
index 3164ee5fb..000000000
--- a/package-parser/tests/data/unused/api_data.json
+++ /dev/null
@@ -1,129 +0,0 @@
-{
- "distribution": "test",
- "package": "test",
- "version": "0.0.1",
- "modules": [
- {
- "name": "test",
- "imports": [],
- "from_imports": [],
- "classes": [
- "test.Unused_Class",
- "test.Rarely_Used_Class",
- "test.Commonly_Used_Class"
- ],
- "functions": [
- "test.unused_global_function",
- "test.rarely_used_global_function",
- "test.commonly_used_global_function"
- ]
- }
- ],
- "classes": [
- {
- "name": "Unused_Class",
- "qname": "test.Unused_Class",
- "decorators": [],
- "superclasses": [],
- "methods": []
- },
- {
- "name": "Rarely_Used_Class",
- "qname": "test.Rarely_Used_Class",
- "decorators": [],
- "superclasses": [],
- "methods": []
- },
- {
- "name": "Commonly_Used_Class",
- "qname": "test.Commonly_Used_Class",
- "decorators": [],
- "superclasses": [],
- "methods": [
- "test.Commonly_Used_Class.unused_method",
- "test.Commonly_Used_Class.rarely_used_method",
- "test.Commonly_Used_Class.commonly_used_method"
- ]
- }
- ],
- "functions": [
- {
- "name": "unused_global_function",
- "unique_name": "unused_global_function",
- "qname": "test.unused_global_function",
- "unique_qname": "test.unused_global_function",
- "decorators": [],
- "parameters": [],
- "results": [],
- "is_public": true,
- "description": "",
- "docstring": "",
- "source_code": ""
- },
- {
- "name": "rarely_used_global_function",
- "unique_name": "rarely_used_global_function",
- "qname": "test.rarely_used_global_function",
- "unique_qname": "test.rarely_used_global_function",
- "decorators": [],
- "parameters": [],
- "results": [],
- "is_public": true,
- "description": "",
- "docstring": "",
- "source_code": ""
- },
- {
- "name": "commonly_used_global_function",
- "unique_name": "commonly_used_global_function",
- "qname": "test.commonly_used_global_function",
- "unique_qname": "test.commonly_used_global_function",
- "decorators": [],
- "parameters": [],
- "results": [],
- "is_public": true,
- "description": "",
- "docstring": "",
- "source_code": ""
- },
- {
- "name": "unused_method",
- "unique_name": "unused_method",
- "qname": "test.Commonly_Used_Class.unused_method",
- "unique_qname": "test.Commonly_Used_Class.unused_method",
- "decorators": [],
- "parameters": [],
- "results": [],
- "is_public": true,
- "description": "",
- "docstring": "",
- "source_code": ""
- },
- {
- "name": "rarely_used_method",
- "unique_name": "rarely_used_method",
- "qname": "test.Commonly_Used_Class.rarely_used_method",
- "unique_qname": "test.Commonly_Used_Class.rarely_used_method",
- "decorators": [],
- "parameters": [],
- "results": [],
- "is_public": true,
- "description": "",
- "docstring": "",
- "source_code": ""
- },
- {
- "name": "commonly_used_method",
- "unique_name": "commonly_used_method",
- "qname": "test.Commonly_Used_Class.commonly_used_method",
- "unique_qname": "test.Commonly_Used_Class.commonly_used_method",
- "decorators": [],
- "parameters": [],
- "results": [],
- "is_public": true,
- "description": "",
- "docstring": "",
- "source_code": ""
- }
- ]
-}
diff --git a/package-parser/tests/data/unused/usage_data.json b/package-parser/tests/data/unused/usage_data.json
deleted file mode 100644
index 39a6d40c6..000000000
--- a/package-parser/tests/data/unused/usage_data.json
+++ /dev/null
@@ -1,113 +0,0 @@
-{
- "class_usages": {
- "test.Unused_Class": [],
- "test.Rarely_Used_Class": [
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- }
- ],
- "test.Commonly_Used_Class": [
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- }
- ]
- },
- "function_usages": {
- "test.unused_global_function": [],
- "test.rarely_used_global_function": [
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- }
- ],
- "test.commonly_used_global_function": [
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- }
- ],
- "test.Commonly_Used_Class.unused_method": [],
- "test.Commonly_Used_Class.rarely_used_method": [
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- }
- ],
- "test.Commonly_Used_Class.commonly_used_method": [
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- },
- {
- "file": "test.py",
- "line": 1,
- "column": 1
- }
- ]
- },
- "parameter_usages": {},
- "value_usages": {}
-}
diff --git a/package-parser/tests/data/constant/usage_data.json b/package-parser/tests/data/usage_data.json
similarity index 94%
rename from package-parser/tests/data/constant/usage_data.json
rename to package-parser/tests/data/usage_data.json
index 875c7df31..93d57be69 100644
--- a/package-parser/tests/data/constant/usage_data.json
+++ b/package-parser/tests/data/usage_data.json
@@ -1,5 +1,20 @@
{
- "class_usages": {},
+ "class_usages": {
+ "test.Commonly_Used_Class": [
+ {
+ "file": "test.py",
+ "line": 1,
+ "column": 1
+ }
+ ],
+ "test.Rarely_Used_Class": [
+ {
+ "file": "test.py",
+ "line": 1,
+ "column": 1
+ }
+ ]
+ },
"function_usages": {
"test.unused_global_function": [],
"test.commonly_used_global_function": [