Skip to content

Commit e41b935

Browse files
Merge pull request #40 from MichaelisTrofficus/add-exception-catching
Add exception catching
2 parents 39f59d1 + 7c66995 commit e41b935

File tree

6 files changed

+120
-50
lines changed

6 files changed

+120
-50
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "gpt4docstrings"
3-
version = "0.1.0"
3+
version = "0.1.1"
44
description = "gpt4docstrings"
55
authors = ["Miguel Otero Pedrido <miguel.otero.pedrido.1993@gmail.com>"]
66
license = "MIT"

src/gpt4docstrings/docstrings_generators/chatgpt_generator.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from gpt4docstrings.docstrings_generators.utils.parsers import DocstringParser
1212
from gpt4docstrings.docstrings_generators.utils.prompts import CLASS_PROMPTS
1313
from gpt4docstrings.docstrings_generators.utils.prompts import FUNCTION_PROMPTS
14+
from gpt4docstrings.exceptions import ASTError
1415

1516

1617
class ChatGPTDocstringGenerator:
@@ -57,6 +58,9 @@ def generate_function_docstring(self, source: str) -> dict:
5758
5859
Returns:
5960
dict: A dictionary containing the generated docstring.
61+
62+
Raises:
63+
ASTError: Raises an ASTError when there are errors interacting with an AST node
6064
"""
6165
source = source.strip()
6266
stripped_source = textwrap.dedent(source)
@@ -66,14 +70,19 @@ def generate_function_docstring(self, source: str) -> dict:
6670
)
6771
_input = prompt.format_prompt(code=stripped_source)
6872
fn_src = DocstringParser().parse(self._get_completion(_input.to_string()))
69-
fn_node = RedBaron(fn_src)[0]
7073

71-
return {
72-
"docstring": utils.add_indentation_to_docstring(
73-
'"""' + textwrap.dedent(fn_node[0].to_python()) + '"""',
74-
fn_node[0].indentation,
75-
)
76-
}
74+
try:
75+
fn_node = RedBaron(fn_src)[0]
76+
return {
77+
"docstring": utils.add_indentation_to_docstring(
78+
'"""' + textwrap.dedent(fn_node[0].to_python()) + '"""',
79+
fn_node[0].indentation,
80+
)
81+
}
82+
except ValueError as e:
83+
raise ASTError(
84+
f"Some error has occurred when trying to parse the current AST node: {e}"
85+
) from e
7786

7887
def generate_class_docstring(self, source: str) -> dict:
7988
"""
@@ -84,6 +93,9 @@ def generate_class_docstring(self, source: str) -> dict:
8493
8594
Returns:
8695
dict: A dictionary containing the generated docstrings.
96+
97+
Raises:
98+
ASTError: Raises an ASTError when there are errors interacting with an AST node
8799
"""
88100
source = source.strip()
89101
stripped_source = textwrap.dedent(source)
@@ -93,20 +105,27 @@ def generate_class_docstring(self, source: str) -> dict:
93105
)
94106
_input = prompt.format_prompt(code=stripped_source)
95107
class_src = DocstringParser().parse(self._get_completion(_input.to_string()))
96-
class_node = RedBaron(class_src)[0]
97-
method_nodes = [f for f in class_node.find_all("def")]
98-
99-
docstrings = {}
100-
for method_node in method_nodes:
101-
docstrings[method_node.name] = utils.add_indentation_to_docstring(
102-
'"""' + textwrap.dedent(method_node[0].to_python()) + '"""',
103-
method_node[0].indentation,
108+
109+
try:
110+
class_node = RedBaron(class_src)[0]
111+
method_nodes = [f for f in class_node.find_all("def")]
112+
113+
docstrings = {}
114+
for method_node in method_nodes:
115+
docstrings[method_node.name] = utils.add_indentation_to_docstring(
116+
'"""' + textwrap.dedent(method_node[0].to_python()) + '"""',
117+
method_node[0].indentation,
118+
)
119+
120+
docstrings["docstring"] = class_node.value[0]
121+
docstrings["docstring"] = utils.add_indentation_to_docstring(
122+
'"""' + textwrap.dedent(class_node[0].to_python()) + '"""',
123+
class_node[0].indentation,
104124
)
105125

106-
docstrings["docstring"] = class_node.value[0]
107-
docstrings["docstring"] = utils.add_indentation_to_docstring(
108-
'"""' + textwrap.dedent(class_node[0].to_python()) + '"""',
109-
class_node[0].indentation,
110-
)
126+
return docstrings
111127

112-
return docstrings
128+
except ValueError as e:
129+
raise ASTError(
130+
f"Some error has occurred when trying to parse the current AST node: {e}"
131+
) from e

src/gpt4docstrings/docstrings_generators/utils/parsers.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
from langchain.schema import BaseOutputParser
44

5-
6-
class DocstringParsingError(Exception):
7-
"""Custom exception for docstring parsing errors."""
8-
9-
pass
5+
from gpt4docstrings.exceptions import DocstringParsingError
106

117

128
class DocstringParser(BaseOutputParser):

src/gpt4docstrings/exceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class DocstringParsingError(Exception):
2+
"""Custom exception for docstring parsing errors."""
3+
4+
pass
5+
6+
7+
class ASTError(Exception):
8+
"""Custom exception for AST related error"""
9+
10+
pass

src/gpt4docstrings/generate_docstrings.py

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
import os
23
import pathlib
34
import sys
@@ -13,6 +14,8 @@
1314
from gpt4docstrings import utils
1415
from gpt4docstrings.ascii_title import title
1516
from gpt4docstrings.docstrings_generators import ChatGPTDocstringGenerator
17+
from gpt4docstrings.exceptions import ASTError
18+
from gpt4docstrings.exceptions import DocstringParsingError
1619

1720

1821
class GPT4Docstrings:
@@ -126,6 +129,7 @@ def get_filenames_from_paths(self) -> List[str]:
126129
self.common_base = utils.get_common_base(filenames)
127130
return filenames
128131

132+
# flake8: noqa: C901
129133
def generate_file_docstrings(self, filename: str):
130134
"""Generates docstrings for a single file.
131135
@@ -136,33 +140,58 @@ def generate_file_docstrings(self, filename: str):
136140
click.echo(click.style(f"Documenting file {filename} ... ", fg="green"))
137141

