Trace a Python Library You Didn't Write

Trace and override methods in a Python library you don't own using Wrapture.

Introduction

30 Second Summary

A library in your project starts behaving strangely, and it is code you did not write. You cannot drop print statements into someone else's package, so you are left guessing at what it actually did.

In this project, you will use Wrapture to record every call a library makes, from the outside, using nothing but a config file. You will read that record, shrink it, fake a return value in a test, and send the same calls to OpenTelemetry.

What You'll Build

You will run an ordinary script, open trace.jsonl, and read the exact calls a package you did not write made, with the arguments that went in and the values that came back.

By the end of this project, you'll have:

  • A config file that watches a class inside someone else's package, without a single edit to that package.
  • A readable trace.jsonl showing the arguments and return values of every call your script triggered, plus a smaller summary version you can switch to when a hot function floods it.
  • A test that passes only because you overrode a return value from outside the library, and the same calls streaming out as spans you can watch in your terminal.
  • Secret Mission: An optional challenge to push your tracing setup further.

Are there any prerequisites?

You need a laptop with Python and pip on it. There is no account to create and no card to enter, because everything here is open source and runs on your own machine.

Before We Start

When a dependency misbehaves, most of us end up adding print statements inside someone else's code. That works until you forget to remove them, or the next install wipes your edits.

wrapture takes a different route. It wraps functions and methods from the outside, so you can watch a library without touching a single line of it.

Before any of that, you need a library worth watching. In this step you'll install wrapture alongside Faker, then run a script that uses Faker with nothing recording it.

In this step, get ready to:
  • Check that Python is ready on your machine.
  • Install wrapture and Faker with pip.
  • Run a script that uses a library you did not write.
Check that Python is installed

wrapture is a Python library, and everything in this project runs locally on your own machine. So the first question is which Python you already have.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.
  • Find out which Python you have by running this command:
python3 --version

✔️ I see a version number

Python is ready. pip ships alongside it, so you can install packages straight away.

ⓧ Command not found

Windows names the command differently, so try the other spelling before assuming Python is missing.

  • Try the Windows spelling by running this command:
python --version
  • If that also fails, download an installer from the official Python downloads page.
  • Close your terminal and reopen it once the installer finishes.
  • Run the version check again to confirm it worked.
Install wrapture and Faker

To watch code you did not write, you need some code you did not write. Faker is a small library that invents realistic names and email addresses, and it makes a perfect subject.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir wrapture-demo
cd wrapture-demo
  • Install both packages by running this command:
pip install wrapture faker

What is wrapture?

wrapture is an open source Python library that extends monkeypatching. It lets you trace or override any function or method from outside the code being watched.

You drive it from a configuration file rather than from edits to the library. The library you are observing stays exactly as its author shipped it.

  • Confirm wrapture landed by running this command:
pip show wrapture

You should see the package details printed, including its name and version number.

Install didn't go through?

If your terminal cannot find pip, try pip3 instead. Some machines only expose the versioned name.

If the install stops on a permissions message, you are most likely installing into a system Python that your account cannot write to.

Why did my pip install fail?

Run the script with nothing watching it

Every trace needs a baseline. Run the script once with no configuration in play, so you know exactly what ordinary looks like before wrapture starts recording.

  • Open the wrapture-demo folder in your code editor.
  • Create a new file inside that folder named run.py.

No code editor yet?

Any plain text editor works for this project. If you don't have one, grab Visual Studio Code and open the folder from its File menu.

  • Back in your terminal, confirm the file exists by running this command:
ls

You should see run.py listed, and nothing else.

  • Write the demo script by copying this code into run.py:
# Faker is a library we did not write, and it is the code we will observe
from faker import Faker

# Create a Faker instance that invents personal details on demand
fake = Faker()

# Ask Faker for a random full name and a random email address
name = fake.name()
email = fake.email()

# Print whatever Faker handed back
print(name)
print(email)

What does this code do?

  • from faker import Faker pulls in the class that does all the generating.
  • fake = Faker() builds the object your script will call.
  • fake.name() and fake.email() are the two method calls you will eventually trace from the outside.
  • Save run.py.

✔️ Awesome, I've got everything!

Great, double check you've saved run.py before you run it.

ⓧ I'd like to double check the full code

# Faker is a library we did not write, and it is the code we will observe
from faker import Faker

# Create a Faker instance that invents personal details on demand
fake = Faker()

# Ask Faker for a random full name and a random email address
name = fake.name()
email = fake.email()

# Print whatever Faker handed back
print(name)
print(email)

Before you run it, what do you think lands in your terminal, and what do you think lands on disk?

  • Run the script by running this command:
python3 run.py

You should see two lines printed: a random full name, then a random email address.

Script won't run?

If Python says it cannot find the faker module, your install went into a different Python from the one running your script.

If nothing prints at all, check you saved run.py and that your terminal is inside the wrapture-demo folder.

Why can't my script find the faker module?

  • Check what landed on disk by running this command:
ls

You should still see only run.py. Two method calls happened inside a library you did not write, and nothing recorded them.

You now have a baseline: real calls happening, nothing captured. Next up, you'll write the config that turns those invisible calls into a file you can read.

Trace the Faker Class

Your script ran and printed a fake name and a fake email address. Nothing recorded those calls, so the only evidence they happened was the text on your screen.

wrapture reads a config file that names the code you want to watch. You never touch the library itself.

In this step you'll point it at Faker and send the record to a file on disk.

In this step, get ready to:
  • Create a wrapture.toml config that names the Faker class and the two methods you want to watch.
  • Add a JSON Lines sink that writes every recorded call to trace.jsonl.
  • Rerun your script through wrapture and confirm trace.jsonl appears on disk.
Name the class you want to watch

wrapture never guesses which code to observe. An [[observe]] entry names one exact target and the members inside it you care about.

  • In the terminal where you ran run.py, create the config file by running this command:
touch wrapture.toml
  • Confirm the file exists by running:
ls
  • You should see wrapture.toml listed alongside run.py in the output.
  • Open wrapture.toml in the same editor you used for run.py.
  • Name the class and the methods you want to watch by pasting this into wrapture.toml:
# Watch one exact target: the Faker class inside the faker package
[[observe]]
target = "faker:Faker"
# The exact members to bind, listed by name
name = ["name", "email"]

What does this config do?

  • target = "faker:Faker" points at one exact place. The part before the colon is the module, the part after it is the class inside that module.
  • name = ["name", "email"] lists the members to bind. Every name you list has to exist on the target, so a typo fails loudly instead of recording nothing.
  • The double square brackets make this a list entry. You add a second [[observe]] block for every extra target you want to watch.
  • Save wrapture.toml.

Why a config file instead of editing the script?

You could import wrapture inside run.py and create the bindings in code. That only works when you own the entry point.

The config file moves the whole setup outside the program. The same file works on a script you wrote and on an app you cannot edit.

Send the recorded calls to a file

The observe entry says what to watch. A sink says where the record goes.

  • Add the sink by pasting this at the bottom of wrapture.toml, below the observe entry:
# Where the recorded events go
[[sink]]
# Built-in sink that streams events to disk as JSON lines
type = "jsonlines"
# The file the sink writes, created in the folder you run from
path = "trace.jsonl"

What does this sink do?

  • type = "jsonlines" picks the built-in sink that writes one JSON object per line. That format drops straight into jq, pandas and ordinary log tooling.
  • path = "trace.jsonl" names the file. It lands in whichever folder you run the command from.
  • A line is written when a call closes, so every line already carries the outcome and the timing.
  • Save wrapture.toml.

✔️ Awesome, I've got everything!

Great. Double check you've saved the file before you run anything.

ⓧ I'd like to double check the full code

Your whole config file should look like this.

