Skip to main content

From source text to running program

A processor understands only its own machine instructions, so Python translates your text first: it compiles the whole file to bytecode, then its virtual machine runs that one step at a time. Worked on two broken files, one that never starts and one that stops halfway.
P01.1.2L0 Foundation25 minIn review

Before this lesson

P01.1.1 A program is a list of instructions

After this lesson you can

  • Describe the two stages by which Python runs a file: compiling the whole source text to bytecode, then running the bytecode on its virtual machine.
  • Compare a compiled language with an interpreted one: what is translated, when, and what each way costs.
  • Predict how much of a broken program runs: nothing for a syntax error anywhere in the file, and every line above the failing one for a misspelt name.
  • Tell a syntax error from a NameError by the stage at which Python finds it.

In review. The maths and code on this page are checked by automated tests on every change, but an independent reader has not signed it off yet. Spot a mistake or a confusing step? Tell us.

The question

Two copies of the four-line program from the last lesson each carry one small mistake. In the first, the last line has lost its closing bracket; in the second, line 3 says prnt instead of print. The first prints nothing at all, not even its three good lines, while the second prints two lines and stops. Why do two small mistakes stop a program at such different points?

From the last lesson

1. In what order does Python carry out the lines of a program?

Show answer

One at a time, from the top line down, each one completely before the next, in the order in which they are written.

2. What does print("9 * 2") show, and what does print(9 * 2) show?

Show answer

The first shows the text 9 * 2, exactly as written. The second shows 18, because Python works out the product first.

Intuition

The processor of the last lesson cannot read Python. It understands only its own machine instructions: tiny steps such as “add these two numbers” or “copy this value”, each stored as a pattern of 0s and 1s (P01.1.4). To the processor, a line such as print(9 * 2 + 33) is not an instruction at all, only a row of characters.

So Python needs a translator. The translator is itself a program, the interpreter: the program you install when you install Python. When you ask it to run a file, it works in two stages.

  1. Compile. It reads the whole source text, checks that every line is written correctly, and translates it into bytecode: a list of small, simple steps, much closer to what a processor does.
  2. Run. Its virtual machine carries out the bytecode, one step at a time.
A flow diagram. Top left, the source text quiz.py: the four print lines of the last lesson. An arrow leads right into a dashed outline labelled the Python interpreter, which holds three boxes stacked from top to bottom: a green box 1 Compile, check and translate the whole file; a grey box listing the bytecode for line 1, LOAD_NAME print, PUSH_NULL, LOAD_CONST 'Study table', CALL 1, POP_TOP, then five steps for each further line; and a green box 2 Run, the virtual machine carries out the steps one at a time. Arrows lead down through the three boxes, and a final arrow leads left to the output panel, which shows Study table, 300, 51 and Done in vermillion.
P01.1.2-F1

How Python runs a file. Stage 1 compiles the whole source text into bytecode; stage 2, the virtual machine, carries the bytecode out one step at a time. The bytecode shown is line 1 as Python 3.15 lists it.

The virtual machine is not a second machine in the box. It is part of the interpreter, a program made of machine instructions, which the real processor carries out.

The two stages explain the two broken files. A missing bracket breaks the rules of how Python is written, so the compile stage cannot finish. No bytecode is made, and nothing runs. A misspelt name is written correctly, so the file compiles. The mistake shows only when the virtual machine reaches that line and finds nothing called prnt.

Build it up

Step 1. Compiled languages translate once, ahead of time. A laptop and a phone often have processors with different machine instructions, so machine code runs only on the family of processor it was made for. In a compiled language such as C, a program called a compiler translates the source text into machine code once and saves it as a file of its own, which then runs directly, and fast. The price: the file suits one family of processor, and every change must be compiled again.

Step 2. Python translates every time. In an interpreted language such as Python, you hand the source text itself to the interpreter, which translates and runs it in one go, every time. You can change a line and run it again at once, and the same text runs on any computer that has Python. The price is speed: a program generally runs more slowly than the same work in machine code.

