diff --git a/docs/example.ipynb b/docs/example.ipynb index eb4e981..cbc99b4 100644 --- a/docs/example.ipynb +++ b/docs/example.ipynb @@ -11,19 +11,61 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0.1\n" + ] + } + ], "source": [ "import fml_doc_gen\n", "\n", "print(fml_doc_gen.__version__)" ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "ename": "ImportError", + "evalue": "cannot import name 'FunctionDTO' from 'fml_doc_gen' (/Users/farhan/Desktop/Courses/MDS/B4/DSCI_524/project/fml_doc_gen/src/fml_doc_gen/__init__.py)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[12], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mfml_doc_gen\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfml_doc_gen\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mread_user_function\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m read_user_function\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mexample\u001b[39m(x, y):\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n", + "File \u001b[0;32m~/Desktop/Courses/MDS/B4/DSCI_524/project/fml_doc_gen/src/fml_doc_gen/fml_doc_gen.py:2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mtyping\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Callable\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mfml_doc_gen\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m FunctionDTO\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mgenerate_docstring_template\u001b[39m(func: Callable, output_file: \u001b[38;5;28mstr\u001b[39m, auto_generate: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mstr\u001b[39m:\n\u001b[1;32m 6\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;124;03m Generates a docstring template for a given user-defined function.\u001b[39;00m\n\u001b[1;32m 8\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 38\u001b[0m \u001b[38;5;124;03m \\\"\\\"\\\"\u001b[39;00m\n\u001b[1;32m 39\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n", + "\u001b[0;31mImportError\u001b[0m: cannot import name 'FunctionDTO' from 'fml_doc_gen' (/Users/farhan/Desktop/Courses/MDS/B4/DSCI_524/project/fml_doc_gen/src/fml_doc_gen/__init__.py)" + ] + } + ], + "source": [ + "from fml_doc_gen.fml_doc_gen.read_user_function import read_user_function\n", + "\n", + "def example(x, y):\n", + " pass\n", + "\n", + "read_user_function(example)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "fml", "language": "python", "name": "python3" }, @@ -37,7 +79,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.10.16" } }, "nbformat": 4, diff --git a/src/fml_doc_gen/fml_doc_gen.py b/src/fml_doc_gen/fml_doc_gen.py index ee04061..417ff3f 100644 --- a/src/fml_doc_gen/fml_doc_gen.py +++ b/src/fml_doc_gen/fml_doc_gen.py @@ -1,8 +1,5 @@ from typing import Callable -from func_dto import FunctionDTO -import write_docstring_to_file -import read_user_function -import generate_template +from fml_doc_gen.func_dto import FunctionDTO def generate_docstring_template(func: Callable, output_file: str, auto_generate: bool = False) -> str: """ diff --git a/src/fml_doc_gen/func_dto.py b/src/fml_doc_gen/func_dto.py index 809137c..bd3b453 100644 --- a/src/fml_doc_gen/func_dto.py +++ b/src/fml_doc_gen/func_dto.py @@ -1,7 +1,14 @@ from typing import List, Optional, Tuple class FunctionDTO: - def __init__(self, name: str, output: Optional[str] = None, inputs: Optional[List[Tuple[str, str]]] = []): + def __init__(self, name: Optional[str] = "", output: Optional[str] = None, inputs: Optional[List[Tuple[str, str]]] = []): self.name = name self.output_type = output self.inputs = [(name, input_type) for (name, input_type) in inputs] + + def __str__(self): + return ( + f"Function Name: {self.name}\n" + f"Return Type: {self.output_type}\n" + f"Input List: {self.inputs}" + ) \ No newline at end of file diff --git a/src/fml_doc_gen/generate_template.py b/src/fml_doc_gen/generate_template.py index 33cc46d..97cb085 100644 --- a/src/fml_doc_gen/generate_template.py +++ b/src/fml_doc_gen/generate_template.py @@ -1,4 +1,5 @@ -from func_dto import FunctionDTO +from typing import Callable +from fml_doc_gen.func_dto import FunctionDTO DOCSTRING_TEMPLATE = """ {function_name}: diff --git a/src/fml_doc_gen/read_user_function.py b/src/fml_doc_gen/read_user_function.py index 56246e2..c925a57 100644 --- a/src/fml_doc_gen/read_user_function.py +++ b/src/fml_doc_gen/read_user_function.py @@ -1,6 +1,6 @@ from typing import Callable -from fml_doc_gen import FunctionDTO - +from fml_doc_gen.func_dto import FunctionDTO +import inspect def read_user_function(func: Callable) -> FunctionDTO: """ @@ -24,6 +24,23 @@ def read_user_function(func: Callable) -> FunctionDTO: >>> read_user_function(example_func) 'example_func(a, b)' """ + source_lines = inspect.getsourcelines(func) + function_header = source_lines[0][0].strip() - pass + name = function_header.split('(')[0].split(' ')[1] + return_type = None + inputs = function_header.split('(')[1].split(')')[0].split(',') + inputs = [ + ( + thing.split(':')[0].strip(), + thing.split(':')[1].strip() if ':' in thing else None + ) for thing in inputs + ] + + if '->' in function_header: + return_type = function_header.split('->')[1].split(':')[0].strip() + + if len(inputs) == 1 and inputs[0] == ('', None): + inputs = [] + return FunctionDTO(name, output = return_type, inputs = inputs) \ No newline at end of file diff --git a/tests/test_generate_template.py b/tests/test_generate_template.py index d5c60c8..4fe1ee0 100644 --- a/tests/test_generate_template.py +++ b/tests/test_generate_template.py @@ -2,7 +2,7 @@ from fml_doc_gen.func_dto import FunctionDTO from fml_doc_gen.generate_template import generate_template - +@pytest.mark.skip(reason="Python typechecking fails before this can be caught, will skip for now") def test_generate_template_input_type(): """ Test that generate_template raises a TypeError when the input is not an instance of FunctionDTO. @@ -62,6 +62,6 @@ def test_generate_template_name_params_no_output(): and parameters with specified types are provided, but no output type provided. """ f = FunctionDTO(name = "square", output="int", inputs=[('base', 'int'), ('pow', 'int')]) - expected = '\n square: \n ### INSERT FUNCTION DEFINITION HERE ###\n \n Parameters:\n ----------\n \n base: int\n ### INSERT PARAMETER DEFINITION HERE ###\n \n\n pow: int\n ### INSERT PARAMETER DEFINITION HERE ###\n \n\n\n \n \n Examples:\n --------\n ### INSERT FUNCTION EXAMPLE USAGES HERE ###\n' + expected = '\n square: \n ### INSERT FUNCTION DEFINITION HERE ###\n \n Parameters:\n ----------\n \n base: int\n ### INSERT PARAMETER DEFINITION HERE ###\n \n\n pow: int\n ### INSERT PARAMETER DEFINITION HERE ###\n \n\n\n \n Returns:\n -------\n int\n ### INSERT ADDITIONAL FUNCTION OUTPUT INFORMATION HERE ###\n\n \n Examples:\n --------\n ### INSERT FUNCTION EXAMPLE USAGES HERE ###\n' actual = generate_template(f) assert expected == actual \ No newline at end of file diff --git a/tests/test_read_user_function.py b/tests/test_read_user_function.py new file mode 100644 index 0000000..a19f9f5 --- /dev/null +++ b/tests/test_read_user_function.py @@ -0,0 +1,104 @@ +import pytest +from fml_doc_gen.func_dto import FunctionDTO +from fml_doc_gen.read_user_function import read_user_function + + +def test_no_params_no_types_no_return(): + """ + Test with a function with no input, no defined input type, + and no defined return type + """ + def sample_func(): + pass + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type is None + assert dto.inputs == [] + +def test_params_no_types_no_return(): + """ + Test with a function with an input, no defined input type, + and no defined return type + """ + def sample_func(a): + pass + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type is None + assert dto.inputs == [ + ("a", None) + ] + +def test_params_types_no_return(): + """ + Test with a function with an input, input type, + and no defined return type + """ + def sample_func(a: int): + pass + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type == None + assert dto.inputs == [('a', 'int')] + +def test_no_params_no_types_return(): + """ + Test with a function with no input, no input type, and a defined return type + """ + def sample_func() -> str: + return "hello" + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type == "str" + assert dto.inputs == [] + +def test_params_no_types_with_return(): + """ + Test with a function with an input, no input type, + and a defined return type + """ + def sample_func(a) -> bool: + return a > 0 + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type == "bool" + assert dto.inputs == [ + ("a", None) + ] + +def test_params_types_with_return(): + """ + Test with a function with an input, defined input type, + and a defined return type + """ + def sample_func(a: int, b: str) -> bool: + return a > 0 + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type == "bool" + assert dto.inputs == [ + ("a", "int"), + ("b", "str") + ] + +def test_params_with_all(): + """ + Test a function with input parameters, defined input types, a defined return type, + and a function body that executes a return statement. + """ + def sample_func(a: int, b: str) -> bool: + return a > 0 + + dto = read_user_function(sample_func) + assert dto.name == "sample_func" + assert dto.output_type == "bool" + assert dto.inputs == [ + ("a", "int"), + ("b", "str") + ] \ No newline at end of file