Skip to content

Commit

Permalink
Merge pull request #17 from UBC-MDS/ml2-readfunc
Browse files Browse the repository at this point in the history
** DO NOT MERGE ** Ml2 readfunc
  • Loading branch information
mikem2m authored Jan 16, 2025
2 parents 1315923 + b562564 commit 272adc0
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 15 deletions.
50 changes: 46 additions & 4 deletions docs/example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand All @@ -37,7 +79,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
"version": "3.10.16"
}
},
"nbformat": 4,
Expand Down
5 changes: 1 addition & 4 deletions src/fml_doc_gen/fml_doc_gen.py
Original file line number Diff line number Diff line change
@@ -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:
"""
Expand Down
9 changes: 8 additions & 1 deletion src/fml_doc_gen/func_dto.py
Original file line number Diff line number Diff line change
@@ -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}"
)
3 changes: 2 additions & 1 deletion src/fml_doc_gen/generate_template.py
Original file line number Diff line number Diff line change
@@ -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}:
Expand Down
23 changes: 20 additions & 3 deletions src/fml_doc_gen/read_user_function.py
Original file line number Diff line number Diff line change
@@ -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:
"""
Expand All @@ -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)
4 changes: 2 additions & 2 deletions tests/test_generate_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
104 changes: 104 additions & 0 deletions tests/test_read_user_function.py
Original file line number Diff line number Diff line change
@@ -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")
]

0 comments on commit 272adc0

Please sign in to comment.