Python Tutorial
The os, sys and pathlib Modules
Scripts constantly interact with their environment: reading environment variables, listing and creating folders, building file paths that work on Windows and Linux, checking the Python version, or exiting with an error code. The os, sys and pathlib modules handle all of this, and shutil adds high-level copying and moving.
This lesson covers each module's most useful features, with a strong recommendation to use pathlib for paths in new code.
pathlib: Object-Oriented Paths
Path objects represent file-system paths. Join them with /, read parts with .name, .stem, .suffix, .parent, check with .exists(), .is_file(), .is_dir(), create with .mkdir(parents=True, exist_ok=True), read and write with .read_text()/.write_text(), and search with .glob("*.csv") and .rglob(). Paths automatically use the right separator for the operating system.
The os Module
os.environ and os.getenv("KEY", default) read environment variables (the standard place for configuration and secrets). os.getcwd(), os.chdir(), os.listdir(), os.makedirs(), os.remove(), os.rename() and os.walk() work with the file system; os.path contains older path helpers; os.cpu_count() and os.getpid() give system information.
The sys Module
sys.argv holds command-line arguments; sys.exit(code) ends the program with an exit status; sys.version and sys.version_info identify the interpreter; sys.platform the operating system; sys.path the module search path; sys.stdin, sys.stdout and sys.stderr the standard streams; sys.getsizeof() an object's memory size.
shutil
shutil.copy(), copytree(), move(), rmtree() (delete a folder and everything in it — carefully!), make_archive() for zip files, and disk_usage().
Examples
Working with paths using pathlib
from pathlib import Path
base = Path("demo_project")
(base / "data").mkdir(parents=True, exist_ok=True)
(base / "data" / "students.csv").write_text("name,marks\nAsha,91\n")
(base / "data" / "notes.txt").write_text("hello")
(base / "README.md").write_text("# Demo")
report = base / "data" / "students.csv"
print(report.name, report.stem, report.suffix, report.parent.name)
print(report.exists(), report.is_file(), (base / "data").is_dir())
print(report.read_text().splitlines())
print(sorted(p.name for p in base.rglob("*.*")))
print(sorted(p.name for p in (base / "data").glob("*.csv")))
students.csv students .csv data
True True True
['name,marks', 'Asha,91']
['README.md', 'notes.txt', 'students.csv']
['students.csv']
Environment variables and directory walking with os
import os
os.environ["APP_MODE"] = "development"
print(os.getenv("APP_MODE"), os.getenv("DB_PASSWORD", "not set"))
os.makedirs("walk_demo/a/b", exist_ok=True)
for name in ["walk_demo/top.txt", "walk_demo/a/mid.txt", "walk_demo/a/b/deep.txt"]:
with open(name, "w") as f:
f.write("x")
for folder, subfolders, files in sorted(os.walk("walk_demo")):
print(folder.replace(os.sep, "/"), sorted(subfolders), files)
print(os.path.join("data", "2026", "report.csv").replace(os.sep, "/"))
print(os.path.splitext("photo.jpeg"), os.path.basename("/tmp/a/b.txt"))
development not set
walk_demo ['a'] ['top.txt']
walk_demo/a ['b'] ['mid.txt']
walk_demo/a/b [] ['deep.txt']
data/2026/report.csv
('photo', '.jpeg') b.txt
The sys module
import sys
print(sys.version_info >= (3, 8), type(sys.argv).__name__)
print(sys.getsizeof([]) < sys.getsizeof([1, 2, 3]))
print("stdout and stderr:", sys.stdout is not None, sys.stderr is not None)
print("errors are written to", sys.stderr.name)
def main():
if len(sys.argv) > 5:
sys.exit("too many arguments")
print("argv ok")
main()
True list
True
stdout and stderr: True True
errors are written to <stderr>
argv ok
Copying, moving, archiving and deleting with shutil
import shutil
from pathlib import Path
src = Path("shutil_demo/src")
src.mkdir(parents=True, exist_ok=True)
(src / "a.txt").write_text("A")
shutil.copy(src / "a.txt", src / "a_copy.txt")
shutil.copytree(src, "shutil_demo/backup", dirs_exist_ok=True)
shutil.move("shutil_demo/src/a_copy.txt", "shutil_demo/moved.txt")
archive = shutil.make_archive("shutil_demo/backup_zip", "zip", "shutil_demo/backup")
print(sorted(p.name for p in Path("shutil_demo").iterdir()))
print(Path(archive).suffix)
shutil.rmtree("shutil_demo")
print(Path("shutil_demo").exists())
['backup', 'backup_zip.zip', 'moved.txt', 'src']
.zip
False
Common Mistakes
- Building paths with string concatenation and hard-coded "\\" or "/" separators.
- Hard-coding secrets instead of reading them from environment variables.
- Using shutil.rmtree on a path built from user input without checks.
- Assuming the current working directory is the script's folder; use Path(__file__).parent.
Key Points to Remember
- pathlib.Path: join with /, inspect name/stem/suffix/parent, read/write text, glob files.
- os: environment variables, directories, walking trees, system info.
- sys: argv, exit, version, platform, path and standard streams.
- shutil: copy, copytree, move, rmtree, make_archive.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.