Skip to main content

Python Installing Python Properly: Practice Problems & Exercises

Practice: Installing Python Properly

10 problems3 Easy4 Medium3 Hard35–50 min
← Back to lesson

Easy

#1Check Python Version ProgrammaticallyEasy
sysversionstdlib

Write a function that returns a dictionary containing the Python version components (major, minor, micro) and the full version string. Use only the sys module.

Python
import sys

def get_python_version():
    """Return a dict with major, minor, micro version numbers and the full version string."""
    pass

info = get_python_version()
print(f"Python {info['major']}.{info['minor']}.{info['micro']}")
print(f"Full version: {info['full']}")
print(f"Is Python 3.8+: {info['major'] == 3 and info['minor'] >= 8}")
Solution
Python
import sys

def get_python_version():
    """Return a dict with major, minor, micro version numbers and the full version string."""
    return {
        "major": sys.version_info.major,
        "minor": sys.version_info.minor,
        "micro": sys.version_info.micro,
        "full": sys.version,
    }

info = get_python_version()
print(f"Python {info['major']}.{info['minor']}.{info['micro']}")
print(f"Full version: {info['full']}")
print(f"Is Python 3.8+: {info['major'] == 3 and info['minor'] >= 8}")

Why this matters:

sys.version_info is a named tuple, so you can access components by name (major, minor, micro, releaselevel, serial). This is far more reliable than parsing sys.version as a string. In production scripts, version checks like sys.version_info >= (3, 8) are the standard way to enforce minimum Python versions.

import sys

def get_python_version():
    """Return a dict with major, minor, micro version numbers and the full version string."""
    # Your code here
    pass

info = get_python_version()
print(f"Python {info['major']}.{info['minor']}.{info['micro']}")
print(f"Full version: {info['full']}")
print(f"Is Python 3.8+: {info['major'] == 3 and info['minor'] >= 8}")
Expected Output
Python 3.X.Y\nFull version: 3.X.Y (main, ...)\nIs Python 3.8+: True
Hints

Hint 1: The `sys` module has `version_info` (a named tuple with major, minor, micro) and `version` (a full string).

Hint 2: `sys.version_info.major` gives 3, `sys.version_info.minor` gives the minor version. `sys.version` gives the complete version string with build info.

#2Identify Which Python Is RunningEasy
sys.executableos.pathwhich-python

Write a function that identifies the exact Python binary that is executing the current script. Determine whether it is a symbolic link and find the real (resolved) path behind it. This is essential for debugging "which Python am I actually using?" issues.

Python
import sys
import os

def identify_python():
    """Return a dict with the executable path, whether it's a symlink, and its real path."""
    pass

result = identify_python()
print(f"Executable: {result['executable']}")
print(f"Is symlink: {result['is_symlink']}")
print(f"Real path:  {result['real_path']}")
print(f"Same file:  {result['executable'] == result['real_path']}")
Solution
Python
import sys
import os

def identify_python():
    """Return a dict with the executable path, whether it's a symlink, and its real path."""
    executable = sys.executable
    real_path = os.path.realpath(executable)
    return {
        "executable": executable,
        "is_symlink": os.path.islink(executable),
        "real_path": real_path,
    }

result = identify_python()
print(f"Executable: {result['executable']}")
print(f"Is symlink: {result['is_symlink']}")
print(f"Real path:  {result['real_path']}")
print(f"Same file:  {result['executable'] == result['real_path']}")

Why this matters:

On many systems, python3 is a symlink to a specific version like python3.11. When you install via pyenv or Homebrew, the symlink chain can be deep. sys.executable tells you the entry point, but os.path.realpath() resolves every symlink to the actual binary. This is the first thing to check when you get "module not found" errors despite having installed a package — you might be installing into one Python and running another.

import sys
import os

def identify_python():
    """Return a dict with the executable path, whether it's a symlink, and its real path."""
    # Your code here
    pass

result = identify_python()
print(f"Executable: {result['executable']}")
print(f"Is symlink: {result['is_symlink']}")
print(f"Real path:  {result['real_path']}")
print(f"Same file:  {result['executable'] == result['real_path']}")
Expected Output
Executable: /path/to/python3\nIs symlink: True|False\nReal path:  /path/to/real/python3\nSame file:  True|False
Hints

Hint 1: `sys.executable` gives the absolute path to the Python interpreter currently running. `os.path.islink()` checks if a path is a symbolic link.

Hint 2: `os.path.realpath()` resolves all symbolic links and returns the canonical path. Compare it with `sys.executable` to see if you are running through a symlink.

#3Verify Virtual Environment Is ActiveEasy
venvsys.prefixsys.base_prefix

Write a function that checks whether a virtual environment is currently active. Return a dictionary with the activation status, both prefix paths, and the venv name if one is active. Use the canonical sys.prefix vs sys.base_prefix comparison.

Python
import sys
import os

def check_venv_status():
    """Check if a virtual environment is currently active. Return a status dict."""
    pass

status = check_venv_status()
print(f"In virtual env: {status['in_venv']}")
print(f"sys.prefix:      {status['prefix']}")
print(f"sys.base_prefix: {status['base_prefix']}")
if status['venv_name']:
    print(f"Venv name:       {status['venv_name']}")
Solution
Python
import sys
import os

def check_venv_status():
    """Check if a virtual environment is currently active. Return a status dict."""
    in_venv = sys.prefix != sys.base_prefix
    venv_name = None
    if in_venv:
        venv_name = os.path.basename(sys.prefix)
    return {
        "in_venv": in_venv,
        "prefix": sys.prefix,
        "base_prefix": sys.base_prefix,
        "venv_name": venv_name,
    }

