error in counting code lines of a function after parsing a .py file using Python’s ast library
Question:
the line count after ast processing is incomplete, with only the first two being complete and everything after ‘draw questions’ being incomplete.
lineCount.py is here
import ast
def get_function_info(file_path):
with open(file_path, 'r') as f:
tree = ast.parse(f.read())
functions = [node for node in tree.body if isinstance(node, ast.FunctionDef)]
function_info = []
for function in functions:
function_name = function.name
function_line_count = function.body[-1].lineno - function.body[0].lineno + 1
function_info.append((function_name, function_line_count))
print(len(functions))
print(function_info)
return len(functions), function_info
get_function_info('mathQuiz.py')
the mathQuiz.py is the file to be counted, which is shown here
import turtle
import random
import math
def draw_square():
turtle.penup()
turtle.forward(100)
turtle.pendown()
turtle.forward(50)
turtle.left(90)
turtle.forward(25)
turtle.left(90)
turtle.forward(50)
turtle.left(90)
turtle.forward(25)
turtle.left(90)
def generate_question():
operators = ['+', '-', '*', '/']
questions = set()
while len(questions) < 5 * 3:
operator = random.choice(operators)
if operator == "+":
a = random.randint(0, 99)
b = random.randint(0, 99 - a)
result = a + b
elif operator == "-":
a = random.randint(1, 99)
b = random.randint(1, a)
result = a - b
elif operator == "*":
a = random.randint(1, math.floor(math.sqrt(99)))
b = random.randint(1, math.floor(math.sqrt(99)))
result = a * b
else:
result = random.randint(1, math.floor(math.sqrt(99)))
b = random.randint(1, math.floor(math.sqrt(result)))
a = result * b
question = f'{a} {operator} {b} = '
if question not in questions:
questions.add(f'{question}{result}')
return questions
def draw_question(questions):
turtle.penup()
turtle.goto(-200, 200)
turtle.pendown()
for i, question in enumerate(questions):
turtle.write(question, font=('Arial', 16, 'normal'))
draw_square()
if i % 3 == 2:
turtle.penup()
turtle.goto(-200, turtle.ycor() - 50)
turtle.pendown()
else:
turtle.penup()
turtle.goto(turtle.xcor() + 75, turtle.ycor())
turtle.pendown()
def draw_answer(questions):
turtle.penup()
turtle.goto(-200, -100)
turtle.pendown()
for i, question in enumerate(questions):
turtle.write(question, font=('Arial', 16, 'normal'))
draw_square()
if i % 3 == 2:
turtle.penup()
turtle.goto(-200, turtle.ycor() - 50)
turtle.pendown()
else:
turtle.penup()
turtle.goto(turtle.xcor() + 75, turtle.ycor())
turtle.pendown()
def put_answer(answers):
turtle.penup()
turtle.goto(-100, -100)
turtle.pendown()
for i, answer in enumerate(answers):
turtle.write(answer, font=('Arial', 16, 'normal'))
if i % 3 == 2:
turtle.penup()
turtle.goto(-100, turtle.ycor() - 50)
turtle.pendown()
else:
turtle.penup()
turtle.goto(turtle.xcor() + 175, turtle.ycor())
turtle.pendown()
turtle.hideturtle()
turtle.speed(0)
turtle.penup()
turtle.goto(-250, 250)
turtle.write("Quiz", font=("Arial", 24, "normal"))
turtle.pendown()
turtle.forward(100)
questions = generate_question()
questions_without_answer = [question.split('=')[0] + ' = ' for question in questions]
draw_question(questions_without_answer)
turtle.penup()
turtle.goto(-250, -50)
turtle.write("Answer", font=("Arial", 24, "normal"))
turtle.pendown()
turtle.forward(100)
draw_answer(questions_without_answer)
answers = [question.split('=')[1] for question in questions]
put_answer(answers)
turtle.done()
I don’t understand what’s wrong with this code, I hope to get the correct line processing result.
Answers:
Your get_function_info
function seemed to work for function bodies that started and ended with unindented lines. Here’s an alternative solution that uses the inspect
module. Note that the argument to the get_function_info
is just the name of the module and not the full file name.
import inspect
import importlib
def get_function_info(module_name):
"""Parse a file and return the number of lines of code in each function."""
module = importlib.import_module(module_name)
functions = inspect.getmembers(module, inspect.isfunction)
function_info = []
for name, value in functions:
function_line_count = len(inspect.getsourcelines(value)[0]) - 1
function_info.append((name, function_line_count))
return len(functions), function_info
num, info = get_function_info("mathQuiz")
print(num)
print(info)
Outputs:
5
[('draw_answer', 16), ('draw_question', 15), ('draw_square', 11), ('generate_question', 24), ('put_answer', 14)]
You’ll also want to wrap your unindented code in the mathQuiz
module in a main
function and call it like below to avoid executing your program when you import the module.
if __name__ == "__main__":
main()
the line count after ast processing is incomplete, with only the first two being complete and everything after ‘draw questions’ being incomplete.
lineCount.py is here
import ast
def get_function_info(file_path):
with open(file_path, 'r') as f:
tree = ast.parse(f.read())
functions = [node for node in tree.body if isinstance(node, ast.FunctionDef)]
function_info = []
for function in functions:
function_name = function.name
function_line_count = function.body[-1].lineno - function.body[0].lineno + 1
function_info.append((function_name, function_line_count))
print(len(functions))
print(function_info)
return len(functions), function_info
get_function_info('mathQuiz.py')
the mathQuiz.py is the file to be counted, which is shown here
import turtle
import random
import math
def draw_square():
turtle.penup()
turtle.forward(100)
turtle.pendown()
turtle.forward(50)
turtle.left(90)
turtle.forward(25)
turtle.left(90)
turtle.forward(50)
turtle.left(90)
turtle.forward(25)
turtle.left(90)
def generate_question():
operators = ['+', '-', '*', '/']
questions = set()
while len(questions) < 5 * 3:
operator = random.choice(operators)
if operator == "+":
a = random.randint(0, 99)
b = random.randint(0, 99 - a)
result = a + b
elif operator == "-":
a = random.randint(1, 99)
b = random.randint(1, a)
result = a - b
elif operator == "*":
a = random.randint(1, math.floor(math.sqrt(99)))
b = random.randint(1, math.floor(math.sqrt(99)))
result = a * b
else:
result = random.randint(1, math.floor(math.sqrt(99)))
b = random.randint(1, math.floor(math.sqrt(result)))
a = result * b
question = f'{a} {operator} {b} = '
if question not in questions:
questions.add(f'{question}{result}')
return questions
def draw_question(questions):
turtle.penup()
turtle.goto(-200, 200)
turtle.pendown()
for i, question in enumerate(questions):
turtle.write(question, font=('Arial', 16, 'normal'))
draw_square()
if i % 3 == 2:
turtle.penup()
turtle.goto(-200, turtle.ycor() - 50)
turtle.pendown()
else:
turtle.penup()
turtle.goto(turtle.xcor() + 75, turtle.ycor())
turtle.pendown()
def draw_answer(questions):
turtle.penup()
turtle.goto(-200, -100)
turtle.pendown()
for i, question in enumerate(questions):
turtle.write(question, font=('Arial', 16, 'normal'))
draw_square()
if i % 3 == 2:
turtle.penup()
turtle.goto(-200, turtle.ycor() - 50)
turtle.pendown()
else:
turtle.penup()
turtle.goto(turtle.xcor() + 75, turtle.ycor())
turtle.pendown()
def put_answer(answers):
turtle.penup()
turtle.goto(-100, -100)
turtle.pendown()
for i, answer in enumerate(answers):
turtle.write(answer, font=('Arial', 16, 'normal'))
if i % 3 == 2:
turtle.penup()
turtle.goto(-100, turtle.ycor() - 50)
turtle.pendown()
else:
turtle.penup()
turtle.goto(turtle.xcor() + 175, turtle.ycor())
turtle.pendown()
turtle.hideturtle()
turtle.speed(0)
turtle.penup()
turtle.goto(-250, 250)
turtle.write("Quiz", font=("Arial", 24, "normal"))
turtle.pendown()
turtle.forward(100)
questions = generate_question()
questions_without_answer = [question.split('=')[0] + ' = ' for question in questions]
draw_question(questions_without_answer)
turtle.penup()
turtle.goto(-250, -50)
turtle.write("Answer", font=("Arial", 24, "normal"))
turtle.pendown()
turtle.forward(100)
draw_answer(questions_without_answer)
answers = [question.split('=')[1] for question in questions]
put_answer(answers)
turtle.done()
I don’t understand what’s wrong with this code, I hope to get the correct line processing result.
Your get_function_info
function seemed to work for function bodies that started and ended with unindented lines. Here’s an alternative solution that uses the inspect
module. Note that the argument to the get_function_info
is just the name of the module and not the full file name.
import inspect
import importlib
def get_function_info(module_name):
"""Parse a file and return the number of lines of code in each function."""
module = importlib.import_module(module_name)
functions = inspect.getmembers(module, inspect.isfunction)
function_info = []
for name, value in functions:
function_line_count = len(inspect.getsourcelines(value)[0]) - 1
function_info.append((name, function_line_count))
return len(functions), function_info
num, info = get_function_info("mathQuiz")
print(num)
print(info)
Outputs:
5
[('draw_answer', 16), ('draw_question', 15), ('draw_square', 11), ('generate_question', 24), ('put_answer', 14)]
You’ll also want to wrap your unindented code in the mathQuiz
module in a main
function and call it like below to avoid executing your program when you import the module.
if __name__ == "__main__":
main()