# Watch one exact target: the Faker class inside the faker package
[[observe]]
target = "faker:Faker"
# The exact members to bind, listed by name
name = ["name", "email"]

# Where the recorded events go
[[sink]]
# Built-in sink that streams events to disk as JSON lines
type = "jsonlines"
# The file the sink writes, created in the folder you run from
path = "trace.jsonl"
Run your script through wrapture

wrapture applies the config before your program starts. The wrappers are in place before run.py imports anything.

Before you run this, what do you think the terminal will print? The same two lines as before, something extra, or nothing at all?

  • Run your script under wrapture by running this command:
python -m wrapture run.py

You'll see the same fake name and email printed as before. Your program's own output is left completely alone.

  • Confirm the trace file appeared by running:
ls
  • You should see trace.jsonl listed alongside run.py and wrapture.toml.
  • Count the calls wrapture recorded by running this command:
wc -l trace.jsonl
  • You should see a count of 2, one line for the name call and one for the email call.

No trace.jsonl in the list?

  • Check you ran the command from the same folder as wrapture.toml. wrapture looks for the config in the current directory, so running from anywhere else means no config and no trace.
  • Check the spelling of target and of each entry in name. A target that binds nothing gives you no events to write.
  • Check you ran python -m wrapture and not plain python. Without the runner, nothing applies your config.
  • Why did my trace file not appear?

You have a recording of code you did not write, made without editing a single line of it. Next up, you'll open trace.jsonl and read what actually went in and came back.

Read the Call Record

Your last run left a trace.jsonl file sitting on disk, written by Wrapture while run.py went about its normal business.

A trace is only worth having if you can read it. In this step you open that file and pull out the arguments and the return value for every call to Faker.name() and Faker.email().

This is the payoff of tracing from the outside. You get the exact values that crossed the boundary, without editing a single line of Faker.

In this step, get ready to:
  • Count how many calls Wrapture recorded during your run.
  • Read one full call record, field by field.
  • Confirm both observed methods made it into the file.
Count the calls in your trace file

Your script calls two methods, and both of them are named in wrapture.yaml. So the size of the file is something you can predict before you look at it.

Before you run the next command, how many lines do you expect trace.jsonl to have?

  • Switch back to the terminal you ran run.py in.
  • Count the recorded calls by running this command:
wc -l trace.jsonl

You should see a count of 2. That is one line for each observed call your script made.

Seeing a count of 0?

An empty file usually means the terminal is sitting in a different folder to the one holding your config. Check that wrapture.yaml and run.py are listed when you run ls.

Seeing more than 2? Check how many times you have run run.py since you wrote the config.

Why is my trace file empty?

Read one call record in full

Each line in the file is one complete JSON object, squeezed onto a single line. That shape is easy for a program to append to and painful for a person to read.

  • See the raw file by running this command:
cat trace.jsonl

You should see two long lines wrapping across your terminal, with no indentation inside them.

  • Make the first record readable by running this command:
python3 -c "import json; print(json.dumps(json.loads(open('trace.jsonl').readline()), indent=2))"

What does this command do?

  • readline() grabs only the first line of the file, which is the first call that was recorded.
  • json.loads turns that line of text into a Python dictionary.
  • json.dumps with indent=2 prints it back out with one field per line.

You should now see the same record spread over several indented lines, with one field on each.

  • Find the field holding the method name and check that it says name.
  • Find the field holding the call arguments.
  • Find the field holding the return value.
  • Find the field holding the timestamp for the call.

That return value is the exact string your script printed. You are reading the value as it crossed out of a library you never edited.

Seeing an error instead of an indented record?

If Python reports that the command was not found, use the same Python command you used to run run.py earlier.

If it complains about decoding, the first line is not complete JSON. Rerun cat trace.jsonl and check the file is not empty or half written.

Help me read my trace file

Check every call is accounted for

One record is a sample. The reason to keep a trace at all is the full record, so the last thing to prove is that both observed methods landed in the file.

