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
- Recommended first
- An editor that works under pressure, Reading and making files, A script that does one real chore, Hex, Base64, and XOR in code
- 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()))
Separate call, outside the function
os.remove(path)
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
- The function opens the file at path as UTF-8 text and reads it.
- print displays a symbolic line count; the source supplies no numeric result.
- 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
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.
- Find the path parameter on line A. Trace it into both open calls so you can see they refer to the same path.
- 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.
- 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.
- 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.
- Count 41 42 as two bytes. Follow the binary read into data, then predict that line D displays 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.
- 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.
- 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
- Python 3.12: os.remove
Documents removal of a named file. Recognize this effect without calling it.
- Python 3.12 tutorial: open modes
Section 7.2 explains why opening an existing file with mode w discards its contents.
- Python 3.12 tutorial: More on Modules
Section 6.1 explains when module statements execute during import.