Course / 04 Code / Lesson 09 of 11

How to read a script someone else wrote

Estimated time: 15 min reading · 15 min activity

A script can print the result you wanted and then overwrite a file on the next line. Read it before running it: identify what it reads, what it displays, and which operations could change stored data. A reassuring filename or comment cannot answer those questions.

A function groups statements under a name. In def count_lines(path), path is a parameter, the name that receives an argument when the function is called. The indented body describes what that call would do. Defining a function does not, by itself, call its body.

A module is a unit of Python code loaded with import. Importing a module can execute its top-level statements, the lines outside function bodies, on its first import. A side effect is an observable effect beyond a returned value, including displayed output or changed files. Here, distinguish display effects from filesystem changes: printing is output; writing or deleting a file changes stored data.

Before you begin

Estimated time
15 min reading, 15 min activity. Setup and optional practice may take longer.
Where you work
Read and trace the supplied source and input in your browser. A later optional run uses Python 3.12 and its standard library in a fresh directory under your own ~/yanosec-lab. The ~ means your home directory. Keep paired files together and avoid overwriting existing work. No extra packages or network calls are required.
Without a lab
Follow each source line against the supplied input and predicted trace. You can complete the same reasoning without running Python; the practical run can wait. Earlier recorded Python results are labeled separately from predictions. Reading works without JavaScript. Answer checking needs JavaScript and a secure context; saving additionally needs usable browser storage.

After this lesson you can:

  • Name a script's input, display output, and modifying line.
  • Treat a comment and a filename as claims, not as proof of safety.
  • Refuse to run a review card that writes or deletes.

01Example

Read the function and the separate deletion call as source, not as instructions to execute. splitlines divides the text into lines and len counts them, so the function would display a line count. The separate os.remove(path) call would delete the named file. It is outside the function and was not called.

def count_lines(path):
    with open(path, encoding="utf-8") as handle:
        data = handle.read()
    print(len(data.splitlines()))

Input: the file at path, as UTF-8 text.
Output: print of a line count.
File change: none in this function.

Separate call, not part of the function:
os.remove(path)
Documented effect: delete the file named by path.
Do not run it.

02A comment does not cancel a write

The default read mode retrieves data. Mode w opens a file for writing and truncates an existing file, meaning its old contents are discarded. Mode wb does the same for binary writing. handle.write then supplies the replacement content. The destructive change can therefore begin at open, before the write call.

A comment starts with # and explains something to the reader; Python does not execute that comment text as an instruction. The comment “safe to share” does not cancel a later write. Follow the path and mode instead of trusting the comment.

The review cards use Line A, Line B, and similar labels, and keep a .txt suffix. They are evidence to annotate, not scripts to run. Read the import statements and top-level calls as well as the function body before trusting an unfamiliar program.

Trace read access and line-count output in the function; review the delete effect on a separate, unexecuted call.Model. A teaching model drawn by hand. It shows the idea, not real tool output.

Read the full explanation below.

Worked read/count function

def count_lines(path):
    with open(path, encoding="utf-8") as handle:
        data = handle.read()
    print(len(data.splitlines()))
Read UTF-8 fileRead file textDisplay line count

Separate call, outside the function

os.remove(path)
Delete effect
What this part means

The function opens the file at path as UTF-8 text and reads it.

No unknown file contents are invented.

The function opens the file at path as UTF-8 text and reads it.

Follow the explanation

  1. The function opens the file at path as UTF-8 text and reads it.
  2. print displays a symbolic line count; the source supplies no numeric result.
  3. The separate os.remove(path) call deletes the named file.
Read every state and value

1. The function opens the file at path as UTF-8 text and reads it.

Selected parts
Read UTF-8 file · Read file text
Explanation
read-note

2. print displays a symbolic line count; the source supplies no numeric result.

Selected parts
Display line count
Explanation
print-note

3. The separate os.remove(path) call deletes the named file.

