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.

Asked By: Xiao Lin

||

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()
Answered By: acurtis166
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.