status = check_venv_status()
print(f"In virtual env: {status['in_venv']}")
print(f"sys.prefix:      {status['prefix']}")
print(f"sys.base_prefix: {status['base_prefix']}")
if status['venv_name']:
    print(f"Venv name:       {status['venv_name']}")

Why this matters:

The sys.prefix != sys.base_prefix check is the most reliable way to detect a virtual environment. When you create a venv with python -m venv myenv, Python sets sys.prefix to the venv directory but leaves sys.base_prefix pointing to the system installation. The VIRTUAL_ENV environment variable is only set by the activation script (source myenv/bin/activate), so it may be absent if you invoke the venv's Python directly (./myenv/bin/python). The prefix comparison works in both cases.

import sys
import os

def check_venv_status():
    """Check if a virtual environment is currently active. Return a status dict."""
    # Your code here
    pass

status = check_venv_status()
print(f"In virtual env: {status['in_venv']}")
print(f"sys.prefix:      {status['prefix']}")
print(f"sys.base_prefix: {status['base_prefix']}")
if status['venv_name']:
    print(f"Venv name:       {status['venv_name']}")
Expected Output
In virtual env: True|False\nsys.prefix:      /path/to/venv\nsys.base_prefix: /path/to/base/python\nVenv name:       my-venv (if active)
Hints

Hint 1: When a virtual environment is active, `sys.prefix` points to the venv directory while `sys.base_prefix` points to the system Python. They differ only inside a venv.

Hint 2: The `VIRTUAL_ENV` environment variable is set by the venv activation script. `os.path.basename()` on `sys.prefix` gives you the venv directory name.


Medium

#4Scan PATH for Multiple Python InstallationsMedium
PATHos.environshutil.whichglob

Write a function that scans every directory in PATH and finds all Python executables (matching patterns like python, python3, python3.11, etc.). For each one, report its full path, whether it is a symlink, and its resolved real path. This simulates what which -a python3 does, but with more detail.

Python
import os
import glob
import stat

def find_all_pythons():
    """Scan PATH directories for all Python executables. Return a list of dicts."""
    pass

pythons = find_all_pythons()
print(f"Found {len(pythons)} Python installation(s):\n")
for p in pythons:
    print(f"  {p['path']}")
    print(f"    Symlink: {p['is_symlink']}, Real: {p['real_path']}")
    print()
Solution
Python
import os
import glob

def find_all_pythons():
    """Scan PATH directories for all Python executables. Return a list of dicts."""
    path_dirs = os.environ.get("PATH", "").split(os.pathsep)
    patterns = ["python", "python3", "python3.*"]
    seen_real = set()
    results = []

    for directory in path_dirs:
        if not os.path.isdir(directory):
            continue
        for pattern in patterns:
            for match in glob.glob(os.path.join(directory, pattern)):
                if not os.path.isfile(match):
                    continue
                if not os.access(match, os.X_OK):
                    continue
                real = os.path.realpath(match)
                # Deduplicate by real path
                key = real
                if key in seen_real:
                    continue
                seen_real.add(key)
                results.append({
                    "path": match,
                    "is_symlink": os.path.islink(match),
                    "real_path": real,
                })
    return results

pythons = find_all_pythons()
print(f"Found {len(pythons)} Python installation(s):\n")
for p in pythons:
    print(f"  {p['path']}")
    print(f"    Symlink: {p['is_symlink']}, Real: {p['real_path']}")
    print()

Why this matters:

Having multiple Python installations is the number one source of "I installed the package but Python can't find it." This script reveals every Python on your system, ordered by PATH priority. The first match is what runs when you type python3. If pyenv, Homebrew, and system Python all coexist, their PATH ordering determines which one wins. Deduplicating by realpath prevents counting /usr/bin/python3 -> /usr/bin/python3.11 as two separate installations.

import os
import glob
import stat

def find_all_pythons():
    """Scan PATH directories for all Python executables. Return a list of dicts."""
    # Your code here
    pass

pythons = find_all_pythons()
print(f"Found {len(pythons)} Python installation(s):\n")
for p in pythons:
    print(f"  {p['path']}")
    print(f"    Symlink: {p['is_symlink']}, Real: {p['real_path']}")
    print()
Expected Output
Found N Python installation(s):\n\n  /usr/bin/python3\n    Symlink: True, Real: /usr/bin/python3.X\n  ...
Hints

Hint 1: Split `os.environ["PATH"]` on `os.pathsep` to get each directory. Then use `glob.glob()` or `os.listdir()` to find files matching patterns like `python*` in each directory.

Hint 2: Check `os.path.isfile()` and `os.access(path, os.X_OK)` to confirm the file is an executable. Use `os.path.realpath()` to resolve symlinks and avoid counting the same binary twice.

#5Requirements.txt Parser and ValidatorMedium
requirements.txtparsingregexpackaging

Write a parser for requirements.txt content that extracts package names and version specifiers. Then write a validator that detects common issues: duplicate packages, missing version pins, and invalid version specifiers.

Python
import re

SAMPLE_REQUIREMENTS = """
# Core dependencies
requests==2.31.0
flask>=2.0,<3.0
numpy
pandas==2.1.4
# Duplicate (should warn)
requests>=2.30.0
# Invalid operator
scipy~=1.11
"""

def parse_requirements(text):
    """Parse a requirements.txt string. Return list of dicts."""
    pass

def validate_requirements(parsed):
    """Check for issues. Return list of warning strings."""
    pass

parsed = parse_requirements(SAMPLE_REQUIREMENTS)
warnings = validate_requirements(parsed)

print(f"Parsed {len(parsed)} requirements\n")
for p in parsed:
    status = "valid" if p['version_spec'] else "warning: no version pinned"
    print(f"  {p['name']} {p['version_spec']}  [{status}]")