Two rows of boxes joined by arrows. Top row, titled A compiled language, such as C: source text hello.c, then a green compiler box that runs once, then a blue box of machine code, a file of its own, then the processor, which runs that file. A note underneath says: translated once, ahead of time; fast, but the file suits one kind of processor. Bottom row, titled An interpreted language, Python: source text quiz.py, then a wide green box, Python interpreter, compile to bytecode, then run it, then a vermillion output box. A note says: translated every time it runs, by a program the processor runs; slower, but no separate step, so you can change the text and run it again.
P01.1.2-F2

Two ways to get from source text to a running program. A compiled language makes a file of machine code once; Python translates the text each time it runs.

That is why the libraries machine learning uses for heavy arithmetic, such as NumPy (P02), do the hard work in compiled code and are called from Python: Python’s convenience, machine code’s speed.

Step 3. Bytecode sits in between. Python’s translation stops halfway, at bytecode. The Python glossary calls Python interpreted, “though the distinction can be blurry because of the presence of the bytecode compiler”.

Step 4. Two stages, two kinds of mistake. Compiling checks the syntax of the whole file: whether each line follows the rules of how Python is written, such as every opening bracket having a closing one. A line that breaks a rule is a syntax error, and the file cannot be translated. Every other problem waits for the run, such as a name that does not exist, like prnt: a NameError.

Go deeper: what bytecode looks like

Python’s tool dis lists the bytecode of a program (modules such as dis come in P01.5). For line 1, Python 3.15 lists (with its wide spacing shortened):

1  LOAD_NAME   0 (print)
   PUSH_NULL
   LOAD_CONST  0 ('Study table')
   CALL        1
   POP_TOP

In words: look up the name print, prepare the call, take the text ‘Study table’, call print with that one value, and drop print’s own result. The other lines take the same five steps, and the listing of line 4 ends with one or two more, which end the program. One surprise: the steps for line 2 load the number 300 directly, because the compiler saw a sum of fixed numbers and worked it out in advance. The output is the same. Bytecode may change between versions, as the documentation of dis warns: Python 3.12 lists the first two steps of line 1 the other way round.

The formal statement

NoteDefinitions
  • Machine code is a program in a processor’s own instructions, the only kind it can carry out.
  • The interpreter is the program that runs Python code. The standard one, from python.org, is called CPython.
  • To compile is to translate source text into another form before it runs. Python compiles a file to bytecode, the form in which the interpreter holds a program.
  • The virtual machine is the part of the interpreter that carries out bytecode, step by step. The Python glossary calls a virtual machine “a computer defined entirely in software”.
  • A syntax error is a mistake in how the code is written. It is found while compiling.
  • A NameError is a mistake found while running: a line uses a name that Python does not know.
NoteHow Python runs a file
  1. The interpreter reads the whole file and compiles it to bytecode. If any line has a syntax error, it reports the first one it finds and stops. No line of the file has run.
  2. Otherwise the virtual machine carries out the bytecode line by line, from the top, by the rule of order of P01.1.1.
  3. If a line fails while it runs, for example with a NameError, the program stops at that line. Every line above it has run, and their output stays on the screen. No line below it runs.

So in a four-line file with the mistake in line \(k\), a syntax error lets none of the lines run, whatever \(k\) is, and a misspelt name lets the \({k - 1}\) lines above it run.

Why can the compiler not catch a misspelt name? The compile stage checks only the shape of each line. prnt(9 * 2 + 33) has the same shape as print(9 * 2 + 33): a name, then brackets with a sum inside. Whether a name stands for anything can only be known when the line runs, because a program can make new names as it goes (P01.2.2). So that check waits for the run.

Worked example

How far does each broken file get?

File A, quiz_a.py, is the program of the last lesson with the closing bracket of line 4 missing:

