PyRaspBeta

Runtime: time, files and input

Your main.py does not run on your computer's clock or a real Pi's clock — it runs inside the simulation. That is what lets a three-hour objective run in seconds when it is checked, and it is why time.sleep, datetime.now(), threads and input() all behave a little differently from a desktop Python.

Simulated time

Time zero is the moment the board is powered on (in the sandbox, the moment you press Run; the contest and practice runs start from the world's own date). The standard time module is patched to read the simulation's clock:

CallReads
time.sleep(seconds)sleeps for simulated seconds
time.monotonic(), time.perf_counter()seconds since the simulation started
time.time()Unix time: the world's start moment plus simulated seconds
time.localtime(), gmtime(), ctime(), strftime()formats the simulated time
datetime.now(), datetime.utcnow(), date.today()the simulated time
import time
from datetime import datetime

print(time.time())            # seconds since 1970 for the simulated date
print(datetime.now())         # e.g. 2026-06-21 09:00:03.250000

start = time.monotonic()
time.sleep(2.5)
print(time.monotonic() - start)     # 2.5

The simulated clock can run faster or slower than real time. In Lab mode you can pause the run — your program pauses with it — or pick a speed from 0.1× to 100×: at 10×, a five-second sleep takes half a second of your life. Short sleeps are special-cased: anything up to 2 ms runs the simulation straight through instead of waiting for the next step, so bit-banging drivers that pulse a pin for 10 µs are both exact and fast.

Threads and callbacks

The interpreter is single-threaded, but threading is replaced by cooperative green threads: each Thread is a task in the interpreter's event loop, and every blocking call — sleep, Event.wait, Lock.acquire, Thread.join, GPIO edge waiting — suspends the calling thread and lets the others run. Locks, events, conditions and re-entrant locks are all provided, including the with lock: form.

from threading import Thread, Event
from time import sleep

stop = Event()

def worker():
    while not stop.is_set():
        print("tick")
        sleep(1)

t = Thread(target=worker, daemon=True)
t.start()
sleep(3)
stop.set()
t.join()

Threads only switch at blocking calls. A loop that spins without sleeping would starve the others, so GPIO.input() also yields every few milliseconds of real time — polling loops like while not GPIO.input(27): pass still let timers and callbacks run. A thread that raises prints its traceback and stops only that thread; daemon threads are discarded when the program ends.

Console input

input() reads a line from the console panel. The optional prompt is printed first, and the program waits as long as it has to — simulated time keeps running — until you type a line and press Enter:

name = input("What should I call you? ")
print(f"Hello, {name}!")

In a judged run there is nobody to type: the judge supplies a fixed list of input lines for tests that need them, and input() returns an empty string when that list runs out. Do not write a program that requires typing and cannot finish without it unless you only ever run it by hand.

Files

The working directory is /home/pi, and that is where relative paths land:

with open("notes.txt", "w") as file:
    file.write("hello from the simulation\n")

print(open("notes.txt").read())

Files you create live for the length of the run in the interpreter's filesystem. The Pi is fresh for each Run, so nothing written is still there next time, and only files the program declares as saved outputs (a Picamera2 capture) appear in the Lab's Files panel. Read-only files that the kernel normally provides are simulated where they matter: /proc/cpuinfo and /proc/device-tree/model describe a Raspberry Pi 4 Model B, /sys/class/thermal/thermal_zone0/temp reports the CPU temperature, and the /sys/bus/w1/devices/28-* files carry DS18B20 readings (see the DS18B20 part page).

Running forever, stopping and exiting

A while True: loop is fine. Your program keeps running in real time until you press Stop, which terminates the interpreter and resets the board for the next run. In the contest and practice runs the judge runs the program for the objective's time limit and then stops it; a program still looping at the end is normal, as long as the checks it was given all pass.

sys.exit() ends the program in the usual way: sys.exit(0) (or returning from main.py) is success, any other number is a failure, and a string is printed to stderr and counted as a failure. An uncaught exception prints a traceback that starts at your own line — the runner's frame is stripped — and exits with status 1. signal.pause() is provided and blocks until Stop, which is the tidy way for a callback-driven program to wait.

Printed output, including partial lines, appears in the console as it is written. stderr is shown the same way, so tracebacks show up live.

Going deeper

Every Run starts fresh, so nothing carries over between runs: files written by the program, imported modules and running threads are all gone. Everything on these pages imports from main.py without installing anything.

When a run ends, the board is released with it. A program that switches an LED on and then simply finishes does not leave the LED lit — to keep something driven, the program has to keep running, for example waiting in signal.pause() or in a loop.

Simulator vs real hardware

  • Time is a simulation. A sleeping program costs the simulation its simulated seconds, not real ones; a run at 10× makes real deadlines (network timeouts, audio) meaningless. There are no NTP, no time zones on the machine beyond the world clock, and no clock drift.
  • No OS threads. A compute-heavy loop without blocking calls stalls every other green thread; on a real Pi the OS would preempt and schedule them. Blocking C extensions (a long numpy operation, for instance) hold the interpreter while they run.
  • input() has no terminal. It reads from the console panel, not a TTY; there are no line editing, EOF or signal semantics beyond the ones described.
  • The filesystem is fresh each run. On a real Pi files persist between runs; here /home/pi resets. The simulated /sys and /proc files exist only while the run is active, and only the ones the runtime writes.
  • Stop is a kill. Real Ctrl+C sends a signal that lets finally blocks and KeyboardInterrupt handlers run; pressing Stop terminates the interpreter outright, so make sure anything you care about has already been printed or captured.