print(f"\nWarnings ({len(warnings)}):")
for w in warnings:
    print(f"  - {w}")
Solution
Python
import re

SAMPLE_REQUIREMENTS = """
# Core dependencies
requests==2.31.0
flask>=2.0,<3.0
numpy
pandas==2.1.4
# Duplicate (should warn)
requests>=2.30.0
# Invalid operator
scipy~=1.11
"""

def parse_requirements(text):
    """Parse a requirements.txt string. Return list of dicts."""
    results = []
    pattern = re.compile(r'^([A-Za-z0-9][A-Za-z0-9._-]*)\s*(.*)?$')
    valid_ops = {"==", ">=", "<=", "!=", ">", "<", "~=", "==="}

    for line in text.strip().splitlines():
        line = line.strip()
        if not line or line.startswith("#"):
            continue
        match = pattern.match(line)
        if not match:
            continue
        name = match.group(1).strip()
        version_spec = match.group(2).strip() if match.group(2) else ""

        # Check if the version operators are valid
        ops_valid = True
        if version_spec:
            specs = [s.strip() for s in version_spec.split(",")]
            for spec in specs:
                op_match = re.match(r'^(~=|===|==|!=|>=|<=|>|<)', spec)
                if not op_match:
                    ops_valid = False

        results.append({
            "name": name,
            "version_spec": version_spec,
            "ops_valid": ops_valid,
        })
    return results

def validate_requirements(parsed):
    """Check for issues. Return list of warning strings."""
    warnings = []
    seen = {}

    for req in parsed:
        name_lower = req["name"].lower()

        # Check for duplicates
        if name_lower in seen:
            seen[name_lower] += 1
        else:
            seen[name_lower] = 1

        # Check for missing version
        if not req["version_spec"]:
            warnings.append(f"{req['name']}: no version constraint (not reproducible)")

        # Check operator validity
        if req["version_spec"] and not req["ops_valid"]:
            warnings.append(f"{req['name']}: invalid version specifier '{req['version_spec']}'")

    # Report duplicates
    for name, count in seen.items():
        if count > 1:
            warnings.append(f"{name}: appears {count} times (duplicate)")

    return warnings

parsed = parse_requirements(SAMPLE_REQUIREMENTS)
warnings = validate_requirements(parsed)

print(f"Parsed {len(parsed)} requirements\n")
for p in parsed:
    status = "valid" if p['version_spec'] else "warning: no version pinned"
    print(f"  {p['name']} {p['version_spec']}  [{status}]")

print(f"\nWarnings ({len(warnings)}):")
for w in warnings:
    print(f"  - {w}")

Why this matters:

A requirements.txt without pinned versions (numpy instead of numpy==1.26.2) is the leading cause of "it works on my machine." This parser catches the three most common requirements file issues: (1) unpinned versions that make builds non-reproducible, (2) duplicate packages with conflicting version specs, and (3) invalid operators. Production-grade tools like pip-compile (from pip-tools) solve this by generating fully pinned files from abstract dependencies.

import re

def parse_requirements(text):
    """Parse a requirements.txt string. Return list of dicts with name, operator, version, valid flag."""
    # Your code here
    pass

def validate_requirements(parsed):
    """Check for issues: duplicates, missing versions, invalid operators. Return list of warnings."""
    # Your code here
    pass
Expected Output
Parsed 6 requirements\n  requests ==2.31.0  [valid]\n  flask >=2.0,<3.0  [valid]\n  numpy  [warning: no version pinned]\n  ...\nWarnings:\n  - numpy: no version constraint (not reproducible)\n  - requests: appears 2 times (duplicate)
Hints

Hint 1: A requirements line format is `package_name[extras]` followed by optional version specifiers like `==1.0`, `>=2.0,<3.0`. Comments start with `#` and blank lines should be skipped.

Hint 2: Use a regex like `^([a-zA-Z0-9_-]+)\s*(.*)$` to separate package name from version spec. Track seen names in a set to catch duplicates.

#6Virtual Environment Health CheckerMedium
venvpipsite-packageshealth-check

Build a health checker that examines the current Python environment and reports on: Python version, executable path, whether a venv is active, pip availability, site-packages location, number of installed packages, and whether site-packages is writable. Output a formatted report with OK/WARN status for each check.

Python
import sys
import os
import sysconfig
import subprocess

def check_venv_health():
    """Run health checks on the current Python environment. Return a report dict."""
    pass

report = check_venv_health()
print("=== Environment Health Report ===")
for key, value in report.items():
    label = key.replace("_", " ").ljust(20)
    status = value.get("status", "")
    detail = value.get("detail", "")
    print(f"{label}{detail}  [{status}]")
Solution
Python
import sys
import os
import sysconfig
import subprocess

