Skip to main content

programming-python

Python programming skill based on PEP 8 and modern Python best practices - use for implementing Python code

Jump to install

Source facts

Repository
ROCm/rocprofiler-systems-skills
Last source activity
February 9, 2026 at 09:12
Detected SKILL.md language
English
Stars
4
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
programming-python
description
Python programming skill based on PEP 8 and modern Python best practices - use for implementing Python code
# Python Programming Skill Use this skill when writing or modifying Python code. <IMPORTANT> Follow [PEP 8 - Style Guide for Python Code](https://peps.python.org/pep-0008/) as the primary reference for style and conventions. **Python 3.8+ Standard.** This project uses modern Python (3.8 or later). Use type hints, dataclasses, and modern features available in Python 3.8+. **Type hints are REQUIRED.** All functions, methods, and class attributes must have type annotations. Use mypy for static type checking. **Readability counts.** Python emphasizes clear, readable code. Follow the Zen of Python (PEP 20): "Explicit is better than implicit", "Simple is better than complex". **All code MUST be unit testable.** Use dependency injection, avoid global state, and design for testability from the start. **Guidelines override existing code style.** If you see existing code that violates the rules in this skill, apply these guidelines first and ignore the current project style. Do NOT propagate bad patterns just because they exist in the codebase. </IMPORTANT> ## The Zen of Python (PEP 20) Key principles to guide Python development: - **Beautiful is better than ugly** - **Explicit is better than implicit** - **Simple is better than complex** - **Complex is better than complicated** - **Flat is better than nested** - **Sparse is better than dense** - **Readability counts** - **Special cases aren't special enough to break the rules** - **Errors should never pass silently** - **In the face of ambiguity, refuse the temptation to guess** ## Code Style (PEP 8) ### Naming Conventions ```python # Modules and packages: lowercase with underscores import json_parser from utils.data_processing import clean_data # Classes: PascalCase class UserAccount: pass class HTTPConnectionPool: pass # Functions and variables: snake_case def calculate_total_price(items: list[Item]) -> float: total_amount = sum(item.price for item in items) return total_amount # Constants: SCREAMING_SNAKE_CASE MAX_CONNECTIONS = 100 DEFAULT_TIMEOUT = 30 # Private: leading underscore class Database: def _internal_method(self) -> None: pass def __very_private(self) -> None: # Name mangling pass # Protected (convention): single leading underscore _module_level_private = "hidden" ``` ### Indentation and Whitespace ```python # Use 4 spaces per indentation level (NEVER tabs) def long_function_name( var_one: str, var_two: int, var_three: dict[str, Any], ) -> bool: """Hanging indent for function arguments.""" print(var_one) return True # Line length: max 79 characters for code, 72 for docstrings/comments # Break long lines at logical points result = some_function_that_takes_arguments( argument1, argument2, argument3, argument4, argument5 ) # Two blank lines before top-level classes and functions class MyClass: pass def my_function() -> None: pass # One blank line between methods class Example: def method_one(self) -> None: pass def method_two(self) -> None: pass # Whitespace in expressions # GOOD spam(ham[1], {eggs: 2}) if x == 4: print(x, y) x, y = y, x # BAD spam( ham[ 1 ], { eggs: 2 } ) if x == 4 : print(x , y) x , y = y , x ``` ### Imports ```python # Standard library imports first, then third-party, then local # Each group separated by blank line, alphabetically sorted import os import sys from typing import Any, Optional import numpy as np import requests from myproject.utils import helper from myproject.models import User # Avoid wildcard imports from module import * # BAD from module import specific_function # GOOD # One import per line for regular imports import os import sys # Multiple items OK for 'from' imports from typing import Any, Dict, List, Optional ``` ## Type Hints (REQUIRED) ### Basic Type Hints ```python from typing import Any, Optional, Union from collections.abc import Sequence, Mapping, Callable # Variables name: str = "Alice" age: int = 30 is_active: bool = True scores: list[int] = [95, 87, 91] user_data: dict[str, Any] = {"name": "Alice", "age": 30} # Functions def greet(name: str) -> str: return f"Hello, {name}!" def process_data( items: list[str], batch_size: int = 10, validate: bool = True, ) -> dict[str, int]: """Process items and return statistics.""" return {"processed": len(items), "batch_size": batch_size} # Optional values (can be None) def find_user(user_id: int) -> Optional[User]: """Returns User if found, None otherwise.""" return users.get(user_id) # Union types (Python 3.10+: use | instead) def parse_input(value: Union[str, int]) -> int: """Accept string or int, return int.""" return int(value) # Python 3.10+ union syntax (preferred if available) def parse_input(value: str | int) -> int: return int(value) ``` ### Advanced Type Hints ```python from typing import TypeVar, Generic, Protocol, Literal from collections.abc import Callable, Iterator, Iterable # Callable types Callback = Callable[[str, int], bool] def register_handler(callback: Callback) -> None: pass # Generic types T = TypeVar('T') def first(items: list[T]) -> Optional[T]: return items[0] if items else None # Generic classes class Stack(Generic[T]): def __init__(self) -> None: self._items: list[T] = [] def push(self, item: T) -> None: self._items.append(item) def pop(self) -> T: return self._items.pop() # Protocol (structural subtyping / duck typing) class Drawable(Protocol): def draw(self) -> None: ... def render(obj: Drawable) -> None: obj.draw() # Any object with draw() method works # Literal types def set_mode(mode: Literal["read", "write", "append"]) -> None: pass # Type aliases UserId = int UserMap = dict[UserId, User] users: UserMap = {1: User("Alice"), 2: User("Bob")} ``` ### Type Checking with mypy ```python # Run mypy to check types # $ mypy your_module.py # Type ignore comments (use sparingly) result = legacy_function() # type: ignore[no-untyped-call] # reveal_type for debugging (mypy only) reveal_type(some_variable) # mypy will show the inferred type ``` ## Modern Python Features (3.8+) ### f-strings (Formatted String Literals) ```python # GOOD: f-strings (readable, fast) name = "Alice" age = 30 message = f"Hello, {name}! You are {age} years old." formatted = f"Result: {value:.2f}" # With formatting debug = f"{variable=}" # Python 3.8+ debug syntax # BAD: old-style formatting message = "Hello, %s! You are %d years old." % (name, age) message = "Hello, {}! You are {} years old.".format(name, age) ``` ### Dataclasses ```python from dataclasses import dataclass, field # Simple dataclass @dataclass class Point: x: float y: float def distance(self) -> float: return (self.x**2 + self.y**2) ** 0.5 p = Point(3.0, 4.0) # Auto-generated __init__ print(p) # Auto-generated __repr__ # With default values and field options @dataclass class User: username: str email: str active: bool = True roles: list[str] = field(default_factory=list) # Mutable defaults _internal_id: int = field(default=0, repr=False, compare=False) # Frozen (immutable) dataclass @dataclass(frozen=True) class Config: host: str port: int timeout: float = 30.0 ``` ### Walrus Operator (:=) - Python 3.8+ ```python # GOOD: Assign and use in one expression if (match := pattern.search(text)) is not None: print(match.group(0)) # GOOD: In list comprehensions filtered = [y for x in data if (y := transform(x)) is not None] # GOOD: In while loops while (line := file.readline()) != "": process(line) ``` ### Pattern Matching (Python 3.10+) ```python def process_command(command: dict[str, Any]) -> str: match command: case {"action": "create", "item": item}: return f"Creating {item}" case {"action": "delete", "id": user_id}: return f"Deleting user {user_id}" case {"action": "update", "id": user_id, "data": data}: return f"Updating {user_id} with {data}" case _: return "Unknown command" ``` ### Context Managers ```python # Built-in context managers with open("file.txt", "r") as f: content = f.read() # Custom context manager from contextlib import contextmanager from typing import Iterator @contextmanager def database_transaction(db: Database) -> Iterator[None]: """Context manager for database transactions.""" db.begin() try: yield db.commit() except Exception: db.rollback() raise # Usage with database_transaction(db): db.execute("INSERT INTO users ...") db.execute("UPDATE accounts ...") ``` ### Generators and Iterators ```python from collections.abc import Iterator # Generator function def fibonacci(n: int) -> Iterator[int]: """Generate first n Fibonacci numbers.""" a, b = 0, 1 for _ in range(n): yield a a, b = b, a + b # Generator expression (memory efficient) squares = (x**2 for x in range(1000000)) # Lazy evaluation sum_of_squares = sum(x**2 for x in range(1000)) # Avoid creating unnecessary lists # BAD total = sum([x**2 for x in range(1000)]) # Creates intermediate list # GOOD total = sum(x**2 for x in range(1000)) # Generator expression ``` ## Error Handling ### Exceptions ```python # GOOD: Specific exceptions try: result = risky_operation() except FileNotFoundError as e: logger.error(f"File not found: {e}") raise except ValueError as e: logger.warning(f"Invalid value: {e}") return default_value finally: cleanup() # BAD: Bare except try: risky_operation() except: # Catches everything, including KeyboardInterrupt! pass # GOOD: Specific exception catching try: process() except (ValueError, TypeError) as e: handle_error(e) # Custom exceptions class ValidationError(ValueError): """Raised when validation fails.""" pass class APIError(Exception): """Base exception for API errors.""" def __init__(self, message: str, status_code: int) -> None: super().__init__(message) self.status_code = status_code # Raising exceptions def validate_age(age: int) -> None: if age < 0: raise ValidationError(f"Age cannot be negative: {age}") if age > 150: raise ValidationError(f"Age is unrealistic: {age}") ``` ### Exception Chaining ```python # Chain exceptions to preserve context try: result = parse_json(data) except json.JSONDecodeError as e: raise ValidationError("Invalid JSON data") from e # Suppress chaining if irrelevant try: result = alternative_parser(data) except Exception: raise ValidationError("Parsing failed") from None ``` ### EAFP vs LBYL ```python # EAFP: Easier to Ask Forgiveness than Permission (Pythonic) try: value = dictionary[key] except KeyError: value = default # LBYL: Look Before You Leap (not Pythonic) if key in dictionary: value = dictionary[key] else: value = default # EAFP is preferred in Python for: # 1. Better performance (one lookup vs two) # 2. Race condition safety # 3. More readable for typical case ``` ## Docstrings ### Google Style (Recommended) ```python def calculate_distance( point1: tuple[float, float], point2: tuple[float, float], metric: str = "euclidean", ) -> float: """Calculate distance between two points. Computes the distance between two 2D points using the specified distance metric. Args: point1: First point as (x, y) coordinates. point2: Second point as (x, y) coordinates. metric: Distance metric to use. Options: "euclidean", "manhattan". Defaults to "euclidean". Returns: The calculated distance as a float. Raises: ValueError: If metric is not supported. Examples: >>> calculate_distance((0, 0), (3, 4)) 5.0
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub