Table of Contents

Naming

Variable and Function Names

Variable and Function Names: Use snake_case (all lowercase letters, with underscores separating words).

# Wrong
UserName = "Zepa"
TotalCount = 100

def ProcessData(data):
    return [item.strip() for item in data]

# Right
user_name = "zepa"
total_count = 100

def process_data(data):
    return [item.strip() for item in data]

Class Names: Use CamelCase.

# Wrong
class company_info:
    def __init__(self, name, city):
        self.name = name
        self.city = city

# Right
class CompanyInfo:
    def __init__(self, name, city):
        self.name = name
        self.city = city

Constant Names

Use all uppercase letters with words separated by underscores.

# Wrong
maxCount = 5
apiKey = "pywndqod1"

# Right
MAX_COUNT = 5
API_KEY = "pywndqod1"

Temporary Variables

Use short, meaningful names.

for i in range(10):
    print(i)

temp = company_name
idx = 0

Python Style Rules

Indentation

Use 4 spaces for indentation. Do not use tabs.

# Wrong
def example_function():
    if True:
        print("Success")

# Right
def example_function():
    if True:
        print("Success")

Line Length

Limit all lines to 80 characters.

# Wrong
def long_function_name_with_many_parameters(param1, param2, param3, param4, param5, param6):
    return param1 + param2 + param3 + param4 + param5 + param6

# Right
def long_function_name_with_many_parameters(param1, param2, param3, param4):
    return param1 + param2 + param3 + param4

Whitespace

Surround binary operators (e.g., +, -, *, etc.) with a single space on both sides.

total_price = price + tax
a = b * (c + d)
discount = current - temp

Do not put spaces immediately inside parentheses in function definitions or slices.

# Wrong
def my_function( x ):
    return x[ 0 : 3 ]

# Right
def my_function(x):
    return x[0:3]

Put a space after for, if, while, and other control flow statements.

# Wrong
if(request_success):
    print("SUCCESS:" + status_code)

# Right
if request_success:
    print("SUCCESS:" + status_code)

3. Python Formatting Rules

Comments

Inline Comments: Use inline comments to explain complex or non-obvious code. Place comments above the code with two spaces before the #.

# Wrong
if request_success:  # This is a bad comment style
    print("SUCCESS:" + status_code)

# Right
# Success Request Check
if request_success:
    print("SUCCESS:" + status_code)

Docstrings: Every function and class should have a docstring to describe what it does, its parameters, and its return values.

# Wrong
def add_numbers(a, b):
    return a + b  # Missing docstring

# Right
def add_numbers(a, b):
    """
    Adds two numbers together.

    Args:
        a (int or float): The first number.
        b (int or float): The second number.

    Returns:
        int or float: The sum of the two numbers.
    """
    return a + b

Type Hints

Type hints should be used for function parameters and return values.

def add(a: int, b: int) -> int:
    return a + b

Use Optional and Union when appropriate to define flexible types.

from typing import Optional, Union, List, Dict, Any

def get_value(x: Optional[str] = None) -> Union[int, str]:
    if x:
        return int(x)
    return "No value"

# Modern Python 3.10+ syntax
def process_data(items: list[str]) -> dict[str, int]:
    return {item: len(item) for item in items}

# Generic types
from typing import TypeVar, Generic

T = TypeVar('T')

class Container(Generic[T]):
    def __init__(self, value: T) -> None:
        self.value = value

    def get(self) -> T:
        return self.value

Imports

Import standard libraries first, followed by third-party libraries, and then local imports. Each group of imports should be separated by a blank line.

import os
import sys

import requests

from mymodule import my_function

Patterns

Exception Handling

Handle exceptions by specifying specific exceptions and minimizing the use of try...except blocks.

# Wrong
try:
    # ...
except:
    print("Something went wrong")  # Too generic, not recommended

# Right
try:
    # ...
except ValueError as e:
    print(f"ValueError occurred: {e}")