Before you run the next command, which method do you expect to see on the second record, and why?

  • Print every record in readable form by running this command:
python3 -c "import json; [print(json.dumps(json.loads(line), indent=2)) for line in open('trace.jsonl')]"

You should see two indented records. The first is the name call and the second is the email call, in the order your script made them.

Why the order matters

The file is a timeline, not a summary. Each line is appended as the call happens, so reading top to bottom replays the run.

That is what makes a trace useful on code you did not write. You get the sequence as well as the values.

Only seeing one record?

A missing method usually means the config never matched it. Open wrapture.yaml and check that both name and email are listed under the observed methods.

Why is one of my methods missing from the trace?

You can now read the record in full. Next up, you'll shrink it, by switching the capture mode to summary and seeing how much smaller the file gets.

Switch to Summary Capture Mode

You just read trace.jsonl line by line and found every argument and every return value sitting there in full.

That level of detail is perfect for two calls. Point wrapture at a function that runs thousands of times and the same setting will fill your disk.

There is one key in your config that decides how much of each value gets written down. In this step you measure your trace, flip that key, and measure again.

In this step, get ready to:
  • Measure the size of your full-mode trace file.
  • Switch the capture level in wrapture.toml from full to summary.
  • Rerun run.py and compare the new trace against the old one.
Measure your full-mode trace before you change anything

A file is only "smaller" against a number you wrote down first. Grab that number while your trace is still in full mode.

  • Return to the terminal you have been running run.py from.
  • Measure your current trace file by running this command:
python -c "import os; print(os.path.getsize('trace.jsonl'), 'bytes')"

You'll see a number followed by the word bytes. That is your full-mode baseline.

  • Record the number you see: your full-mode file size in bytes.

Seeing an error about a missing file?

Your terminal is probably in a different folder. Check that run.py and trace.jsonl sit in the folder you are running from.

Why can Python not find my trace file?

Change the capture level to summary

What does the capture key control?

The top-level capture key sets how much of each call's values wrapture records. It applies to every binding the config file creates.

The value full keeps the complete arguments and return values. That is exactly what you read line by line in the last step.

The value summary reduces those values to bounded summaries with a depth limit. Nothing huge or unserialisable ends up in your trace file.

  • Open wrapture.toml in your editor.
  • Find the capture line at the very top of the file, above the first [[observe]] entry:
capture = "full"
  • Replace "full" with "summary", so the line now looks like this:
capture = "summary"
  • Save wrapture.toml.

✔️ Awesome, I've got everything!

Great, double check you've saved the file and head to the rerun below.

ⓧ I'd like to double check the full code

Your whole wrapture.toml should now read like this.

capture = "summary"

[[observe]]
target = "faker:Faker"
name = "name"

[[observe]]
target = "faker:Faker"
name = "email"

[[sink]]
type = "jsonlines"
path = "trace.jsonl"
Rerun the script and compare the two files
  • Clear the old trace so your next measurement is pure summary mode by running this command:
rm trace.jsonl

Before you rerun, make a call: does the new trace.jsonl come out bigger, smaller, or about the same as the number you recorded?

  • Regenerate the trace under your new config by running this command:
python -m wrapture run.py

Your terminal prints the same fake name and email as always. The script has not changed, only the config watching it has.

  • Measure the new trace file by running the same command as before:
python -c "import os; print(os.path.getsize('trace.jsonl'), 'bytes')"
  • Record the new number: your summary-mode file size in bytes.

The summary number is smaller than the full-mode number you wrote down earlier. Same script, same two calls, fewer bytes on disk.

Trace file the same size as before?

Check you saved wrapture.toml after the edit. An unsaved config leaves wrapture running on the old capture level.

Check the line reads exactly capture = "summary", with the value in double quotes.

Check you deleted the old trace.jsonl before the rerun, so you are not measuring the old lines as well as the new ones.

Why is my wrapture trace file not getting smaller?

  • Read the first line of the new trace by running this command:
python -c "print(open('trace.jsonl').readline())"

You get one line of JSON Lines, still naming the method and still carrying a timestamp. The values inside it are noticeably shorter.

Why your trace got smaller

Summary mode did not drop any calls. Your file still holds one line per observed call to name and email.

What shrank is each line. Arguments and return values are stored as bounded summaries rather than complete objects.

Two calls make this a small saving. On a function called ten thousand times, it is the difference between a readable trace and an unusable one.

You can now dial how loud your trace is without touching a line of the library. Next up, you'll use wrapture to fake a return value in a test, still without editing the code you are testing.

Override a Return Value in a Test

Summary mode shrank your trace file down to counts and statistics, so you now have a compact record of what run.py actually did. Recording is only half of what a wrapture binding can do.

Tests that touch a library like Faker hit a wall, because a fresh random name on every run leaves nothing stable to assert on. The usual escape route is to reach into the package and swap the method out.

A binding wraps Faker.name from the outside and pins what it hands back, for the length of one test. The installed package on disk never changes.

In this step, get ready to:
  • Create a test file that imports wrapture and Faker.
  • Pin what Faker.name() returns using a binding.
  • Run the test and confirm the faker package was never edited.
Create your test file
  • Back in your terminal, create your test file by running this command:
touch test_faker_override.py
  • Confirm the file landed in the right place by running this command:
ls

You should see test_faker_override.py listed alongside run.py, wrapture.yaml and trace.jsonl.

Pin the return value with a binding

A binding names a target and the behaviour you want from it. Declaring one changes nothing on its own, and the wrapper only goes on when you enter the with block.

  • In your editor, open test_faker_override.py from the same folder as run.py.
  • Write the whole test by copying this code into the empty file:
import unittest

import wrapture
from faker import Faker

class FakerOverrideTest(unittest.TestCase):
    def test_name_is_overridden(self):
        # Name the target and say what a call should hand back.
        binding = wrapture.binding(Faker, "name").on_call.returns("Test User")

        with binding:
            fake = Faker()
            # Inside the block the wrapped call returns the pinned value.
            self.assertEqual(fake.name(), "Test User")

        # Outside the block the wrapper is off, so real names come back.
        self.assertNotEqual(Faker().name(), "Test User")

What does this code do?

  • wrapture.binding(Faker, "name") names the target: the name method on the Faker class.
  • .on_call.returns("Test User") sets the behaviour, which is the value a call hands back once the binding is applied.
  • The with block is where the wrapper actually goes on. Leaving the block takes it off again.
  • The final assertNotEqual proves the wrapper was removed, because a real random name comes back.
  • Save test_faker_override.py.

Before you run it, what do you think fake.name() gives back inside the block, and what does it give back outside?

  • Run your test by running this command:
python -m unittest test_faker_override

You should see a single dot, then OK on the last line. Both assertions held, so the pinned value came back inside the block and a real name came back outside it.

Test not passing?

  • Check you saved test_faker_override.py before running the command.
  • If the command reports that it cannot find test_faker_override, run it from the folder that holds run.py.
  • Check the class is spelled Faker with a capital F, since the binding target is case-sensitive.
  • My wrapture test is failing, help me work out why.

✔️ Awesome, I've got everything!

Great, double check you've saved your file and move on.

ⓧ I'd like to double check the full code

Here's the full test_faker_override.py file to compare against.

import unittest

import wrapture
from faker import Faker

class FakerOverrideTest(unittest.TestCase):
    def test_name_is_overridden(self):
        # Name the target and say what a call should hand back.
        binding = wrapture.binding(Faker, "name").on_call.returns("Test User")

        with binding:
            fake = Faker()
            # Inside the block the wrapped call returns the pinned value.
            self.assertEqual(fake.name(), "Test User")

        # Outside the block the wrapper is off, so real names come back.
        self.assertNotEqual(Faker().name(), "Test User")
