Python Essentials

Script Arguments

sys.argv (basic)

import sys
# python myscript.py first 2 True
# sys.argv = ['myscript.py', 'first', '2', 'True']
# Always: list of strings; sys.argv[0] = script name

argparse (preferred)

import argparse
 
parser = argparse.ArgumentParser()
 
# Positional (mandatory, order matters)
parser.add_argument("word", type=str)
parser.add_argument("number", type=int)
 
# Optional (use -- prefix, order doesn't matter, default=None)
parser.add_argument("--verbose", action="store_true")
parser.add_argument("--output", type=str, default="out.txt")
 
args = parser.parse_args()
print(args.word, args.number, args.verbose)

Run: python myscript.py hello 42 --verbose --output result.txt

Auto-generates --help and error messages.

if __name__ == "__main__" Guard

def main():
    # all real logic here
    pass
 
if __name__ == "__main__":
    main()
  • __name__ == "__main__" when file is run directly
  • __name__ == "module_name" when file is imported

Prevents top-level code from running when your module is imported by another script.

Virtual Environments (venv)

5-Command Cheatsheet

python -m venv .venv          # 1. Create
source .venv/bin/activate     # 2. Activate (prompt shows (.venv))
pip install <package>         # 3. Install (goes to .venv, not system)
pip freeze                    # 4. Check installed packages
deactivate                    # 5. Deactivate

pip commands

pip show gpiozero             # location, version, dependencies
pip freeze > requirements.txt # lock dependencies
pip install -r requirements.txt # recreate venv on another machine

venvs are non-portable

Moving/copying a project? Recreate the venv — don’t move the .venv folder.

sudo + venv problem (critical for reTerminal)

# WRONG: sudo uses root's python, not venv
sudo python script.py           # Module not found error
 
# CORRECT: point sudo to venv's python
sudo $(which python) script.py  # command substitution
sudo ./.venv/bin/python script.py  # relative path

apt vs pip

aptpip
SourceDebian repoPyPI
Use forSystem tools, appsProject dev libraries
Namingpython3-<name>exact PyPI name
In new DebianAlways worksMust use venv

Logging

import logging
 
# Create logger (best practice: one per file)
logger = logging.getLogger(__name__)
 
# Setup (call once at app startup)
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
 
# Log levels (in increasing severity)
logger.debug("Debug info")
logger.info("General info")
logger.warning("Warning")
logger.error("Error occurred")
logger.critical("Critical failure")

Log to file

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
 
handler = logging.FileHandler('app.log')
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

pytest

Basic test

# test_sample.py
 
def func(x):
    return x + 1
 
def test_answer():
    assert func(3) == 4   # passes

Run: pytest / pytest tests/unit/ / pytest -v

Fixtures

import pytest
 
@pytest.fixture
def my_fixture():
    return 42          # Arrange step
 
def test_uses_fixture(my_fixture):
    assert my_fixture == 42   # Act + Assert

conftest.py — Share fixtures

Place in tests/conftest.py → available to all test files in that directory and below.

Async tests

import pytest
 
@pytest.mark.asyncio
async def test_async_function():
    result = await some_async_function()
    assert result == expected_value

AAA Pattern

  • Arrange: set up test conditions (use fixtures)
  • Act: call function under test
  • Assert: check result

subprocess — Run Bash from Python

import subprocess
 
# Run a command (list form — safer than shell=True)
subprocess.run(['ls', '-la'])
 
# Capture output
result = subprocess.run(['ls', '-la'], capture_output=True, text=True)
print(result.stdout)
print(result.returncode)   # 0 = success
 
# Raise exception if command fails
subprocess.check_call(['./script.sh', 'arg1'])
 
# Call a bash script (relative path)
subprocess.run(['../bash/backlight', '255'])

Key point: pass arguments as a list, not a single string, to avoid shell injection and quoting issues.

See Also