Selected parts
Delete effect
Explanation
remove-note
Trace read access and line-count output in the function; review the delete effect on a separate, unexecuted call.

03Mark the read, the print, and the write

The activity shows a function named publish that reads and prints text, then opens the same path for writing. Mark its effects without renaming or launching the card.

YanoSec / Code
Review card. Do not save this as a .py file and do not run it.
The line labels keep it from being a program.

Line A: def publish(path):
Line B:     with open(path, encoding="utf-8") as handle:
Line C:         data = handle.read()
Line D:     print(data)
Line E:     # comment says: safe to share
Line F:     with open(path, "w", encoding="utf-8") as handle:
Line G:         handle.write("replaced")

Input: the file named by path, read as UTF-8 text.
Display output: print(data).
Comment: line E. A comment does not change what line F does.
Modifying part: lines F and G. Mode w discards an existing file’s contents when open succeeds, before the write.
The read on lines B and C does not replace the file.

Download the non-runnable review card

  1. Find the path parameter on line A. Trace it into both open calls so you can see they refer to the same path.
  2. Mark B and C as the text read, D as displayed output, and E as a comment. A read and a print do not erase the input file.
  3. Mark F as the point that opens the path with mode w. Mark G as the write of replaced. Compare with the card: F and G are the modifying part, and F can discard existing contents.
  4. Stop when your notes name the input, the display output, and the modifying lines. Keep the .txt suffix. No execution or cleanup is needed.

04Python module case

Use the separate Python case to connect byte counting with script review. It supplies two source bytes and a function with a different destination path. Review the rubric after making your prediction.

YanoSec / Code
Python module self-review case.
Review card. Do not save this as a .py file and do not run it.

Supplied source bytes, hex: 41 42
That is two bytes.

Line A: def copy_out(source, dest):
Line B:     with open(source, "rb") as handle:
Line C:         data = handle.read()
Line D:     print(len(data))
Line E:     with open(dest, "wb") as handle:
Line F:         handle.write(data)

Predicted display from line D, for the supplied two bytes: 2
Line D prints a count. It does not delete source.
Lines E and F write those bytes into dest. That changes dest.
Do not run the card to discover the write. The mode wb is the evidence.
Rubric: name the input, the printed count, and the modifying lines.
State that a comment or a harmless filename would not have made line E safe.

Download the script-review case

  1. Count 41 42 as two bytes. Follow the binary read into data, then predict that line D displays 2.
  2. Trace source and dest separately. Lines B and C read source; line E opens dest with wb and line F writes the bytes there.
  3. Explain that a harmless-looking destination name does not prevent mode wb from replacing existing destination contents. The source is read; the destination is changed.
  4. Stop with the annotated card. Do not create a destination or try a deletion to confirm what the source says.

05Optional challenge

Spend about ten extra minutes writing a read-only version in notes. Keep the activity’s read and display, and omit the write block. Check that your sketch has no writing mode or deletion call. This removes the shown file change; it does not prove every possible imported module is safe.

06Fresh cases for the checks

Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.

Script case A: labeled source for reviewOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

A: def summarize(source, destination):
B:     with open(source, encoding="utf-8") as handle:
C:         text = handle.read()
D:     print(len(text.splitlines()))
E:     # display only; no changes
F:     with open(destination, "w", encoding="utf-8") as handle:
G:         handle.write("reviewed")
H: summarize("input.txt", "summary.txt")
Assume both files exist; input.txt contains two text lines. These are source predictions, not an execution.

07Explain your prediction

Optional written self-check: Record input, output, first modifying operation and destination. Follow top-level calls as well as the function body, and distinguish a source prediction from recorded execution evidence.

08Common mistakes

If the comment is your only evidence of safety, inspect the next operation and its mode. Compare that behavior with the stated purpose before considering a run.

If you inspect only function bodies, find the top-level statements and imports too. They can have effects before a function you were interested in is called.

If you rename a review card to .py to discover its behavior, keep it as text instead. The path and mode already supply the evidence needed for this lesson.

References