Prove the faker package never changed

Something handed back Test User during that test. The question worth answering is whether anything on disk had to change for that to happen.

Before you run your original script again, do you expect a fixed name or a random one?

  • Run your original script by running this command:
python run.py

You should see a random name and a random email printed, exactly as they were before this step. The override lived and died inside the with block.

Why wrap instead of replace?

Swapping a method out for a stand-in means the real method never runs. Everything downstream is then answered by the stand-in, including behaviour you never thought to fake.

A binding leaves the real method in place and sits around it. You pick the one thing you want to change, and the code being watched never learns it is being watched.

Your binding can now record calls and rewrite results. Next up, you'll send those same calls out as OpenTelemetry spans.

Export Traces to OpenTelemetry

Your test passed with Faker.name() returning a fixed value and the faker package untouched. Everything you have recorded so far lives in one place though: trace.jsonl on your own laptop.

Teams do not read call records off a laptop. They ship them to a tracing backend, and the format those backends agree on is OpenTelemetry.

wrapture can turn the same events it writes to trace.jsonl into OpenTelemetry spans. You will point them at your console first, so you can see the shape of a span before wiring one up to anything.

In this step, get ready to:
  • Install wrapture's otel extra.
  • Switch on span export with a console exporter.
  • Read your Faker calls back as spans.
Install wrapture's OpenTelemetry extra

wrapture already ships the OpenTelemetry code in every release. The otel extra adds the dependencies that code needs to send anything.

  • Switch back to the terminal you have been running run.py from.
  • Install the extra by running this command:
pip install "wrapture[otel]"
  • Read pip's output once it finishes. You should see opentelemetry-sdk among the packages it installed.

Install did not finish?

  • If pip is not found, try pip3 instead. Some systems only register the versioned command.
  • Keep the quotes around the package name. Some shells try to expand the square brackets in wrapture[otel] when they are unquoted.

Still stuck on the install?

Switch on the OpenTelemetry export

wrapture reads a top-level [otel] table for everything to do with export. The table being there is the opt-in, so adding it is the whole switch.

  • In wrapture.yaml, scroll to the bottom of the file.
  • Add the export table below your existing [[sink]] entry by copying this in:
[otel]
service_name = "wrapture-demo"
signals = ["traces"]

[otel.environment]
traces_exporter = "console"

What does this table do?

  • [otel] opts you in to export. With no table, wrapture exports nothing.
  • service_name is the name your calls arrive under in a backend.
  • signals picks which signals leave the process. wrapture can also export metrics and logs, and both of those want somewhere to send them.
  • [otel.environment] sets OpenTelemetry's own environment variables for you. traces_exporter becomes OTEL_TRACES_EXPORTER=console, which prints spans to standard output instead of shipping them anywhere.
  • Save wrapture.yaml.

Before you run it, what do you expect to see: the same two printed names as always, or something more?

  • Run your script under wrapture by running this command:
python -m wrapture --config wrapture.yaml run.py

Your two printed names still appear. Around them, each call now comes back as a span printed in full, with a name, a span id, start and end times, and the attributes wrapture captured.

✔️ I see spans printed in the console

Those are your Faker.name() and Faker.email() calls in OpenTelemetry's own shape. There is still not one line of OpenTelemetry code in run.py.

ⓧ I see an error asking me to install wrapture[otel]

wrapture checks for the extra when it loads a config carrying an [otel] table, and the error names the fix rather than failing on a missing import.

  • Install the extra by running this command:
pip install "wrapture[otel]"
  • Run run.py under wrapture again.

ⓧ I see the two names but no spans

The script ran, so the config loaded. The export table is the part that has not taken effect.

  • Check that --config wrapture.yaml is part of the command you ran.
  • Check that [otel] sits at the left margin, not indented under another table.
  • Check that traces_exporter sits under [otel.environment] rather than under [otel].
  • Check that signals still includes "traces".
Check both outputs are still firing