def check_venv_health():
    """Run health checks on the current Python environment. Return a report dict."""
    report = {}

    # 1. Python version
    ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
    report["python_version"] = {
        "detail": ver,
        "status": "OK" if sys.version_info >= (3, 8) else "WARN",
    }

    # 2. Executable path
    exe = sys.executable
    report["executable"] = {
        "detail": exe,
        "status": "OK" if os.path.exists(exe) else "FAIL",
    }

    # 3. Virtual environment status
    in_venv = sys.prefix != sys.base_prefix
    report["venv_active"] = {
        "detail": str(in_venv),
        "status": "OK" if in_venv else "WARN",
    }

    # 4. pip availability
    try:
        result = subprocess.run(
            [sys.executable, "-m", "pip", "--version"],
            capture_output=True, text=True, timeout=10
        )
        pip_ok = result.returncode == 0
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pip_ok = False
    report["pip_available"] = {
        "detail": str(pip_ok),
        "status": "OK" if pip_ok else "FAIL",
    }

    # 5. site-packages location
    paths = sysconfig.get_paths()
    site_packages = paths.get("purelib", "unknown")
    report["site_packages"] = {
        "detail": site_packages,
        "status": "OK" if os.path.isdir(site_packages) else "FAIL",
    }

    # 6. Package count
    pkg_count = 0
    if os.path.isdir(site_packages):
        pkg_count = len([
            d for d in os.listdir(site_packages)
            if d.endswith(".dist-info")
        ])
    report["package_count"] = {
        "detail": f"{pkg_count} installed",
        "status": "OK" if pkg_count > 0 else "WARN",
    }

    # 7. Writable
    writable = os.access(site_packages, os.W_OK) if os.path.isdir(site_packages) else False
    report["writable"] = {
        "detail": str(writable),
        "status": "OK" if writable else "WARN",
    }

    return report

report = check_venv_health()
print("=== Environment Health Report ===")
for key, value in report.items():
    label = key.replace("_", " ").ljust(20)
    status = value.get("status", "")
    detail = value.get("detail", "")
    print(f"{label}{detail}  [{status}]")

Why this matters:

When something goes wrong with package installations, you need a systematic way to diagnose the environment. This health checker covers the most common failure modes: wrong Python version, pip not installed (common in minimal Docker images), site-packages not writable (permission issues), and no venv active (risking global pollution). The .dist-info directory count is how pip itself tracks installed packages.

import sys
import os
import sysconfig
import subprocess

def check_venv_health():
    """Run health checks on the current Python environment. Return a report dict."""
    # Your code here
    pass
Expected Output
=== Environment Health Report ===\nPython version:     3.X.Y  [OK]\nExecutable:         /path/to/python  [OK]\nVenv active:        True|False  [OK|WARN]\npip available:      True  [OK]\nsite-packages:      /path/to/site-packages  [OK]\nPackage count:      N installed  [OK]\nWritable:           True  [OK]
Hints

Hint 1: Use `sysconfig.get_paths()` to find the `purelib` path (where site-packages live). Check if it exists with `os.path.isdir()` and if it is writable with `os.access(path, os.W_OK)`.

Hint 2: To check if pip is available without importing it, use `subprocess.run([sys.executable, "-m", "pip", "--version"])` and check the return code. Count packages with `os.listdir()` on site-packages, filtering for `.dist-info` directories.

#7Detect Common Installation IssuesMedium
diagnosticsPATHvenvtroubleshooting

Write a diagnostic tool that scans for the most common Python installation issues. Check for: Python version below 3.8, no virtual environment active, pip not available, python command missing (only python3 exists), and multiple conflicting Python installations on PATH. Report each check as PASS, WARN, or FAIL.

Python
import sys
import os
import shutil
import subprocess

def diagnose_installation():
    """Detect common Python installation problems. Return a list of issue dicts."""
    pass

issues = diagnose_installation()
print("=== Installation Diagnostic ===\n")
for issue in issues:
    icon = {"PASS": "PASS", "WARN": "WARN", "FAIL": "FAIL"}[issue["level"]]
    print(f"[{icon}] {issue['message']}")
    if issue.get("detail"):
        print(f"       {issue['detail']}")
Solution
Python
import sys
import os
import shutil
import subprocess
import glob

def diagnose_installation():
    """Detect common Python installation problems. Return a list of issue dicts."""
    issues = []

    # 1. Python version check
    if sys.version_info >= (3, 8):
        issues.append({
            "level": "PASS",
            "message": f"Python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} detected",
        })
    else:
        issues.append({
            "level": "FAIL",
            "message": f"Python {sys.version_info.major}.{sys.version_info.minor} is below minimum 3.8",
            "detail": "Upgrade Python. Many modern packages require 3.8+.",
        })

    # 2. Virtual environment check
    in_venv = sys.prefix != sys.base_prefix
    if in_venv:
        issues.append({
            "level": "PASS",
            "message": "Virtual environment is active",
            "detail": f"venv: {os.path.basename(sys.prefix)}",
        })
    else:
        issues.append({
            "level": "WARN",
            "message": "No virtual environment active",
            "detail": "Run: python -m venv .venv && source .venv/bin/activate",
        })

    # 3. pip availability
    try:
        result = subprocess.run(
            [sys.executable, "-m", "pip", "--version"],
            capture_output=True, text=True, timeout=10
        )
        if result.returncode == 0:
            pip_ver = result.stdout.strip().split("\n")[0]
            issues.append({"level": "PASS", "message": "pip is available", "detail": pip_ver})
        else:
            issues.append({
                "level": "FAIL",
                "message": "pip is not working",
                "detail": "Run: python -m ensurepip --upgrade",
            })
    except Exception:
        issues.append({
            "level": "FAIL",
            "message": "pip is not available",
            "detail": "Run: python -m ensurepip --upgrade",
        })

    # 4. python vs python3 command
    python_cmd = shutil.which("python")
    python3_cmd = shutil.which("python3")
    if python_cmd and python3_cmd:
        # Check if they point to the same binary
        real_py = os.path.realpath(python_cmd) if python_cmd else None
        real_py3 = os.path.realpath(python3_cmd) if python3_cmd else None
        if real_py == real_py3:
            issues.append({"level": "PASS", "message": "'python' and 'python3' both resolve to the same binary"})
        else:
            issues.append({
                "level": "WARN",
                "message": "'python' and 'python3' resolve to different binaries",
                "detail": f"python -> {real_py}, python3 -> {real_py3}",
            })
    elif python3_cmd and not python_cmd:
        issues.append({
            "level": "WARN",
            "message": "'python' command not found (only 'python3' exists)",
            "detail": "Some tools expect 'python'. Consider a symlink or alias.",
        })
    elif not python3_cmd:
        issues.append({
            "level": "FAIL",
            "message": "'python3' command not found on PATH",
        })

    # 5. Multiple Python versions on PATH
    path_dirs = os.environ.get("PATH", "").split(os.pathsep)
    python_binaries = set()
    for d in path_dirs:
        if not os.path.isdir(d):
            continue
        for entry in glob.glob(os.path.join(d, "python3.*")):
            if os.path.isfile(entry) and os.access(entry, os.X_OK):
                # Extract just the version part
                basename = os.path.basename(entry)
                if not basename.endswith(".py"):
                    python_binaries.add(entry)

    if len(python_binaries) > 1:
        issues.append({
            "level": "WARN",
            "message": f"Multiple Python 3.x versions found on PATH ({len(python_binaries)})",
            "detail": ", ".join(sorted(python_binaries)),
        })
    else:
        issues.append({"level": "PASS", "message": "No conflicting Python versions on PATH"})

    return issues

