Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.

Read-only Access

We've opened up the autobadger tool in attempt to make things more visible to you and less of a "black box". We've done this by making the repository read-only, meaning you should be able to git clone the repo but not git push to it.

To start, navigate to a directory outside of any class project. I'd recommend cloning to the same directory as your projects.

git clone https://oauth2:glpat-CSTX_tgpf38eJHyUW213@git.doit.wisc.edu/cdis/cs/courses/cs544/s25/tools/autobadger.git

NOTE: if you want to use this method throughout the semester, you'll need to git pull to get up-to-date code for each project.

Your folder structure should look something like

some-directory/
	autobadger/
	p1/
	p2/
    ... # other projects

Making Changes

You can change the code inside of autobadger. The only files that will be of interest to you are inside the projects/ directory, i.e. projects/*.py). Your changes will be for debugging, i.e. print() or breakpoint() statements.

Using pip

For whatever project you're working on, you will need to apply any changes you make using pip

For example, assuming

  • I'm working on p2
  • in my p2 directory
  • and have my venv activated

I would do something like:

pip3 install ../autobadger/.

This would install and replace my local version of autobadger . Now when I run

autobadger --project=p2

I will see my changes in effect.

Breakpoints

Since breakpoint() is less known and straightforward, I will teach about it here.

NOTE: It is not required to use breakpoint(). You are also welcome to use print() instead. breakpoint() has a steeper learning curve, but may help you iterate more quickly and save you time once the basic concepts are well-understood.

What is a breakpoint?

breakpoint() is a built-in function in Python and starts the debugger at the point where it is called. It allows developers to inspect variables, step through code, and debug interactively.

Simple Example:

# Inside of /path/to/file.py

def calculate_sum(a, b):
    breakpoint()  # Debugger starts here
    return a + b 


calculate_sum(3, 5)  # execute function

Adding a breakpoint() will pause execution, allowing you to inspect a and b before proceeding. I would see something like:

> /path/to/file.py(3)calculate_sum()
-> return a + b

in the terminal, which displays

  1. the next line to be executed return a + b
  2. (3)calculate_sum() tells me the line number and the function name (if applicable)
  3. /path/to/file.py tells me the current file

Navigating the debugger

While the Python debugger is active, you can use several commands to navigate through your program and investigate.

  • Variable name: I can type any variable that is in scope and get it's value.
    • Ex: Typing a in the previous example would return the value of a
    • NOTE: if a variable name also coincides with a command keyword in the debugger, you may need to use print(<variable_name>) instead. b is one of those commands, so to print the value of b to the terminal, I would need to do print(b):
  • Evaluation: I can also evaluate statements (i.e. add two numbers)
In [3]: calculate_sum(3, 5)
> <ipython-input-2-443b6e8e0b0a>(3)calculate_sum()
-> return a + b

(Pdb) print(a)
3

(Pdb) print(b)
5

(Pdb) print(a + b)
8
  • n: Steps to the next line of my program
  • c: Continues execution of the program until the next breakpoint, or until the program ends.
  • s: Steps into a function or method call
  • exit: kills the debugger and ends the program

An example

Using breakpoints

Suppose I want to investigate Q4 for p2. I can add breakpoint() statements to the Q4 test method for the ProjectTwoTest class.

Navigating to projects/p2.py inside of autobadger, I find:

@graded(Q=4, points=10)  
def test_simple_http(self) -> int | TestError:  
    address = self._test_cache_server("-cache-1")  
    if isinstance(address, TestError):  
        return address  
    r = requests.get(f"{address}/lookup/53706")  
    r.raise_for_status()  
    result = r.json()  
    if "addrs" not in result or "source" not in result:  
        return TestError(  
            message=f"Result body should be JSON with 'addrs' and 'source' fields, but got {result}.",  
            earned=5,  
        )  
    return 10

Note: This is Q4 since I have Q=4 in the decorator.

I can edit this method by adding breakpoints!

@graded(Q=4, points=10)  
def test_simple_http(self) -> int | TestError:  
    breakpoint()  
    address = self._test_cache_server("-cache-1")  
    if isinstance(address, TestError):  
        return address  
    r = requests.get(f"{address}/lookup/53706")  
    breakpoint()
    r.raise_for_status()
    result = r.json()  
    if "addrs" not in result or "source" not in result:  
        return TestError(  
            message=f"Result body should be JSON with 'addrs' and 'source' fields, but got {result}.",  
            earned=5,  
        )  
    return 10

Now, after I update with pip as mentioned above, I can run autobadger --project=p2 and get:

> /Users/.../p2.py(103)test_simple_http()
-> address = self._test_cache_server("-cache-1")

Note that in this situation, typing address would give me an error cause it not yet defined:

(Pdb) address
*** NameError: name 'address' is not defined
Using n (next line)

address defined on the next line. So, I use the n command to step!

(Pdb) n
> /Users/.../p2.py(104)test_simple_http()
-> if isinstance(address, TestError):

(Pdb) address
'http://localhost:64879'
Using s (step into)

I could have also used s to step into self._test_cache_server(...) if I had wanted to investigate further:

> /Users/.../p2.py(103)test_simple_http()
-> address = self._test_cache_server("-cache-1")

(Pdb) s
--Call--
> /Users/.../p2.py(118)_test_cache_server()
-> def _test_cache_server(self, server_suffix: str) -> str | TestError:
       # Now in a new method — _test_cache_server

(Pdb) n
> /Users/.../p2.py(119)_test_cache_server()
-> cache_server = [c for c in self.containers if c["Name"].endswith(server_suffix)]
Using c (continue)

I can also continue till the next breakpoint, which is quite convenient if you don't need to step over every line of code:

> /Users/.../p2.py(103)test_simple_http()
-> address = self._test_cache_server("-cache-1")

(Pdb) c
> /Users/.../p2.py(108)test_simple_http()
-> r.raise_for_status()

(Pdb) print(r.json())
{'addrs': [...], 'error': None, 'source': '...'}

Using c jumped from line 103 to line 108, where I had my two breakpoints defined.

NOTE: using c again would continue the Python program till the end of its execution since I have no other breakpoint() statements