Turning on export does not retire your jsonlines sink. wrapture registers the OpenTelemetry sink ahead of the [[sink]] list, and every sink after it hears the same calls.

  • Clear out your current trace file by running this command:
rm trace.jsonl

With spans now going to the console, do you think trace.jsonl comes back on the next run, or has the export taken over?

  • Run your script one more time by running this command:
python -m wrapture --config wrapture.yaml run.py
  • Read the trace file back by running this command:
cat trace.jsonl

Your summary entries for name and email are back, with the spans printed above them. One set of calls reached two destinations.

Why both destinations fire

The [otel] table builds a sink like any other. It just always registers first, so it hears a call before anything in the [[sink]] list does.

That is the shape you would ship to a real backend. Swap the console exporter for an endpoint and the same config sends these spans somewhere your team can search them.

trace.jsonl did not come back?

  • Check your [[sink]] entry is still in the file. Adding the export table below it leaves it alone, but replacing it removes your jsonlines output.
  • Check you passed --config wrapture.yaml on the rerun. Without it, wrapture looks for its own default config file and will not find yours.

Still no trace file?

Secret mission

Turn Your Trace Into a Report

Your trace file holds the record of every call wrapture watched. Reading raw JSON lines by eye gets old fast. Write a small reader that turns trace.jsonl into a report you can scan, then sweep it for values you would not want to commit.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely on your own machine with open source packages, so there are no ongoing costs.

Resources you used:

  • run.py, the script that calls the Faker methods you traced.
  • wrapture.yaml, your config with the summary capture mode, the jsonlines sink, and the OpenTelemetry console exporter.
  • trace.jsonl, the recorded call data from your last run.
  • test_faker_override.py, the test that overrides a return value from outside the library.
  • The wrapture and faker packages installed into your Python environment.

Keep everything running

No action needed. Choose this if you're still actively building on this trace setup.

  • Keep wrapture.yaml as the reusable part of this project.
  • Point the same config at another class whenever you want to watch different code.
  • Leave wrapture and faker installed in your Python environment.

Pause - I'll come back to this later

Clear out the recorded data but keep your config and scripts. Choose this if you want to pick this trace setup back up later.

  • Delete the recorded call data on macOS or Linux by running this command:
rm trace.jsonl
  • On Windows, delete the recorded call data by running this command instead:
del trace.jsonl
  • Keep wrapture.yaml, run.py, and test_faker_override.py so you can rerun the trace in one command later.

Why clear the trace file first

A trace records the arguments passed to every call it watches. Anything sensitive that flowed through those methods is now sitting in trace.jsonl in plain text.

Clearing it before you walk away keeps recorded arguments out of your repo.

Delete - I don't want to use this again

Remove all project resources and start fresh. Choose this if you don't plan to use wrapture again.

  • Delete your four project files on macOS or Linux by running this command:
rm run.py wrapture.yaml trace.jsonl test_faker_override.py
  • On Windows, delete the four files by running this command instead:
del run.py wrapture.yaml trace.jsonl test_faker_override.py
  • Confirm the files are gone by running:
ls
  • Uninstall both packages by running this command:
pip uninstall wrapture faker
  • Type y and press Enter when pip asks you to confirm each package.

Still seeing the files listed?

Your terminal may be sitting in a different folder than the one holding your project files. Run pwd on macOS or Linux to see where you are.

Move into the folder you created your project files in, then run the delete command again.

Nice Work!

Nice Work!

Nice work. You've just traced a Python library you did not write, from the outside, without changing a single line of its source.

You've learned how to:

  • Point wrapture.yaml at faker.Faker and record every call to trace.jsonl, with no edits to the library.
  • Read the arguments and return values back out of your trace file, then shrink it by switching capture mode to summary.
  • Override a return value from a test through wrapture, and watch the same calls come out as OpenTelemetry spans.
  • Take on the optional Secret Mission to push your debugging skills further.

Ready to quiz yourself?