issues = diagnose_installation()
print("=== Installation Diagnostic ===\n")
for issue in issues:
    icon = {"PASS": "PASS", "WARN": "WARN", "FAIL": "FAIL"}[issue["level"]]
    print(f"[{icon}] {issue['message']}")
    if issue.get("detail"):
        print(f"       {issue['detail']}")

Why this matters:

This is the script you wish you had the first time you spent an hour debugging "ModuleNotFoundError" on a fresh machine. The five checks cover the most frequent causes: (1) ancient Python version that is missing modern features, (2) installing packages globally instead of in a venv, (3) pip not available in minimal Linux installs or Docker images, (4) python not existing as a command (common on Ubuntu), and (5) PATH ordering causing the wrong Python to run. Save a version of this script and run it on every new machine.

import sys
import os
import shutil

def diagnose_installation():
    """Detect common Python installation problems. Return a list of issue dicts."""
    # Your code here
    pass
Expected Output
=== Installation Diagnostic ===\n[PASS] Python 3.8+ detected\n[WARN] No virtual environment active\n[PASS] pip is available\n[WARN] Multiple python3 found in PATH\n...
Hints

Hint 1: Check for these common issues: Python version too old (below 3.8), no venv active, pip missing, `python` command not found (only `python3` exists), and multiple Python versions on PATH.

Hint 2: Use `shutil.which()` to check if `python` and `python3` resolve to executables. Scan PATH directories for multiple python3.* binaries to detect version conflicts.


Hard

#8Build an Environment Fingerprint ToolHard
platformsysconfigpkg_resourcesfingerprint

Build a comprehensive environment fingerprint tool that captures everything needed to reproduce a Python environment: Python version and implementation, platform details, all configured paths, installed packages with versions, and a SHA-256 hash of the entire fingerprint for quick comparison. Output should be valid JSON.

Python
import sys
import os
import platform
import sysconfig
import hashlib
import json

def environment_fingerprint():
    """Generate a complete fingerprint of the current Python environment."""
    pass

fp = environment_fingerprint()
print(json.dumps(fp, indent=2))
print(f"\nFingerprint hash: {fp['hash']}")
Solution
Python
import sys
import os
import platform
import sysconfig
import hashlib
import json

def get_installed_packages():
    """Scan site-packages for installed packages by reading .dist-info metadata."""
    packages = []
    paths = sysconfig.get_paths()
    site_packages = paths.get("purelib", "")

    if not os.path.isdir(site_packages):
        return packages

    for entry in sorted(os.listdir(site_packages)):
        if entry.endswith(".dist-info"):
            # Format: package_name-version.dist-info
            parts = entry[:-len(".dist-info")]
            # The last hyphen-separated segment with digits is the version
            segments = parts.split("-")
            if len(segments) >= 2:
                name = "-".join(segments[:-1])
                version = segments[-1]
                packages.append({"name": name, "version": version})
            else:
                packages.append({"name": parts, "version": "unknown"})

    return packages

def environment_fingerprint():
    """Generate a complete fingerprint of the current Python environment."""
    # Python info
    python_info = {
        "version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
        "full_version": sys.version,
        "implementation": platform.python_implementation(),
        "compiler": platform.python_compiler(),
        "executable": sys.executable,
        "prefix": sys.prefix,
        "base_prefix": sys.base_prefix,
        "in_venv": sys.prefix != sys.base_prefix,
    }

    # Platform info
    platform_info = {
        "system": platform.system(),
        "release": platform.release(),
        "machine": platform.machine(),
        "processor": platform.processor(),
        "platform": platform.platform(),
    }

    # Path configuration
    all_paths = sysconfig.get_paths()
    path_info = {
        "prefix": all_paths.get("prefix", ""),
        "stdlib": all_paths.get("stdlib", ""),
        "site_packages": all_paths.get("purelib", ""),
        "include": all_paths.get("include", ""),
        "scripts": all_paths.get("scripts", ""),
        "data": all_paths.get("data", ""),
    }

    # Installed packages
    packages = get_installed_packages()

    # Build the fingerprint (without hash)
    fingerprint = {
        "python": python_info,
        "platform": platform_info,
        "paths": path_info,
        "packages": packages,
        "package_count": len(packages),
    }

    # Generate reproducible hash
    canonical = json.dumps(fingerprint, sort_keys=True, separators=(",", ":"))
    fp_hash = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
    fingerprint["hash"] = f"sha256:{fp_hash[:16]}"

    return fingerprint

fp = environment_fingerprint()
print(json.dumps(fp, indent=2))
print(f"\nFingerprint hash: {fp['hash']}")