138142
for node in source.find_all("def"):
139-
if not utils.check_def_node_is_class_method(node):
143+
if not utils.check_def_node_is_class_method(
144+
node
145+
) and not utils.check_def_node_is_nested(node):
140146
if not node.value[0].type == "string":
141-
fn_docstring = self.docstring_generator.generate_function_docstring(
142-
node.dumps()
143-
)
144-
node.value.insert(0, fn_docstring["docstring"])
145-
self.documented_nodes.append([filename, node.name])
147+
try:
148+
fn_docstring = (
149+
self.docstring_generator.generate_function_docstring(
150+
node.dumps()
151+
)
152+
)
153+
node.value.insert(0, fn_docstring["docstring"])
154+
self.documented_nodes.append([filename, node.name])
155+
except DocstringParsingError:
156+
logging.warning(
157+
f"Skipping {node.name} from {filename} due to errors when parsing"
158+
)
159+
except ASTError:
160+
logging.warning(
161+
f"Skipping {node.name} from {filename} due to errors when accessing AST node"
162+
)
146163

147164
for node in source.find_all("class"):
148165
if not node.value[0].type == "string":
149-
class_docstring = self.docstring_generator.generate_class_docstring(
150-
node.dumps()
151-
)
152-
node.value.insert(
153-
0,
154-
class_docstring["docstring"],
155-
)
156-
157-
for method_node in node.value:
158-
if (
159-
method_node.type == "def"
160-
and not utils.check_is_private_method(method_node)
161-
and not method_node.value[0].type == "string"
162-
):
163-
method_node.value.insert(0, class_docstring[method_node.name])
164-
165-
self.documented_nodes.append([filename, node.name])
166+
try:
167+
class_docstring = self.docstring_generator.generate_class_docstring(
168+
node.dumps()
169+
)
170+
node.value.insert(
171+
0,
172+
class_docstring["docstring"],
173+
)
174+
175+
for method_node in node.value:
176+
if (
177+
method_node.type == "def"
178+
and not utils.check_is_private_method(method_node)
179+
and not method_node.value[0].type == "string"
180+
and not utils.check_def_node_is_nested(method_node)
181+
):
182+
method_node.value.insert(
183+
0, class_docstring[method_node.name]
184+
)
185+
186+
self.documented_nodes.append([filename, node.name])
187+
except DocstringParsingError:
188+
logging.warning(
189+
f"Skipping {node.name} from {filename} due to errors when parsing"
190+
)
191+
except ASTError:
192+
logging.warning(
193+
f"Skipping {node.name} from {filename} due to errors when accessing AST node"
194+
)
166195

167196
utils.write_updated_source_to_file(source, filename)
168197

src/gpt4docstrings/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,22 @@ def get_common_base(files: List[str]) -> str:
2828
return str(common_base)
2929

3030

31+
def check_def_node_is_nested(node) -> bool:
32+
"""Check if the given node is a nested function.
33+
34+
Args:
35+
node: The node to check
36+
37+
Returns:
38+
bool: True if the node is a nested function, False otherwise.
39+
"""
40+
try:
41+
is_nested = True if node.parent.type == "def" else False
42+
except AttributeError:
43+
is_nested = False
44+
return is_nested
45+
46+
3147
def check_def_node_is_class_method(node) -> bool:
3248
"""Check if the given node is a class method.
3349

0 commit comments

Comments
 (0)