except FileNotFoundError as e:
    print(f"File not found: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

Function and Class Definitions

Functions should perform one clear task. A function should be as small and simple as possible.

def calculate_area(radius: float) -> float:
    return 3.14159 * radius * radius

Classes should have a single responsibility and contain related methods.

class Rectangle:
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

Modern Python Features

Data Classes

Use data classes for simple data containers.

from dataclasses import dataclass, field
from typing import List, Optional

@dataclass
class User:
    name: str
    email: str
    age: int
    tags: List[str] = field(default_factory=list)
    is_active: bool = True

@dataclass(frozen=True)
class Point:
    x: float
    y: float

    def distance_from_origin(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5

# Usage
user = User("John Doe", "john@example.com", 30, ["developer", "python"])
point = Point(3.0, 4.0)
print(point.distance_from_origin())  # 5.0

Enums

Use enums for representing fixed sets of values.

from enum import Enum, auto, IntEnum

class Status(Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"

class Priority(IntEnum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3
    CRITICAL = 4

class Color(Enum):
    RED = auto()
    GREEN = auto()
    BLUE = auto()

# Usage
status = Status.PENDING
if status == Status.APPROVED:
    print("Request approved")

# Enum comparison
if Priority.HIGH > Priority.MEDIUM:
    print("High priority is greater than medium")

Pathlib

Use pathlib for modern file path handling.

from pathlib import Path

# Create paths
config_file = Path("config") / "settings.json"
backup_dir = Path.home() / "backups"

# Path operations
if config_file.exists():
    content = config_file.read_text()
    config_file.write_text(content.upper())

# Iterate over files
for py_file in Path(".").glob("*.py"):
    print(f"Python file: {py_file}")

# Create directories
backup_dir.mkdir(parents=True, exist_ok=True)

Context Managers

Use context managers for resource management.

from contextlib import contextmanager, suppress
import tempfile
import os

# Built-in context managers
with open("file.txt", "r") as f:
    content = f.read()

# Custom context manager
@contextmanager
def temporary_file():
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    try:
        yield temp_file
    finally:
        temp_file.close()
        os.unlink(temp_file.name)

# Usage
with temporary_file() as temp:
    temp.write(b"Hello, World!")

# Suppress exceptions
with suppress(FileNotFoundError):
    os.remove("nonexistent_file.txt")

Walrus Operator

Use the walrus operator (:=) for assignment expressions.

# Traditional approach
data = get_data()
if data:
    process_data(data)

# With walrus operator
if data := get_data():
    process_data(data)

# In loops
while (line := file.readline()) != "":
    process_line(line)

# In list comprehensions
results = [y for x in data if (y := process(x)) is not None]

Pattern Matching (Python 3.10+)

Use pattern matching for complex conditional logic.

def handle_response(response):
    match response:
        case {"status": "success", "data": data}:
            return f"Success: {data}"
        case {"status": "error", "message": msg}:
            return f"Error: {msg}"
        case {"status": "pending"}:
            return "Request is pending"
        case _:
            return "Unknown response format"

# Pattern matching with types
def process_value(value):
    match value:
        case int(n) if n > 0:
            return f"Positive integer: {n}"
        case int(n) if n < 0:
            return f"Negative integer: {n}"
        case str(s) if len(s) > 10:
            return f"Long string: {s[:10]}..."
        case str(s):
            return f"Short string: {s}"
        case list(items):
            return f"List with {len(items)} items"
        case _:
            return "Unknown type"

Object-Oriented Programming

Class Design Principles

Follow SOLID principles for better class design.

from abc import ABC, abstractmethod
from typing import Protocol

# Single Responsibility Principle
class UserValidator:
    def validate_email(self, email: str) -> bool:
        return "@" in email and "." in email.split("@")[1]

class UserRepository:
    def save(self, user: dict) -> None:
        # Save user to database
        pass

# Open/Closed Principle
class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass

class Rectangle(Shape):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius

    def area(self) -> float:
        return 3.14159 * self.radius ** 2

# Liskov Substitution Principle
class Bird(ABC):
    @abstractmethod
    def move(self) -> None:
        pass

class FlyingBird(Bird):
    def move(self) -> None:
        print("Flying")

class WalkingBird(Bird):
    def move(self) -> None:
        print("Walking")

# Interface Segregation Principle
class Readable(Protocol):
    def read(self) -> str: ...

class Writable(Protocol):
    def write(self, data: str) -> None: ...

class FileReader:
    def read(self) -> str:
        return "file content"

class FileWriter:
    def write(self, data: str) -> None:
        print(f"Writing: {data}")

# Dependency Inversion Principle
class Database(ABC):
    @abstractmethod
    def save(self, data: dict) -> None: ...

class PostgreSQLDatabase(Database):
    def save(self, data: dict) -> None:
        print(f"Saving to PostgreSQL: {data}")

class UserService:
    def __init__(self, database: Database):
        self.database = database

    def create_user(self, user_data: dict) -> None:
        self.database.save(user_data)

Properties and Descriptors

Use properties for controlled attribute access.

class Temperature:
    def __init__(self, celsius: float = 0):
        self._celsius = celsius

    @property
    def celsius(self) -> float:
        return self._celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("Temperature cannot be below absolute zero")
        self._celsius = value

    @property
    def fahrenheit(self) -> float:
        return self._celsius * 9/5 + 32

    @fahrenheit.setter
    def fahrenheit(self, value: float) -> None:
        self.celsius = (value - 32) * 5/9

# Custom descriptor
class ValidatedString:
    def __init__(self, min_length: int = 0, max_length: int = 100):
        self.min_length = min_length
        self.max_length = max_length

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj.__dict__.get(self.name, "")

    def __set__(self, obj, value):
        if not isinstance(value, str):
            raise TypeError(f"{self.name} must be a string")
        if len(value) < self.min_length:
            raise ValueError(f"{self.name} must be at least {self.min_length} characters")
        if len(value) > self.max_length:
            raise ValueError(f"{self.name} must be at most {self.max_length} characters")
        obj.__dict__[self.name] = value

class User:
    name = ValidatedString(min_length=2, max_length=50)
    email = ValidatedString(min_length=5, max_length=100)

    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email

Metaclasses

Use metaclasses for advanced class customization.

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class DatabaseConnection(metaclass=SingletonMeta):
    def __init__(self):
        self.connected = True

# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # True

# Metaclass for automatic registration
class PluginRegistry(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if hasattr(cls, 'plugin_name'):
            PluginRegistry._plugins[cls.plugin_name] = cls
        return cls

    _plugins = {}

class Plugin(metaclass=PluginRegistry):
    pass

class EmailPlugin(Plugin):
    plugin_name = "email"

    def send(self, message: str) -> None:
        print(f"Sending email: {message}")

class SMSPlugin(Plugin):
    plugin_name = "sms"

    def send(self, message: str) -> None:
        print(f"Sending SMS: {message}")

# Usage
email_plugin = PluginRegistry._plugins["email"]()
email_plugin.send("Hello!")

Functional Programming

Higher-Order Functions

Use higher-order functions for functional programming patterns.

from functools import reduce, partial
from operator import add, mul

# Map, filter, reduce
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Map
squares = list(map(lambda x: x ** 2, numbers))
doubles = list(map(lambda x: x * 2, numbers))

# Filter
evens = list(filter(lambda x: x % 2 == 0, numbers))
odds = list(filter(lambda x: x % 2 != 0, numbers))

# Reduce
sum_all = reduce(add, numbers)
product_all = reduce(mul, numbers)

# Partial functions
def multiply(x: int, y: int) -> int:
    return x * y

double = partial(multiply, 2)
triple = partial(multiply, 3)

print(double(5))  # 10
print(triple(5))  # 15

Decorators

Use decorators for cross-cutting concerns.

from functools import wraps
import time
from typing import Callable, Any

# Basic decorator
def timer(func: Callable) -> Callable:
    @wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

# Decorator with parameters
def retry(max_attempts: int = 3, delay: float = 1.0):
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise e
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

# Class decorator
def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

# Usage
@timer
@retry(max_attempts=3, delay=0.5)
def fetch_data(url: str) -> str:
    # Simulate API call
    if url == "error":
        raise ConnectionError("Connection failed")
    return f"Data from {url}"

@singleton
class Config:
    def __init__(self):
        self.settings = {"debug": True}

Generators and Iterators

Use generators for memory-efficient iteration.

# Generator function
def fibonacci(n: int):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

# Generator expression
squares = (x ** 2 for x in range(10))

# Custom iterator
class CountDown:
    def __init__(self, start: int):
        self.start = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        self.start -= 1
        return self.start + 1

# Usage
for num in fibonacci(10):
    print(num, end=" ")
print()

for square in squares:
    print(square, end=" ")
print()

for num in CountDown(5):
    print(num, end=" ")
print()

Async Programming

Async/Await

Use async/await for asynchronous programming.

import asyncio
import aiohttp
from typing import List, Dict, Any

async def fetch_url(session: aiohttp.ClientSession, url: str) -> Dict[str, Any]:
    try:
        async with session.get(url) as response:
            return {
                "url": url,
                "status": response.status,
                "content": await response.text()
            }
    except Exception as e:
        return {
            "url": url,
            "error": str(e)
        }

async def fetch_multiple_urls(urls: List[str]) -> List[Dict[str, Any]]:
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        return await asyncio.gather(*tasks)

# Usage
async def main():
    urls = [
        "https://httpbin.org/delay/1",
        "https://httpbin.org/delay/2",
        "https://httpbin.org/delay/3"
    ]
    results = await fetch_multiple_urls(urls)
    for result in results:
        print(f"URL: {result['url']}, Status: {result.get('status', 'Error')}")

# Run the async function
asyncio.run(main())

Async Context Managers

Use async context managers for resource management.

import asyncio
from contextlib import asynccontextmanager

@asynccontextmanager
async def async_file_reader(filename: str):
    try:
        with open(filename, 'r') as f:
            yield f
    except FileNotFoundError:
        print(f"File {filename} not found")
        yield None

# Async generator
async def async_range(start: int, stop: int, step: int = 1):
    current = start
    while current < stop:
        yield current
        current += step
        await asyncio.sleep(0.1)  # Simulate async work

# Usage
async def process_file():
    async with async_file_reader("data.txt") as file:
        if file:
            content = file.read()
            print(f"File content: {content}")

async def count_async():
    async for num in async_range(0, 10):
        print(f"Count: {num}")

# Run both
asyncio.run(asyncio.gather(process_file(), count_async()))

Error Handling

Custom Exceptions

Create custom exceptions for better error handling.

class ValidationError(Exception):
    """Raised when validation fails"""
    def __init__(self, message: str, field: str = None):
        super().__init__(message)
        self.field = field

class DatabaseError(Exception):
    """Raised when database operations fail"""
    def __init__(self, message: str, error_code: int = None):
        super().__init__(message)
        self.error_code = error_code

class APIError(Exception):
    """Raised when API calls fail"""
    def __init__(self, message: str, status_code: int = None):
        super().__init__(message)
        self.status_code = status_code

# Exception chaining
def process_data(data: dict) -> None:
    try:
        validate_data(data)
        save_to_database(data)
    except ValidationError as e:
        raise DatabaseError("Failed to process data") from e

def validate_data(data: dict) -> None:
    if not data.get("name"):
        raise ValidationError("Name is required", "name")
    if not data.get("email"):
        raise ValidationError("Email is required", "email")

Error Recovery

Implement error recovery strategies.

import logging
from typing import Optional, Callable, Any

def retry_on_failure(
    max_attempts: int = 3,
    delay: float = 1.0,
    backoff: float = 2.0,
    exceptions: tuple = (Exception,)
):
    def decorator(func: Callable) -> Callable:
        def wrapper(*args, **kwargs) -> Any:
            current_delay = delay
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    if attempt == max_attempts - 1:
                        logging.error(f"Function {func.__name__} failed after {max_attempts} attempts: {e}")
                        raise e
                    logging.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {current_delay}s...")
                    time.sleep(current_delay)
                    current_delay *= backoff
            return None
        return wrapper
    return decorator

@retry_on_failure(max_attempts=3, delay=1.0, exceptions=(ConnectionError, TimeoutError))
def fetch_data(url: str) -> str:
    # Simulate API call that might fail
    import random
    if random.random() < 0.7:  # 70% chance of failure
        raise ConnectionError("Connection failed")
    return f"Data from {url}"

Testing

Unit Testing with pytest

Write comprehensive unit tests using pytest.

import pytest
from unittest.mock import Mock, patch
from typing import List

# Test functions
def add_numbers(a: int, b: int) -> int:
    return a + b

def divide_numbers(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

def process_items(items: List[str]) -> List[str]:
    return [item.upper() for item in items if item]

# Test classes
class Calculator:
    def __init__(self):
        self.history = []

    def add(self, a: int, b: int) -> int:
        result = a + b
        self.history.append(f"{a} + {b} = {result}")
        return result

    def get_history(self) -> List[str]:
        return self.history.copy()

# Test file
class TestCalculator:
    def test_add_positive_numbers(self):
        calc = Calculator()
        result = calc.add(2, 3)
        assert result == 5
        assert "2 + 3 = 5" in calc.get_history()

    def test_add_negative_numbers(self):
        calc = Calculator()
        result = calc.add(-2, -3)
        assert result == -5

    def test_add_zero(self):
        calc = Calculator()
        result = calc.add(5, 0)
        assert result == 5

def test_add_numbers():
    assert add_numbers(2, 3) == 5
    assert add_numbers(-1, 1) == 0
    assert add_numbers(0, 0) == 0

def test_divide_numbers():
    assert divide_numbers(10, 2) == 5.0
    assert divide_numbers(7, 3) == pytest.approx(2.333, rel=1e-2)

def test_divide_by_zero():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide_numbers(10, 0)

def test_process_items():
    items = ["hello", "", "world", None, "python"]
    result = process_items(items)
    assert result == ["HELLO", "WORLD", "PYTHON"]

# Fixtures
@pytest.fixture
def sample_data():
    return [1, 2, 3, 4, 5]

@pytest.fixture
def calculator():
    return Calculator()

def test_calculator_with_fixture(calculator, sample_data):
    result = calculator.add(sample_data[0], sample_data[1])
    assert result == 3

# Parametrized tests
@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (0, 0, 0),
    (10, -5, 5)
])
def test_add_parametrized(a, b, expected):
    assert add_numbers(a, b) == expected

# Mocking
def test_with_mock():
    with patch('requests.get') as mock_get:
        mock_get.return_value.json.return_value = {"status": "success"}

        # Your function that uses requests.get
        response = mock_get("https://api.example.com")
        data = response.json()

        assert data["status"] == "success"
        mock_get.assert_called_once_with("https://api.example.com")

Performance

Profiling and Optimization

Use profiling tools to identify performance bottlenecks.

import cProfile
import pstats
from functools import lru_cache
import time

# Profiling decorator
def profile(func):
    def wrapper(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        result = func(*args, **kwargs)
        profiler.disable()

        stats = pstats.Stats(profiler)
        stats.sort_stats('cumulative')
        stats.print_stats(10)  # Print top 10 functions

        return result
    return wrapper

# Caching
@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# Memory profiling
import tracemalloc

def memory_profile(func):
    def wrapper(*args, **kwargs):
        tracemalloc.start()
        result = func(*args, **kwargs)
        current, peak = tracemalloc.get_traced_memory()
        print(f"Current memory usage: {current / 1024 / 1024:.2f} MB")
        print(f"Peak memory usage: {peak / 1024 / 1024:.2f} MB")
        tracemalloc.stop()
        return result
    return wrapper

@profile
@memory_profile
def process_large_data():
    data = [i ** 2 for i in range(10000)]
    return sum(data)

# Performance comparison
def slow_function(n: int) -> int:
    result = 0
    for i in range(n):
        result += i ** 2
    return result

def fast_function(n: int) -> int:
    return sum(i ** 2 for i in range(n))

# Time comparison
start = time.time()
slow_function(10000)
slow_time = time.time() - start

start = time.time()
fast_function(10000)
fast_time = time.time() - start

print(f"Slow function: {slow_time:.4f}s")
print(f"Fast function: {fast_time:.4f}s")

Memory Optimization

Optimize memory usage for large datasets.

import sys
from typing import Iterator, Generator

# Generator for memory-efficient processing
def read_large_file(filename: str) -> Generator[str, None, None]:
    with open(filename, 'r') as file:
        for line in file:
            yield line.strip()

# Memory-efficient data processing
def process_large_dataset(data: Iterator[str]) -> Generator[dict, None, None]:
    for line in data:
        if line:  # Skip empty lines
            parts = line.split(',')
            if len(parts) >= 3:
                yield {
                    'id': int(parts[0]),
                    'name': parts[1],
                    'value': float(parts[2])
                }

# Usage
def analyze_large_file(filename: str):
    data = read_large_file(filename)
    processed = process_large_dataset(data)

    total = 0
    count = 0
    for record in processed:
        total += record['value']
        count += 1

    return total / count if count > 0 else 0

# Memory-efficient data structures
from collections import deque
import heapq

# Use deque for efficient append/pop operations
queue = deque()
queue.append(1)
queue.append(2)
queue.popleft()  # Efficient left pop

# Use heapq for efficient priority queues
priority_queue = []
heapq.heappush(priority_queue, (1, 'task1'))
heapq.heappush(priority_queue, (3, 'task3'))
heapq.heappush(priority_queue, (2, 'task2'))

while priority_queue:
    priority, task = heapq.heappop(priority_queue)
    print(f"Processing {task} with priority {priority}")

Packaging and Distribution

Project Structure

Organize your project with proper structure.

my_project/
├── pyproject.toml
├── README.md
├── LICENSE
├── .gitignore
├── src/
│   └── my_project/
│       ├── __init__.py
│       ├── core/
│       │   ├── __init__.py
│       │   ├── models.py
│       │   └── services.py
│       ├── utils/
│       │   ├── __init__.py
│       │   └── helpers.py
│       └── cli/
│           ├── __init__.py
│           └── main.py
├── tests/
│   ├── __init__.py
│   ├── test_models.py
│   └── test_services.py
├── docs/
│   └── index.md
└── examples/
    └── basic_usage.py

pyproject.toml Configuration

Configure your project with pyproject.toml.

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-project"
version = "0.1.0"
description = "A sample Python project"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Your Name", email = "your.email@example.com"}
]
keywords = ["python", "example", "package"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
]
dependencies = [
    "requests>=2.25.0",
    "click>=8.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=6.0",
    "pytest-cov>=2.0",
    "black>=21.0",
    "flake8>=3.8",
    "mypy>=0.800",
]
docs = [
    "sphinx>=4.0",
    "sphinx-rtd-theme>=0.5",
]

[project.scripts]
my-project = "my_project.cli.main:main"

[project.urls]
Homepage = "https://github.com/yourusername/my-project"
Repository = "https://github.com/yourusername/my-project"
Documentation = "https://my-project.readthedocs.io"
"Bug Tracker" = "https://github.com/yourusername/my-project/issues"

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-dir]
"" = "src"

[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
extend-exclude = '''
/(
  # directories
  \.eggs
  | \.git
  | \.hg
  | \.mypy_cache
  | \.tox
  | \.venv
  | build
  | dist
)/
'''

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
    "--strict-markers",
    "--strict-config",
    "--cov=my_project",
    "--cov-report=term-missing",
    "--cov-report=html",
]

[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true

Security

Input Validation and Sanitization

Implement proper input validation and sanitization.

import re
import html
from typing import Any, Dict, List
from dataclasses import dataclass

@dataclass
class ValidationError(Exception):
    field: str
    message: str

class InputValidator:
    @staticmethod
    def validate_email(email: str) -> str:
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(pattern, email):
            raise ValidationError("email", "Invalid email format")
        return email.lower()

    @staticmethod
    def validate_password(password: str) -> str:
        if len(password) < 8:
            raise ValidationError("password", "Password must be at least 8 characters")
        if not re.search(r'[A-Z]', password):
            raise ValidationError("password", "Password must contain uppercase letter")
        if not re.search(r'[a-z]', password):
            raise ValidationError("password", "Password must contain lowercase letter")
        if not re.search(r'\d', password):
            raise ValidationError("password", "Password must contain digit")
        return password

    @staticmethod
    def sanitize_html(text: str) -> str:
        return html.escape(text)

    @staticmethod
    def validate_sql_input(input_str: str) -> str:
        # Remove potential SQL injection patterns
        dangerous_patterns = [
            r'(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)',
            r'(\b(OR|AND)\s+\d+\s*=\s*\d+)',
            r'(\'|\"|;|--|\/\*|\*\/)'
        ]

        for pattern in dangerous_patterns:
            if re.search(pattern, input_str, re.IGNORECASE):
                raise ValidationError("input", "Potentially dangerous input detected")

        return input_str

# Usage
def validate_user_input(data: Dict[str, Any]) -> Dict[str, str]:
    validator = InputValidator()
    validated_data = {}

    try:
        validated_data['email'] = validator.validate_email(data['email'])
        validated_data['password'] = validator.validate_password(data['password'])
        validated_data['name'] = validator.sanitize_html(data['name'])
        validated_data['comment'] = validator.sanitize_html(data.get('comment', ''))
    except ValidationError as e:
        raise e

    return validated_data

Secure Configuration

Implement secure configuration management.

import os
from typing import Optional
from dataclasses import dataclass
from pathlib import Path

@dataclass
class SecurityConfig:
    secret_key: str
    database_url: str
    redis_url: str
    debug: bool = False

    @classmethod
    def from_env(cls) -> 'SecurityConfig':
        secret_key = os.getenv('SECRET_KEY')
        if not secret_key:
            raise ValueError("SECRET_KEY environment variable is required")

        database_url = os.getenv('DATABASE_URL')
        if not database_url:
            raise ValueError("DATABASE_URL environment variable is required")

        return cls(
            secret_key=secret_key,
            database_url=database_url,
            redis_url=os.getenv('REDIS_URL', 'redis://localhost:6379'),
            debug=os.getenv('DEBUG', 'False').lower() == 'true'
        )

    def validate(self) -> None:
        if len(self.secret_key) < 32:
            raise ValueError("SECRET_KEY must be at least 32 characters")

        if self.debug and os.getenv('ENVIRONMENT') == 'production':
            raise ValueError("Debug mode cannot be enabled in production")

# Environment-specific configuration
class Config:
    def __init__(self):
        self.environment = os.getenv('ENVIRONMENT', 'development')
        self.config = self._load_config()

    def _load_config(self) -> SecurityConfig:
        if self.environment == 'production':
            return self._load_production_config()
        elif self.environment == 'testing':
            return self._load_testing_config()
        else:
            return self._load_development_config()

    def _load_production_config(self) -> SecurityConfig:
        config = SecurityConfig.from_env()
        config.validate()
        return config

    def _load_testing_config(self) -> SecurityConfig:
        return SecurityConfig(
            secret_key='test-secret-key-for-testing-only',
            database_url='sqlite:///:memory:',
            redis_url='redis://localhost:6379/1',
            debug=True
        )

    def _load_development_config(self) -> SecurityConfig:
        return SecurityConfig(
            secret_key=os.getenv('SECRET_KEY', 'dev-secret-key-not-for-production'),
            database_url=os.getenv('DATABASE_URL', 'sqlite:///dev.db'),
            redis_url=os.getenv('REDIS_URL', 'redis://localhost:6379/0'),
            debug=True
        )

Documentation

Document your code effectively.

"""
User management module.

This module provides functionality for managing users in the system.
It includes user creation, validation, and retrieval operations.

Example:
    >>> from my_project import UserService
    >>> service = UserService()
    >>> user = service.create_user({"name": "John", "email": "john@example.com"})
    >>> print(user.name)
    John
"""

from typing import List, Optional, Dict, Any
from dataclasses import dataclass
from datetime import datetime

@dataclass
class User:
    """
    Represents a user in the system.

    Attributes:
        id: Unique identifier for the user
        name: User's full name
        email: User's email address
        created_at: Timestamp when user was created
        is_active: Whether the user account is active

    Example:
        >>> user = User(1, "John Doe", "john@example.com")
        >>> print(user.name)
        John Doe
    """
    id: int
    name: str
    email: str
    created_at: datetime = None
    is_active: bool = True

    def __post_init__(self):
        if self.created_at is None:
            self.created_at = datetime.now()

    def deactivate(self) -> None:
        """
        Deactivate the user account.

        This method sets the is_active flag to False, effectively
        disabling the user's access to the system.

        Returns:
            None

        Example:
            >>> user = User(1, "John", "john@example.com")
            >>> user.deactivate()
            >>> print(user.is_active)
            False
        """
        self.is_active = False

class UserService:
    """
    Service class for user management operations.

    This class provides methods for creating, retrieving, and managing
    users in the system. It handles validation and business logic
    related to user operations.

    Attributes:
        database: Database interface for data persistence

    Example:
        >>> service = UserService()
        >>> user = service.create_user({
        ...     "name": "Jane Doe",
        ...     "email": "jane@example.com"
        ... })
        >>> users = service.get_active_users()
    """

    def __init__(self, database: DatabaseInterface):
        """
        Initialize the UserService.

        Args:
            database: Database interface for data persistence

        Raises:
            ValueError: If database is None
        """
        if database is None:
            raise ValueError("Database cannot be None")
        self.database = database

    def create_user(self, user_data: Dict[str, Any]) -> User:
        """
        Create a new user.

        This method validates the user data and creates a new user
        in the system. It performs email validation and ensures
        the user data is complete.

        Args:
            user_data: Dictionary containing user information
                - name (str): User's full name
                - email (str): User's email address

        Returns:
            User: The created user object

        Raises:
            ValueError: If user data is invalid
            ValidationError: If email format is invalid

        Example:
            >>> service = UserService(database)
            >>> user = service.create_user({
            ...     "name": "John Doe",
            ...     "email": "john@example.com"
            ... })
            >>> print(user.name)
            John Doe
        """
        # Validation logic here
        user = User(
            id=self._generate_id(),
            name=user_data["name"],
            email=user_data["email"]
        )

        self.database.save(user.__dict__)
        return user

    def get_active_users(self) -> List[User]:
        """
        Retrieve all active users.

        Returns:
            List[User]: List of active user objects

        Example:
            >>> service = UserService(database)
            >>> active_users = service.get_active_users()
            >>> print(f"Found {len(active_users)} active users")
        """
        users_data = self.database.find({"is_active": True})
        return [User(**data) for data in users_data]

    def _generate_id(self) -> int:
        """Generate a unique user ID."""
        # Implementation here
        return 1