Why this matters:

Environment fingerprints are how teams debug "works on my machine" problems. When a CI build fails but your local run passes, comparing fingerprints immediately reveals the difference: a different Python micro version, a missing package, a different platform. The SHA-256 hash lets you embed a single string in logs or Docker labels: if two hashes match, the environments are identical. This pattern is used in production by tools like pip freeze and conda env export, but this version captures platform details they miss.

import sys
import os
import platform
import sysconfig
import hashlib
import json

def environment_fingerprint():
    """Generate a complete fingerprint of the current Python environment.
    Returns a dict that can be serialized to JSON for reproducibility tracking.
    """
    # Your code here
    pass
Expected Output
{\n  "python": { "version": "3.X.Y", "implementation": "CPython", ... },\n  "platform": { "system": "Linux", "machine": "x86_64", ... },\n  "paths": { "prefix": "...", "site_packages": "...", ... },\n  "packages": [ { "name": "pip", "version": "..." }, ... ],\n  "hash": "sha256:abc123..."\n}
Hints

Hint 1: Use `platform` module for OS info (`platform.system()`, `platform.machine()`, `platform.python_implementation()`). Use `sysconfig.get_paths()` for all Python paths. For packages, scan the site-packages directory for `.dist-info` folders.

Hint 2: To generate a reproducible hash, serialize the sorted fingerprint dict to a JSON string and hash it with `hashlib.sha256()`. This lets you compare environments by a single hash value.

#9Dependency Conflict DetectorHard
dependenciespackagingconflict-detectionversion-parsing

Build a dependency conflict detector. Given multiple sets of requirements (simulating different projects or dependency trees), parse version specifiers, and determine whether they are mutually satisfiable. Report which packages have conflicts and why.

Python
import re

# Simulated dependency requirements from two projects
PROJECT_A = {
    "numpy": ">=1.24,<1.26",
    "requests": ">=2.28",
    "pandas": ">=2.0,<2.2",
    "flask": "==2.3.2",
}

PROJECT_B = {
    "numpy": ">=1.26",
    "requests": ">=2.25,<3.0",
    "pandas": ">=1.5,<2.1",
    "flask": ">=2.0,<3.0",
}

def parse_version(version_str):
    """Parse a version string into a comparable tuple of ints."""
    pass

def check_constraints_compatible(specs_list):
    """Check if a list of version spec strings can be simultaneously satisfied.
    Return (is_compatible, explanation)."""
    pass

def detect_conflicts(project_deps):
    """Given a dict of project_name -> {package: spec}, detect conflicts."""
    pass

conflicts = detect_conflicts({"project-a": PROJECT_A, "project-b": PROJECT_B})

print("=== Dependency Conflict Report ===\n")
for pkg, result in sorted(conflicts.items()):
    if result["conflict"]:
        print(f"CONFLICT: {pkg}")
    else:
        print(f"OK:       {pkg}")
    for detail in result["details"]:
        print(f"          {detail}")
    print()
Solution
Python
import re

PROJECT_A = {
    "numpy": ">=1.24,<1.26",
    "requests": ">=2.28",
    "pandas": ">=2.0,<2.2",
    "flask": "==2.3.2",
}

PROJECT_B = {
    "numpy": ">=1.26",
    "requests": ">=2.25,<3.0",
    "pandas": ">=1.5,<2.1",
    "flask": ">=2.0,<3.0",
}

def parse_version(version_str):
    """Parse a version string into a comparable tuple of ints."""
    parts = version_str.strip().split(".")
    result = []
    for p in parts:
        # Extract digits only
        digits = re.match(r'(\d+)', p)
        result.append(int(digits.group(1)) if digits else 0)
    # Pad to at least 3 components
    while len(result) < 3:
        result.append(0)
    return tuple(result)

def parse_spec(spec_str):
    """Parse a version spec string like '>=1.24,<1.26' into a list of (operator, version_tuple)."""
    constraints = []
    parts = [s.strip() for s in spec_str.split(",")]
    pattern = re.compile(r'^(===|==|~=|!=|>=|<=|>|<)(.+)$')
    for part in parts:
        match = pattern.match(part)
        if match:
            op = match.group(1)
            ver = parse_version(match.group(2))
            constraints.append((op, ver))
    return constraints

def find_bounds(all_constraints):
    """Given a flat list of (op, ver) constraints, compute the effective bounds.
    Returns (lower_bound, lower_inclusive, upper_bound, upper_inclusive, eq_value)."""
    lower = (0, 0, 0)
    lower_inclusive = True
    upper = (999, 999, 999)
    upper_inclusive = True
    eq_values = []

    for op, ver in all_constraints:
        if op == "==":
            eq_values.append(ver)
        elif op == ">=":
            if ver > lower or (ver == lower and not lower_inclusive):
                lower = ver
                lower_inclusive = True
        elif op == ">":
            if ver > lower or (ver == lower):
                lower = ver
                lower_inclusive = False
        elif op == "<=":
            if ver < upper or (ver == upper and not upper_inclusive):
                upper = ver
                upper_inclusive = True
        elif op == "<":
            if ver < upper or (ver == upper):
                upper = ver
                upper_inclusive = False
        elif op == "!=":
            pass  # Exclude checks are harder; skip for this exercise
        elif op == "~=":
            # ~=1.11 means >=1.11,<2.0 (compatible release)
            if ver > lower:
                lower = ver
                lower_inclusive = True
            compat_upper = (ver[0] + 1, 0, 0) if len(ver) <= 2 else (ver[0], ver[1] + 1, 0)
            if compat_upper < upper:
                upper = compat_upper
                upper_inclusive = False

    return lower, lower_inclusive, upper, upper_inclusive, eq_values