print("Study table")
print(55 + 65 + 80 + 40 + 60)
print(9 * 2 + 33)
print("Done"

Stage 1, compile. The interpreter reads all four lines. Lines 1 to 3 are fine. Line 4 opens a bracket and never closes it, which breaks a rule of Python’s syntax. Compiling stops with a syntax error.

Stage 2, run. It never starts: there is no bytecode to run. Not one line runs, not even line 1. The screen shows only the error report, copied here from Python 3.15 (the wording varies a little between versions, and on your computer the file name includes its folder):

  File "quiz_a.py", line 4
    print("Done"
         ^
SyntaxError: '(' was never closed

File B, quiz_b.py, has all its brackets, but line 3 misspells print:

print("Study table")
print(55 + 65 + 80 + 40 + 60)
prnt(9 * 2 + 33)
print("Done")

Stage 1, compile. Every line has the shape of a correct line of Python, a name followed by brackets, so the whole file compiles to bytecode.

Stage 2, run. Line 1 runs and prints Study table. Line 2 runs and prints 300. Line 3 asks for the name prnt, which Python does not have, so the program stops with a NameError. Line 4 never runs. The screen shows (Python 3.15 again):

Study table
300
Traceback (most recent call last):
  File "quiz_b.py", line 3, in <module>
    prnt(9 * 2 + 33)
    ^^^^
NameError: name 'prnt' is not defined. Did you mean: 'print'?
Two panels, one per broken file. Top, File A, line 4 has lost its closing bracket: a cross and the words Compile, stops at line 4, and beside them Run, never starts. The code has line 4 on a reddish-purple band with a marker under its opening bracket, and every line is labelled never ran. A large 0 of 4, lines ran, stands at the right. The output panel shows File quiz_a.py, line 4, and SyntaxError: '(' was never closed. Bottom, File B, line 3 misspells print as prnt: a tick and Compile, no mistake found, and a cross and Run, stops at line 3. Lines 1 and 2 are ticked as ran, line 3 is on a reddish-purple band with a marker under prnt and a cross, stopped here, and line 4 is labelled never ran. A large 2 of 4, lines ran. The output shows Study table and 300 in vermillion, then NameError: name 'prnt' is not defined.
P01.1.2-F3

How far each broken file gets. A syntax error stops the compile stage, so 0 of 4 lines run; a misspelt name stops the run at line 3, after 2 of 4 lines have run.

Code

A cell that stopped with an error would stop this page from being built, so the broken files above appear as plain text with their real messages. The good program runs:

print("Study table")
print(55 + 65 + 80 + 40 + 60)
print(9 * 2 + 33)
print("Done")
Study table
300
51
Done

Python checks the spelling of names, not of text inside quotes. A string is shown exactly as written, mistakes and all, with no message:

print("Dnoe")
Dnoe
Go deeper: doing the two stages yourself

Two built-in functions do the interpreter’s two stages separately: compile turns source text into bytecode, and exec runs it.

exec(compile("print(9 * 2 + 33)", "quiz.py", "exec"))
51

Given print(9 * 2 + 33, compile stops with a syntax error. Given prnt(9 * 2 + 33), it compiles without complaint, and only exec stops, with a NameError.

Explore

P01.1-X1

Line-by-line stepper

Press Step: the first press compiles the whole file, and each press after that runs one line and adds its output. Move a line and watch the output reorder, or plant a missing bracket or a misspelt name in any line and see how far the program gets.

The static version: two broken copies of the four-line program. File A has lost the closing bracket of line 4, so compiling stops and 0 of 4 lines run; the output is only SyntaxError: '(' was never closed. File B misspells print as prnt in line 3, so it compiles, lines 1 and 2 run and print Study table and 300, and the run stops at line 3 with a NameError: 2 of 4 lines ran.

Predict first, then check: with a missing bracket in line 1, how many lines run? With a misspelt name in line 1, or in line 4?

Pitfalls

Wrong beside right. At the top, the file quiz_a.py, whose line 4 has lost its closing bracket and sits on a reddish-purple band, with the question: what appears when you run this file? Bottom left, marked with a cross and titled Each line run as it is read?: a screen showing Study table, 300 and 51, then SyntaxError: '(' was never closed, with the note that you never see this, because lines 1 to 3 do not print anything. Bottom right, marked with a tick and titled The whole file comes first: a screen with only the SyntaxError line, and the note that nothing else appears, since the file could not be translated, so not one of its lines ran.
P01.1.2-F4

Wrong beside right. If Python ran each line as it read it, the first three lines would print before the error. It compiles the whole file first, so only the error appears.

“Python reads a line, runs it, then reads the next.”

Not when it runs a file. The whole file is compiled first, so a syntax error even in the last line stops everything, and the good lines above it never print. The belief comes from the interactive prompt of the next lesson, which does take one line at a time.

“The program ran without an error message, so it is right.”

Python checks the syntax, and while running it checks that names exist. It never checks that the program does what you meant: print("Dnoe"), or the swapped lines of P01.1.1, run without complaint. Only comparing the output with a result worked out by hand catches them. In machine learning this is the dangerous kind: a model trained on wrongly prepared data runs smoothly and answers worse (P01.8).

“Python is interpreted, so it never compiles.”

It compiles every file to bytecode before running it; it just does not make machine code. The two words say where a language’s usual translation stops, not whether there is one.

Check yourself

Recall

1. Why can a processor not run a line of Python directly?

Show answer

A processor understands only its own machine instructions. A line of Python must first be translated, which is the interpreter’s job.

2. Name the two stages the interpreter goes through when it runs a file, and say what each does.

Show answer

Compile: it reads the whole file, checks its syntax and translates it to bytecode. Run: its virtual machine carries out the bytecode one step at a time, from the top.

3. What is bytecode, and what carries it out?

Show answer

The list of small steps that Python compiles a program into. The interpreter’s virtual machine carries it out.

4. A four-line file has a syntax error in line 4. How many of its lines run, and why?

Show answer

None. The syntax error stops the compile stage, so no bytecode is made and the run stage never starts, even for the three good lines above it.

5. A four-line file misspells a name in line 3. How many lines run, and why did compiling not catch it?

Show answer

Two, lines 1 and 2. The misspelt line has the right shape, so the file compiles; the unknown name is found only when line 3 runs, and the program stops there.

Exercises

1. ★ For each line, say whether Python stops while compiling, stops while running, or does not stop at all, and what the line shows if it runs. (a) print("Done" (b) prnt("Done") (c) print("Dnoe") (d) print(9 * 2 + )

Show solution

(a) It stops while compiling: the bracket is never closed, a syntax error.

(b) It compiles, then stops while running, with a NameError: Python has no name prnt.

(c) It does not stop. It shows Dnoe: Python does not check spelling inside quotes.

(d) It stops while compiling: a plus sign with nothing after it is a syntax error (Python 3.15 reports SyntaxError: invalid syntax).

2. ★★ A file has ten lines, print(1), print(2) and so on up to print(10). What appears on the screen if line 7 becomes prnt(7)? And if it becomes print(7, its bracket lost?

Show solution

With prnt(7) the file compiles. Lines 1 to 6 run and print the numbers 1 to 6, one per line: \({7 - 1 = 6}\) lines. Line 7 stops the run with a NameError; lines 8 to 10 never run.

With print(7 the file does not compile, so nothing runs: the screen shows only the syntax error message, which names line 7.

3. ★★★ This five-line file has two mistakes. What appears when you run it, and which mistake does Python report? You fix that one and run the file again: what appears now?

print("Study table")
prnt(55 + 65 + 80 + 40 + 60)
print(9 * 2 + 33)
print("Done")
print("End"
Show solution

Compiling reads the whole file first and stops at the syntax error in line 5, a bracket never closed. Nothing runs, so the screen shows only that error. The misspelt name in line 2 is never reached, so Python says nothing about it.

With the bracket fixed, the file compiles. Line 1 prints Study table, then line 2 stops the run with a NameError. A syntax error is always reported before any mistake found while running, because it stops the earlier stage.

Summary card

Python compiles the whole file to bytecode, then runs the bytecode one step at a time.

The anchor figure again: the source text quiz.py goes into the Python interpreter, which compiles the whole file to bytecode and then runs the bytecode on its virtual machine, one step at a time, producing the output Study table, 300, 51, Done.
P01.1.2-F1

Compile, then run.

  • Translation is needed. A processor runs only its own machine instructions.
  • Compiled languages translate once into a file of machine code; Python translates every time it runs, to bytecode for its virtual machine.
  • Syntax error: found while compiling, so no line runs: 0 of 4.
  • NameError: found while running, at its line, so the \({k - 1}\) lines above it run.
  • No message is not proof. A program can run cleanly and still be wrong.

Sources and next steps

Sources. Each was opened and checked against this lesson on 12 November 2026.

Next. P01.1.3 Running Python yourself installs Python and runs it both ways: line by line at its prompt, and as a whole file.

This lesson unlocks

P01.1.3 Running Python yourself

Your progress is saved in this browser only. No account, no tracking.
Back to top