def check_constraints_compatible(all_specs):
    """Check if multiple spec strings can be simultaneously satisfied."""
    all_constraints = []
    for spec in all_specs:
        all_constraints.extend(parse_spec(spec))

    lower, lower_incl, upper, upper_incl, eq_values = find_bounds(all_constraints)

    # If there are equality pins, check they satisfy all bounds
    if eq_values:
        for eq in eq_values:
            satisfies_lower = eq > lower or (eq == lower and lower_incl)
            satisfies_upper = eq < upper or (eq == upper and upper_incl)
            if not (satisfies_lower and satisfies_upper):
                return False, f"pinned {eq} outside range [{lower}, {upper}]"
        # Check all eq values are the same
        if len(set(eq_values)) > 1:
            return False, f"conflicting pins: {eq_values}"
        return True, f"pinned at {eq_values[0]}, within bounds"

    # Check if range is non-empty
    if lower > upper:
        return False, f"lower bound {lower} > upper bound {upper}"
    if lower == upper and not (lower_incl and upper_incl):
        return False, f"empty range at {lower} (exclusive bound)"

    return True, f"satisfiable range: [{'>=' if lower_incl else '>'}{lower}, {'<=' if upper_incl else '<'}{upper}]"

def detect_conflicts(project_deps):
    """Given a dict of project_name -> {package: spec}, detect conflicts."""
    # Collect all packages across all projects
    all_packages = set()
    for proj_reqs in project_deps.values():
        all_packages.update(proj_reqs.keys())

    results = {}
    for pkg in sorted(all_packages):
        specs = []
        details = []
        for proj_name, proj_reqs in project_deps.items():
            if pkg in proj_reqs:
                spec = proj_reqs[pkg]
                specs.append(spec)
                details.append(f"{proj_name} requires: {pkg}{spec}")

        if len(specs) <= 1:
            results[pkg] = {"conflict": False, "details": details + ["Only one source, no conflict possible."]}
            continue

        is_ok, explanation = check_constraints_compatible(specs)
        details.append(f"{'Compatible' if is_ok else 'INCOMPATIBLE'}: {explanation}")
        results[pkg] = {"conflict": not is_ok, "details": details}

    return results

conflicts = detect_conflicts({"project-a": PROJECT_A, "project-b": PROJECT_B})

print("=== Dependency Conflict Report ===\n")
for pkg, result in sorted(conflicts.items()):
    if result["conflict"]:
        print(f"CONFLICT: {pkg}")
    else:
        print(f"OK:       {pkg}")
    for detail in result["details"]:
        print(f"          {detail}")
    print()

Why this matters:

Dependency conflicts (also called "dependency hell") are the bane of Python packaging. When project A needs numpy>=1.24,<1.26 and project B needs numpy>=1.26, there is no version of numpy that satisfies both. Tools like pip attempt to resolve this at install time but can fail silently or pick a version that breaks one project. Understanding how version constraints form intervals and how to check interval overlap is foundational to understanding what pip, poetry, and conda do under the hood. The key insight: constraints define a range, and compatibility means the intersection of all ranges is non-empty.

import re
import os
import sysconfig

def parse_version(version_str):
    """Parse a version string into a comparable tuple."""
    # Your code here
    pass

def check_requirement(installed_version, spec_str):
    """Check if an installed version satisfies a requirement spec like '>=1.0,<2.0'."""
    # Your code here
    pass

def detect_conflicts(requirements_sets):
    """Given multiple sets of requirements, detect version conflicts."""
    # Your code here
    pass
Expected Output
Checking dependency sets for conflicts...\n\nConflict detected for 'numpy':\n  project-a requires: numpy>=1.24,<1.26\n  project-b requires: numpy>=1.26\n  These constraints cannot be satisfied simultaneously.\n\nNo conflict for 'requests':\n  project-a requires: requests>=2.28\n  project-b requires: requests>=2.25,<3.0\n  Compatible range exists.
Hints

Hint 1: Parse version strings by splitting on `.` and converting to integers: `(1, 24, 0)`. Compare tuples directly since Python compares tuples element-by-element. Handle missing micro versions by defaulting to 0.

Hint 2: For each package, collect all constraints from all requirement sets. Then check if there exists any version that satisfies ALL constraints simultaneously. If the maximum lower bound exceeds the minimum upper bound, there is a conflict.

#10Cross-Platform Python Path ResolverHard
cross-platformsysconfigpathlibos.path

Build a cross-platform Python path resolver that maps out where Python stores its standard library, site-packages, scripts, and headers on the current platform. Then implement a module finder that locates where any given module would be loaded from (file path, built-in, or frozen). Handle the differences between POSIX and Windows path schemes.

Python
import sys
import os
import sysconfig
import platform
import importlib.util
from pathlib import Path

def resolve_python_paths():
    """Build a complete map of Python paths on the current platform."""
    pass

def find_module_location(module_name):
    """Find where Python would load a module from. Return a description string."""
    pass

# Part 1: Path map
path_map = resolve_python_paths()
print(f"=== Python Path Map ({platform.system()}) ===\n")
for key, value in path_map.items():
    label = key.ljust(18)
    print(f"{label}{value}")

# Part 2: Module lookup
print("\n=== Module Lookup ===\n")
test_modules = ["os", "json", "sys", "collections", "importlib", "pathlib"]
for mod in test_modules:
    location = find_module_location(mod)
    print(f"{mod.ljust(16)}{location}")
Solution
Python
import sys
import os
import sysconfig
import platform
import importlib.util
from pathlib import Path

def resolve_python_paths():
    """Build a complete map of Python paths on the current platform."""
    scheme = sysconfig.get_default_scheme()
    paths = sysconfig.get_paths()
    is_venv = sys.prefix != sys.base_prefix

    result = {
        "scheme": scheme,
        "platform": platform.system(),
        "prefix": sys.prefix,
        "base_prefix": sys.base_prefix,
        "in_venv": str(is_venv),
        "stdlib": paths.get("stdlib", "N/A"),
        "site_packages": paths.get("purelib", "N/A"),
        "platlib": paths.get("platlib", "N/A"),
        "scripts": paths.get("scripts", "N/A"),
        "include": paths.get("include", "N/A"),
        "data": paths.get("data", "N/A"),
    }

    # Add sys.path for full search order
    result["sys.path_count"] = str(len(sys.path))

    # Platform-specific notes
    system = platform.system()
    if system == "Windows":
        result["note"] = "Windows uses 'nt' scheme; Scripts/ instead of bin/"
    elif system == "Darwin":
        result["note"] = "macOS may have Framework builds with different paths"
    else:
        result["note"] = "Linux uses posix_prefix; check for dist-packages on Debian/Ubuntu"

    # Check for Debian/Ubuntu dist-packages vs site-packages
    if system == "Linux":
        dist_packages = os.path.join(sys.prefix, "lib",
            f"python{sys.version_info.major}.{sys.version_info.minor}",
            "dist-packages")
        result["dist_packages_exists"] = str(os.path.isdir(dist_packages))

    return result

def find_module_location(module_name):
    """Find where Python would load a module from. Return a description string."""
    # Check if it's a built-in module
    if module_name in sys.builtin_module_names:
        return "built-in (compiled into interpreter)"

    # Try to find the module spec
    try:
        spec = importlib.util.find_spec(module_name)
    except (ModuleNotFoundError, ValueError):
        return "NOT FOUND"

    if spec is None:
        return "NOT FOUND"

    # Check if it's a frozen module
    if spec.origin == "frozen":
        return "frozen (embedded in interpreter)"

    # Check if it's a namespace package (no origin)
    if spec.origin is None:
        if spec.submodule_search_locations:
            locations = list(spec.submodule_search_locations)
            return f"namespace package: {locations[0]}"
        return "namespace package (no fixed location)"

    # Regular file-based module
    origin = spec.origin
    # Determine if it's stdlib or third-party
    stdlib_path = sysconfig.get_paths()["stdlib"]
    site_path = sysconfig.get_paths()["purelib"]

    if origin.startswith(stdlib_path):
        category = "stdlib"
    elif origin.startswith(site_path):
        category = "third-party"
    else:
        category = "other"

    # Check if it's a package (directory) or single file
    is_package = spec.submodule_search_locations is not None
    kind = "package" if is_package else "module"

    return f"{origin}  [{category} {kind}]"

# Part 1: Path map
path_map = resolve_python_paths()
print(f"=== Python Path Map ({platform.system()}) ===\n")
for key, value in path_map.items():
    label = key.ljust(18)
    print(f"{label}  {value}")

# Part 2: Module lookup
print("\n=== Module Lookup ===\n")
test_modules = ["os", "json", "sys", "collections", "importlib", "pathlib"]
for mod in test_modules:
    location = find_module_location(mod)
    print(f"{mod.ljust(16)}{location}")

# Bonus: Show full sys.path search order
print("\n=== sys.path Search Order ===\n")
for i, p in enumerate(sys.path):
    exists = "exists" if os.path.exists(p) else "MISSING"
    print(f"  [{i}] {p}  ({exists})")

Why this matters:

Python's import system searches sys.path in order, and the exact paths differ dramatically across platforms. Windows uses Scripts\ where POSIX uses bin/. Debian/Ubuntu add a dist-packages directory alongside site-packages. macOS Framework builds store everything under /Library/Frameworks. Virtual environments override sys.prefix to redirect imports to an isolated directory.

Understanding this path resolution is critical for debugging imports. When import numpy fails, the answer is always in this chain: (1) which Python is running, (2) what is its sys.path, (3) where are packages actually installed. The importlib.util.find_spec() call is exactly what Python's import machinery uses internally — it is the authoritative answer to "where will this module come from?"

import sys
import os
import sysconfig
import platform
from pathlib import Path

def resolve_python_paths():
    """Build a complete map of Python paths on the current platform.
    Handle differences between Windows, macOS, and Linux.
    Return a structured dict.
    """
    # Your code here
    pass

def find_python_file(module_name):
    """Given a module name, find where Python would load it from.
    Return the file path or None.
    """
    # Your code here
    pass
Expected Output
=== Python Path Map (macOS/Linux/Windows) ===\nScheme:         posix_prefix | nt\nPrefix:         /usr/local | C:\\Python311\nStdlib:         /usr/local/lib/python3.11 | C:\\Python311\\Lib\nSite-packages:  ...lib/python3.11/site-packages | ...\\Lib\\site-packages\nScripts/bin:    /usr/local/bin | C:\\Python311\\Scripts\nHeaders:        .../include/python3.11 | ...\\Include\n\n=== Module Lookup ===\nos:       /usr/local/lib/python3.11/os.py\njson:     /usr/local/lib/python3.11/json/__init__.py\nsys:      built-in
Hints

Hint 1: Use `sysconfig.get_default_scheme()` to detect the platform scheme (e.g., `posix_prefix` on Linux/macOS, `nt` on Windows). `sysconfig.get_paths()` returns all paths for the current scheme.

Hint 2: To find where a module lives, use `importlib.util.find_spec(module_name)`. The returned spec object has an `origin` attribute with the file path, or it is None for built-in modules.

© 2026 EngineersOfAI. All rights reserved.