Skip to main content

Modules and Packages

Introduction​

As your Python programs grow larger, organizing code becomes essential. Modules allow you to split your code into separate files, making it more manageable, reusable, and testable.

A module is simply a Python file containing functions, classes, and variables. A package is a collection of modules organized in directories with special __init__.py files.

Python's module system provides:

  • Code organization and reusability
  • Namespace management
  • Import control and visibility
  • Standard library access
  • Third-party package integration

Understanding modules is crucial for building scalable Python applications and leveraging the vast Python ecosystem.


Concepts​

Creating Modules

  • Any .py file is a module
  • Module name is the filename without .py
  • Use import to access module contents

Import Statements

  • import module_name - imports entire module
  • from module_name import function_name - imports specific items
  • from module_name import * - imports all (not recommended)
  • import module_name as alias - creates an alias

Packages

  • Directories containing __init__.py file
  • Can contain sub-packages and modules
  • Create hierarchical organization
  • __init__.py controls package initialization

Module Search Path

  • Python looks for modules in specific locations
  • Current directory, PYTHONPATH, standard library, site-packages
  • Use sys.path to see search paths

Examples​

Basic Module Creation

# math_utils.py
"""A module for mathematical utility functions"""

PI = 3.14159
GOLDEN_RATIO = 1.61803

def add(a, b):
"""Add two numbers"""
return a + b

def multiply(a, b):
"""Multiply two numbers"""
return a * b

def circle_area(radius):
"""Calculate circle area"""
return PI * radius * radius

class Calculator:
"""A simple calculator class"""

def __init__(self):
self.history = []

def calculate(self, operation, a, b):
if operation == "add":
result = add(a, b)
elif operation == "multiply":
result = multiply(a, b)
else:
raise ValueError(f"Unknown operation: {operation}")

self.history.append(f"{operation}({a}, {b}) = {result}")
return result

if __name__ == "__main__":
# Code that runs only when module is executed directly
print("Math utils module loaded")
print(f"PI = {PI}")
calc = Calculator()
print(f"2 + 3 = {calc.calculate('add', 2, 3)}")

Using the Module

# main.py
import math_utils

# Access module attributes
print(f"PI value: {math_utils.PI}")

# Use module functions
result = math_utils.add(5, 3)
print(f"5 + 3 = {result}")

# Use module classes
calculator = math_utils.Calculator()
area = math_utils.circle_area(10)
print(f"Circle area (r=10): {area}")

# Different import styles
from math_utils import multiply, GOLDEN_RATIO
print(f"4 * 7 = {multiply(4, 7)}")
print(f"Golden ratio: {GOLDEN_RATIO}")

# Import with alias
import math_utils as math
print(f"Using alias: {math.add(1, 2)}")

# Import specific class
from math_utils import Calculator
my_calc = Calculator()
print(f"Calculator result: {my_calc.calculate('multiply', 6, 7)}")

Package Structure

my_package/
__init__.py
core/
__init__.py
database.py
models.py
utils/
__init__.py
helpers.py
validators.py
tests/
__init__.py
test_core.py
test_utils.py

Package Example - Database Module

# my_package/core/database.py
"""Database connection and operations"""

import json
from typing import Dict, List, Any

class DatabaseConnection:
"""Mock database connection"""

def __init__(self, connection_string: str):
self.connection_string = connection_string
self.is_connected = False
self.data = {}

def connect(self):
"""Establish database connection"""
print(f"Connecting to: {self.connection_string}")
self.is_connected = True
return True

def disconnect(self):
"""Close database connection"""
self.is_connected = False
print("Database connection closed")

def insert(self, table: str, data: Dict[str, Any]):
"""Insert data into table"""
if not self.is_connected:
raise ConnectionError("Not connected to database")

if table not in self.data:
self.data[table] = []

# Add ID if not present
if 'id' not in data:
data['id'] = len(self.data[table]) + 1

self.data[table].append(data)
return data['id']

def select(self, table: str, where: Dict[str, Any] = None) -> List[Dict[str, Any]]:
"""Select data from table"""
if not self.is_connected:
raise ConnectionError("Not connected to database")

if table not in self.data:
return []

results = self.data[table]

if where:
filtered_results = []
for row in results:
match = True
for key, value in where.items():
if key not in row or row[key] != value:
match = False
break
if match:
filtered_results.append(row)
return filtered_results

return results.copy()

def create_connection(db_type: str = "sqlite", **kwargs) -> DatabaseConnection:
"""Factory function to create database connections"""
if db_type == "sqlite":
conn_str = kwargs.get("database", "memory.db")
elif db_type == "postgresql":
host = kwargs.get("host", "localhost")
port = kwargs.get("port", 5432)
database = kwargs.get("database", "mydb")
conn_str = f"postgresql://{host}:{port}/{database}"
else:
raise ValueError(f"Unsupported database type: {db_type}")

return DatabaseConnection(conn_str)

Package Example - Models Module

# my_package/core/models.py
"""Data models for the application"""

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

@dataclass
class User:
"""User model"""
username: str
email: str
first_name: str = ""
last_name: str = ""
is_active: bool = True
created_at: datetime = field(default_factory=datetime.now)
id: Optional[int] = None

def full_name(self) -> str:
"""Get user's full name"""
return f"{self.first_name} {self.last_name}".strip()

def to_dict(self) -> Dict[str, Any]:
"""Convert user to dictionary"""
return {
'id': self.id,
'username': self.username,
'email': self.email,
'first_name': self.first_name,
'last_name': self.last_name,
'full_name': self.full_name(),
'is_active': self.is_active,
'created_at': self.created_at.isoformat()
}

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'User':
"""Create user from dictionary"""
# Handle datetime conversion
if isinstance(data.get('created_at'), str):
data['created_at'] = datetime.fromisoformat(data['created_at'])

return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})

@dataclass
class Product:
"""Product model"""
name: str
price: float
category: str = "general"
description: str = ""
in_stock: bool = True
id: Optional[int] = None
created_at: datetime = field(default_factory=datetime.now)

def formatted_price(self) -> str:
"""Get formatted price string"""
return f"${self.price:.2f}"

def to_dict(self) -> Dict[str, Any]:
"""Convert product to dictionary"""
return {
'id': self.id,
'name': self.name,
'price': self.price,
'formatted_price': self.formatted_price(),
'category': self.category,
'description': self.description,
'in_stock': self.in_stock,
'created_at': self.created_at.isoformat()
}

class UserRepository:
"""Repository for user operations"""

def __init__(self, db_connection):
self.db = db_connection
self.table_name = "users"

def create(self, user: User) -> User:
"""Create a new user"""
user_data = user.to_dict()
user_data.pop('id', None) # Remove ID for creation

user_id = self.db.insert(self.table_name, user_data)
user.id = user_id
return user

def find_by_username(self, username: str) -> Optional[User]:
"""Find user by username"""
results = self.db.select(self.table_name, {"username": username})
if results:
return User.from_dict(results[0])
return None

def find_all(self) -> List[User]:
"""Get all users"""
results = self.db.select(self.table_name)
return [User.from_dict(row) for row in results]

Package Initialization

# my_package/__init__.py
"""My Package - A demonstration of Python packages"""

__version__ = "1.0.0"
__author__ = "Your Name"

# Import key components for easy access
from .core.database import create_connection
from .core.models import User, Product, UserRepository

# Package-level constants
DEFAULT_DATABASE_TYPE = "sqlite"

# Package-level functions
def get_version():
"""Get package version"""
return __version__

def initialize_database(db_type=None):
"""Initialize database with default settings"""
db_type = db_type or DEFAULT_DATABASE_TYPE
conn = create_connection(db_type)
conn.connect()
return conn

# Control what gets imported with "from my_package import *"
__all__ = [
'create_connection',
'User',
'Product',
'UserRepository',
'get_version',
'initialize_database'
]

Using the Package

# app.py
"""Application using our custom package"""

# Different ways to import from packages
import my_package
from my_package import User, initialize_database
from my_package.core.database import DatabaseConnection
from my_package.core import models

def main():
# Using package-level imports
print(f"Package version: {my_package.get_version()}")

# Initialize database
db = initialize_database("sqlite")

# Create repository
user_repo = my_package.UserRepository(db)

# Create users
user1 = User(
username="alice",
email="alice@example.com",
first_name="Alice",
last_name="Johnson"
)

user2 = models.User(
username="bob",
email="bob@example.com",
first_name="Bob",
last_name="Smith"
)

# Save users
saved_user1 = user_repo.create(user1)
saved_user2 = user_repo.create(user2)

print(f"Created user: {saved_user1.full_name()} (ID: {saved_user1.id})")
print(f"Created user: {saved_user2.full_name()} (ID: {saved_user2.id})")

# Find users
found_user = user_repo.find_by_username("alice")
if found_user:
print(f"Found user: {found_user.to_dict()}")

# List all users
all_users = user_repo.find_all()
print(f"Total users: {len(all_users)}")

# Clean up
db.disconnect()

if __name__ == "__main__":
main()

Challenges​

1. File Operations Module​

Create a module file_manager.py with functions for reading, writing, and managing files.

💡 Show Answer
# file_manager.py
"""File management utilities module"""

import os
import json
import shutil
from pathlib import Path
from typing import List, Dict, Any, Optional

def read_text_file(filepath: str) -> str:
"""Read entire text file"""
try:
with open(filepath, 'r', encoding='utf-8') as file:
return file.read()
except FileNotFoundError:
raise FileNotFoundError(f"File not found: {filepath}")
except Exception as e:
raise IOError(f"Error reading file {filepath}: {e}")

def write_text_file(filepath: str, content: str, append: bool = False) -> bool:
"""Write content to text file"""
try:
mode = 'a' if append else 'w'
# Ensure directory exists
Path(filepath).parent.mkdir(parents=True, exist_ok=True)

with open(filepath, mode, encoding='utf-8') as file:
file.write(content)
return True
except Exception as e:
raise IOError(f"Error writing file {filepath}: {e}")

def read_json_file(filepath: str) -> Dict[str, Any]:
"""Read and parse JSON file"""
try:
with open(filepath, 'r', encoding='utf-8') as file:
return json.load(file)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in {filepath}: {e}")
except FileNotFoundError:
raise FileNotFoundError(f"JSON file not found: {filepath}")

def write_json_file(filepath: str, data: Dict[str, Any], indent: int = 2) -> bool:
"""Write data to JSON file"""
try:
Path(filepath).parent.mkdir(parents=True, exist_ok=True)

with open(filepath, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=indent, ensure_ascii=False)
return True
except Exception as e:
raise IOError(f"Error writing JSON file {filepath}: {e}")

def list_files(directory: str, extension: str = None, recursive: bool = False) -> List[str]:
"""List files in directory"""
try:
path = Path(directory)
if not path.exists():
raise FileNotFoundError(f"Directory not found: {directory}")

pattern = "**/*" if recursive else "*"
if extension:
pattern += f".{extension.lstrip('.')}"

files = [str(f) for f in path.glob(pattern) if f.is_file()]
return sorted(files)
except Exception as e:
raise IOError(f"Error listing files in {directory}: {e}")

def copy_file(source: str, destination: str, overwrite: bool = False) -> bool:
"""Copy file from source to destination"""
try:
if not overwrite and Path(destination).exists():
raise FileExistsError(f"Destination file already exists: {destination}")

# Ensure destination directory exists
Path(destination).parent.mkdir(parents=True, exist_ok=True)

shutil.copy2(source, destination)
return True
except Exception as e:
raise IOError(f"Error copying file from {source} to {destination}: {e}")

def delete_file(filepath: str, silent: bool = False) -> bool:
"""Delete file"""
try:
Path(filepath).unlink()
return True
except FileNotFoundError:
if not silent:
raise FileNotFoundError(f"File not found: {filepath}")
return False
except Exception as e:
raise IOError(f"Error deleting file {filepath}: {e}")

def get_file_info(filepath: str) -> Dict[str, Any]:
"""Get file information"""
try:
path = Path(filepath)
if not path.exists():
raise FileNotFoundError(f"File not found: {filepath}")

stat = path.stat()
return {
'name': path.name,
'size_bytes': stat.st_size,
'size_mb': round(stat.st_size / 1024 / 1024, 2),
'created': stat.st_ctime,
'modified': stat.st_mtime,
'is_file': path.is_file(),
'is_directory': path.is_dir(),
'extension': path.suffix,
'parent': str(path.parent)
}
except Exception as e:
raise IOError(f"Error getting file info for {filepath}: {e}")

class FileManager:
"""File manager class for organized file operations"""

def __init__(self, base_directory: str = "."):
self.base_directory = Path(base_directory)
self.base_directory.mkdir(parents=True, exist_ok=True)

def get_path(self, filename: str) -> str:
"""Get full path for filename"""
return str(self.base_directory / filename)

def save_data(self, filename: str, data: Any, format: str = "json") -> bool:
"""Save data in specified format"""
filepath = self.get_path(filename)

if format.lower() == "json":
return write_json_file(filepath, data)
elif format.lower() == "txt":
return write_text_file(filepath, str(data))
else:
raise ValueError(f"Unsupported format: {format}")

def load_data(self, filename: str, format: str = "json") -> Any:
"""Load data from file"""
filepath = self.get_path(filename)

if format.lower() == "json":
return read_json_file(filepath)
elif format.lower() == "txt":
return read_text_file(filepath)
else:
raise ValueError(f"Unsupported format: {format}")

def cleanup(self, pattern: str = "*") -> int:
"""Clean up files matching pattern"""
files = list(self.base_directory.glob(pattern))
count = 0
for file_path in files:
if file_path.is_file():
file_path.unlink()
count += 1
return count

# Usage example
if __name__ == "__main__":
# Test the module
print("Testing file_manager module...")

# Create test data
test_data = {
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
],
"timestamp": "2023-01-01T12:00:00"
}

# Test file operations
fm = FileManager("test_files")

# Save and load JSON
fm.save_data("users.json", test_data)
loaded_data = fm.load_data("users.json")
print(f"Loaded data: {loaded_data}")

# Save and load text
fm.save_data("readme.txt", "This is a test file", format="txt")
text_content = fm.load_data("readme.txt", format="txt")
print(f"Text content: {text_content}")

# List files
files = list_files("test_files")
print(f"Files in test_files: {files}")

# Get file info
for file_path in files:
info = get_file_info(file_path)
print(f"File info for {info['name']}: {info['size_bytes']} bytes")

# Cleanup
count = fm.cleanup()
print(f"Cleaned up {count} files")
# test_file_manager.py - Using the module
from file_manager import FileManager, read_json_file, write_json_file

def test_file_operations():
# Test basic file operations
data = {"test": True, "value": 42}

# Write and read JSON
write_json_file("test.json", data)
loaded = read_json_file("test.json")

assert loaded == data
print("✅ JSON operations work correctly")

# Test FileManager class
fm = FileManager("temp")
fm.save_data("config.json", {"debug": True, "port": 8080})

config = fm.load_data("config.json")
print(f"✅ FileManager works: {config}")

# Cleanup
fm.cleanup("*.json")

if __name__ == "__main__":
test_file_operations()

2. Configuration Package​

Create a package config with modules for different configuration types (database, logging, API settings).

💡 Show Answer
# config/__init__.py
"""Configuration package for application settings"""

import os
from typing import Dict, Any

# Package version
__version__ = "1.0.0"

# Import all configuration modules
from .database import DatabaseConfig, get_database_config
from .logging_config import LoggingConfig, setup_logging
from .api import APIConfig, get_api_config
from .base import BaseConfig, Environment

# Default environment
DEFAULT_ENVIRONMENT = Environment.DEVELOPMENT

def load_all_configs(env: Environment = None) -> Dict[str, Any]:
"""Load all configuration objects"""
env = env or DEFAULT_ENVIRONMENT

return {
'database': get_database_config(env),
'logging': LoggingConfig(env),
'api': get_api_config(env),
'environment': env.value
}

def setup_application(env: Environment = None):
"""Setup application with all configurations"""
configs = load_all_configs(env)

# Setup logging first
setup_logging(configs['logging'])

print(f"Application configured for {configs['environment']}")
return configs

# Export main items
__all__ = [
'DatabaseConfig', 'LoggingConfig', 'APIConfig', 'BaseConfig',
'Environment', 'get_database_config', 'get_api_config',
'setup_logging', 'load_all_configs', 'setup_application'
]
# config/base.py
"""Base configuration classes and utilities"""

import os
from enum import Enum
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional

class Environment(Enum):
"""Application environments"""
DEVELOPMENT = "development"
TESTING = "testing"
STAGING = "staging"
PRODUCTION = "production"

class BaseConfig(ABC):
"""Base configuration class"""

def __init__(self, environment: Environment = Environment.DEVELOPMENT):
self.environment = environment
self.load_from_environment()

@abstractmethod
def load_from_environment(self):
"""Load configuration from environment variables"""
pass

def get_env_var(self, key: str, default: Any = None, required: bool = False) -> str:
"""Get environment variable with validation"""
value = os.getenv(key, default)

if required and value is None:
raise ValueError(f"Required environment variable {key} is not set")

return value

def get_env_bool(self, key: str, default: bool = False) -> bool:
"""Get boolean environment variable"""
value = self.get_env_var(key, str(default))
return value.lower() in ('true', '1', 'yes', 'on')

def get_env_int(self, key: str, default: int = 0) -> int:
"""Get integer environment variable"""
value = self.get_env_var(key, str(default))
try:
return int(value)
except ValueError:
raise ValueError(f"Environment variable {key} must be an integer")

def to_dict(self) -> Dict[str, Any]:
"""Convert configuration to dictionary"""
return {
key: value for key, value in self.__dict__.items()
if not key.startswith('_')
}

def __str__(self):
items = [f"{k}={v}" for k, v in self.to_dict().items()]
return f"{self.__class__.__name__}({', '.join(items)})"
# config/database.py
"""Database configuration module"""

from typing import Optional
from .base import BaseConfig, Environment

class DatabaseConfig(BaseConfig):
"""Database configuration"""

def __init__(self, environment: Environment = Environment.DEVELOPMENT):
# Default values
self.host = "localhost"
self.port = 5432
self.database = "myapp"
self.username = "user"
self.password = "password"
self.pool_size = 5
self.ssl_mode = "prefer"
self.timeout = 30

super().__init__(environment)

def load_from_environment(self):
"""Load database config from environment variables"""
self.host = self.get_env_var("DB_HOST", self.host)
self.port = self.get_env_int("DB_PORT", self.port)
self.database = self.get_env_var("DB_NAME", self.database)
self.username = self.get_env_var("DB_USER", self.username)
self.password = self.get_env_var("DB_PASSWORD", self.password)
self.pool_size = self.get_env_int("DB_POOL_SIZE", self.pool_size)
self.ssl_mode = self.get_env_var("DB_SSL_MODE", self.ssl_mode)
self.timeout = self.get_env_int("DB_TIMEOUT", self.timeout)

# Environment-specific overrides
if self.environment == Environment.PRODUCTION:
self.ssl_mode = "require"
self.pool_size = 20
elif self.environment == Environment.TESTING:
self.database = f"{self.database}_test"
self.pool_size = 2

def get_connection_string(self) -> str:
"""Get database connection string"""
return (f"postgresql://{self.username}:{self.password}@"
f"{self.host}:{self.port}/{self.database}?"
f"sslmode={self.ssl_mode}")

def get_connection_params(self) -> dict:
"""Get connection parameters as dictionary"""
return {
'host': self.host,
'port': self.port,
'database': self.database,
'user': self.username,
'password': self.password,
'sslmode': self.ssl_mode,
'connect_timeout': self.timeout
}

def get_database_config(environment: Environment = None) -> DatabaseConfig:
"""Factory function to get database configuration"""
if environment is None:
environment = Environment.DEVELOPMENT

return DatabaseConfig(environment)

# Environment-specific configurations
class DevelopmentDatabaseConfig(DatabaseConfig):
def __init__(self):
super().__init__(Environment.DEVELOPMENT)
self.host = "localhost"
self.database = "myapp_dev"

class ProductionDatabaseConfig(DatabaseConfig):
def __init__(self):
super().__init__(Environment.PRODUCTION)
self.ssl_mode = "require"
self.pool_size = 50
# config/logging_config.py
"""Logging configuration module"""

import logging
import sys
from typing import Dict, Any
from .base import BaseConfig, Environment

class LoggingConfig(BaseConfig):
"""Logging configuration"""

def __init__(self, environment: Environment = Environment.DEVELOPMENT):
# Default values
self.level = "INFO"
self.format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
self.date_format = "%Y-%m-%d %H:%M:%S"
self.log_to_file = False
self.log_file = "app.log"
self.max_file_size = 10 * 1024 * 1024 # 10MB
self.backup_count = 5
self.log_to_console = True

super().__init__(environment)

def load_from_environment(self):
"""Load logging config from environment variables"""
self.level = self.get_env_var("LOG_LEVEL", self.level).upper()
self.format = self.get_env_var("LOG_FORMAT", self.format)
self.log_to_file = self.get_env_bool("LOG_TO_FILE", self.log_to_file)
self.log_file = self.get_env_var("LOG_FILE", self.log_file)
self.log_to_console = self.get_env_bool("LOG_TO_CONSOLE", self.log_to_console)

# Environment-specific settings
if self.environment == Environment.PRODUCTION:
self.level = "WARNING"
self.log_to_file = True
self.log_file = "/var/log/myapp/app.log"
elif self.environment == Environment.DEVELOPMENT:
self.level = "DEBUG"
self.log_to_console = True
elif self.environment == Environment.TESTING:
self.level = "ERROR"
self.log_to_console = False

def get_logging_config(self) -> Dict[str, Any]:
"""Get logging configuration dictionary"""
return {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': self.format,
'datefmt': self.date_format,
},
'detailed': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(funcName)s - %(message)s',
'datefmt': self.date_format,
}
},
'handlers': self._get_handlers(),
'root': {
'level': self.level,
'handlers': self._get_handler_names(),
}
}

def _get_handlers(self) -> Dict[str, Any]:
"""Get logging handlers configuration"""
handlers = {}

if self.log_to_console:
handlers['console'] = {
'class': 'logging.StreamHandler',
'level': self.level,
'formatter': 'default',
'stream': 'ext://sys.stdout',
}

if self.log_to_file:
handlers['file'] = {
'class': 'logging.handlers.RotatingFileHandler',
'level': self.level,
'formatter': 'detailed',
'filename': self.log_file,
'maxBytes': self.max_file_size,
'backupCount': self.backup_count,
}

return handlers

def _get_handler_names(self) -> list:
"""Get list of handler names"""
names = []
if self.log_to_console:
names.append('console')
if self.log_to_file:
names.append('file')
return names

def setup_logging(config: LoggingConfig):
"""Setup logging with the provided configuration"""
import logging.config

logging_dict = config.get_logging_config()
logging.config.dictConfig(logging_dict)

# Test logging setup
logger = logging.getLogger(__name__)
logger.info(f"Logging configured for {config.environment.value} environment")
logger.debug(f"Log level: {config.level}")
# config/api.py
"""API configuration module"""

from typing import List, Dict, Any
from .base import BaseConfig, Environment

class APIConfig(BaseConfig):
"""API server configuration"""

def __init__(self, environment: Environment = Environment.DEVELOPMENT):
# Default values
self.host = "127.0.0.1"
self.port = 8000
self.debug = True
self.cors_enabled = True
self.cors_origins = ["*"]
self.rate_limit = 100 # requests per minute
self.api_key_required = False
self.api_version = "v1"
self.request_timeout = 30
self.max_content_length = 16 * 1024 * 1024 # 16MB

super().__init__(environment)

def load_from_environment(self):
"""Load API config from environment variables"""
self.host = self.get_env_var("API_HOST", self.host)
self.port = self.get_env_int("API_PORT", self.port)
self.debug = self.get_env_bool("API_DEBUG", self.debug)
self.cors_enabled = self.get_env_bool("CORS_ENABLED", self.cors_enabled)
self.rate_limit = self.get_env_int("RATE_LIMIT", self.rate_limit)
self.api_key_required = self.get_env_bool("API_KEY_REQUIRED", self.api_key_required)
self.api_version = self.get_env_var("API_VERSION", self.api_version)

# Parse CORS origins
cors_origins = self.get_env_var("CORS_ORIGINS", ",".join(self.cors_origins))
self.cors_origins = [origin.strip() for origin in cors_origins.split(",")]

# Environment-specific settings
if self.environment == Environment.PRODUCTION:
self.debug = False
self.host = "0.0.0.0"
self.api_key_required = True
self.cors_origins = self.get_production_cors_origins()
self.rate_limit = 1000
elif self.environment == Environment.TESTING:
self.port = 0 # Use random port for testing
self.debug = False
self.rate_limit = 1000

def get_production_cors_origins(self) -> List[str]:
"""Get production CORS origins"""
prod_origins = self.get_env_var("PROD_CORS_ORIGINS")
if prod_origins:
return [origin.strip() for origin in prod_origins.split(",")]
return ["https://myapp.com", "https://api.myapp.com"]

def get_server_url(self) -> str:
"""Get full server URL"""
return f"http://{self.host}:{self.port}"

def get_api_base_url(self) -> str:
"""Get API base URL with version"""
return f"{self.get_server_url()}/api/{self.api_version}"

def get_api_config(environment: Environment = None) -> APIConfig:
"""Factory function to get API configuration"""
if environment is None:
environment = Environment.DEVELOPMENT

return APIConfig(environment)
# test_config_package.py - Using the configuration package
import os
from config import (
load_all_configs,
setup_application,
Environment,
DatabaseConfig,
APIConfig,
LoggingConfig
)

def test_configuration_package():
"""Test the configuration package"""

# Test individual configs
print("Testing individual configurations:")

# Database config
db_config = DatabaseConfig(Environment.DEVELOPMENT)
print(f"Database: {db_config}")
print(f"Connection string: {db_config.get_connection_string()}")

# API config
api_config = APIConfig(Environment.DEVELOPMENT)
print(f"API: {api_config}")
print(f"Server URL: {api_config.get_server_url()}")

# Logging config
log_config = LoggingConfig(Environment.DEVELOPMENT)
print(f"Logging: {log_config}")

# Test loading all configs
print("\nTesting complete configuration loading:")
configs = load_all_configs(Environment.DEVELOPMENT)

for config_name, config_obj in configs.items():
if hasattr(config_obj, 'to_dict'):
print(f"{config_name}: {config_obj.to_dict()}")
else:
print(f"{config_name}: {config_obj}")

# Test environment variables
print("\nTesting with environment variables:")
os.environ["DB_HOST"] = "production.example.com"
os.environ["API_PORT"] = "9000"
os.environ["LOG_LEVEL"] = "ERROR"

env_configs = load_all_configs(Environment.PRODUCTION)
print(f"Production DB host: {env_configs['database'].host}")
print(f"Production API port: {env_configs['api'].port}")
print(f"Production log level: {env_configs['logging'].level}")

# Test application setup
print("\nTesting application setup:")
app_configs = setup_application(Environment.DEVELOPMENT)
print("Application configured successfully!")

if __name__ == "__main__":
test_configuration_package()

3. Math Library Package​

Create a comprehensive math package with modules for basic operations, statistics, and geometry.

💡 Show Answer
# math_lib/__init__.py
"""Math Library - A comprehensive mathematics package"""

__version__ = "1.0.0"
__author__ = "Python Developer"

# Import main modules
from . import basic_ops
from . import statistics as stats
from . import geometry
from . import constants

# Import commonly used functions for convenience
from .basic_ops import add, subtract, multiply, divide, power, factorial
from .statistics import mean, median, mode, std_dev
from .geometry import Circle, Rectangle, Triangle
from .constants import PI, E, GOLDEN_RATIO

# Package-level convenience functions
def calculate(expression: str) -> float:
"""Safe calculator for basic expressions"""
return basic_ops.safe_eval(expression)

def distance_2d(x1: float, y1: float, x2: float, y2: float) -> float:
"""Calculate 2D distance between two points"""
return geometry.distance((x1, y1), (x2, y2))

# Export main items
__all__ = [
# Modules
'basic_ops', 'stats', 'geometry', 'constants',
# Functions
'add', 'subtract', 'multiply', 'divide', 'power', 'factorial',
'mean', 'median', 'mode', 'std_dev',
'calculate', 'distance_2d',
# Classes
'Circle', 'Rectangle', 'Triangle',
# Constants
'PI', 'E', 'GOLDEN_RATIO'
]
# math_lib/constants.py
"""Mathematical constants"""

import math

# Basic constants
PI = 3.141592653589793
E = 2.718281828459045
GOLDEN_RATIO = 1.618033988749895

# Extended constants
TAU = 2 * PI # Full circle in radians
SQRT_2 = math.sqrt(2)
SQRT_3 = math.sqrt(3)
SQRT_5 = math.sqrt(5)

# Physical constants (for reference)
SPEED_OF_LIGHT = 299_792_458 # m/s
PLANCK_CONSTANT = 6.62607015e-34 # J⋅Hz⁻¹
AVOGADRO_NUMBER = 6.02214076e23 # mol⁻¹

# Mathematical limits
INFINITY = float('inf')
NEG_INFINITY = float('-inf')
NAN = float('nan')

def get_all_constants():
"""Get all mathematical constants as a dictionary"""
return {
'PI': PI,
'E': E,
'GOLDEN_RATIO': GOLDEN_RATIO,
'TAU': TAU,
'SQRT_2': SQRT_2,
'SQRT_3': SQRT_3,
'SQRT_5': SQRT_5,
}
# math_lib/basic_ops.py
"""Basic mathematical operations"""

import re
from typing import Union

Number = Union[int, float]

def add(a: Number, b: Number) -> Number:
"""Add two numbers"""
return a + b

def subtract(a: Number, b: Number) -> Number:
"""Subtract b from a"""
return a - b

def multiply(a: Number, b: Number) -> Number:
"""Multiply two numbers"""
return a * b

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

def power(base: Number, exponent: Number) -> Number:
"""Raise base to the power of exponent"""
return base ** exponent

def factorial(n: int) -> int:
"""Calculate factorial of n"""
if not isinstance(n, int) or n < 0:
raise ValueError("Factorial is only defined for non-negative integers")

if n <= 1:
return 1

result = 1
for i in range(2, n + 1):
result *= i
return result

def gcd(a: int, b: int) -> int:
"""Calculate Greatest Common Divisor"""
while b:
a, b = b, a % b
return abs(a)

def lcm(a: int, b: int) -> int:
"""Calculate Least Common Multiple"""
return abs(a * b) // gcd(a, b) if a and b else 0

def is_prime(n: int) -> bool:
"""Check if number is prime"""
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False

for i in range(3, int(n**0.5) + 1, 2):
if n % i == 0:
return False
return True

def fibonacci(n: int) -> int:
"""Generate nth Fibonacci number"""
if n < 0:
raise ValueError("Fibonacci is only defined for non-negative integers")
if n <= 1:
return n

a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b

def safe_eval(expression: str) -> float:
"""Safely evaluate mathematical expressions"""
# Remove whitespace
expression = expression.replace(" ", "")

# Check for allowed characters only
allowed_chars = set("0123456789+-*/.()^")
if not all(c in allowed_chars for c in expression):
raise ValueError("Invalid characters in expression")

# Replace ^ with **
expression = expression.replace("^", "**")

# Simple validation
if not re.match(r'^[0-9+\-*/.()^]+$', expression.replace("**", "^")):
raise ValueError("Invalid expression format")

try:
# Use eval with restricted globals
return eval(expression, {"__builtins__": {}})
except Exception as e:
raise ValueError(f"Error evaluating expression: {e}")

class Calculator:
"""Advanced calculator class"""

def __init__(self):
self.history = []
self.memory = 0

def calculate(self, expression: str) -> float:
"""Calculate expression and store in history"""
try:
result = safe_eval(expression)
self.history.append(f"{expression} = {result}")
return result
except Exception as e:
error_msg = f"{expression} = ERROR: {e}"
self.history.append(error_msg)
raise

def memory_store(self, value: Number):
"""Store value in memory"""
self.memory = value

def memory_recall(self) -> Number:
"""Recall value from memory"""
return self.memory

def memory_clear(self):
"""Clear memory"""
self.memory = 0

def get_history(self, limit: int = 10) -> list:
"""Get calculation history"""
return self.history[-limit:]

def clear_history(self):
"""Clear calculation history"""
self.history.clear()
# math_lib/statistics.py
"""Statistical functions"""

from typing import List, Union
import math

Number = Union[int, float]

def mean(data: List[Number]) -> float:
"""Calculate arithmetic mean"""
if not data:
raise ValueError("Cannot calculate mean of empty list")
return sum(data) / len(data)

def median(data: List[Number]) -> float:
"""Calculate median"""
if not data:
raise ValueError("Cannot calculate median of empty list")

sorted_data = sorted(data)
n = len(sorted_data)

if n % 2 == 0:
return (sorted_data[n//2 - 1] + sorted_data[n//2]) / 2
else:
return sorted_data[n//2]

def mode(data: List[Number]) -> List[Number]:
"""Calculate mode(s)"""
if not data:
raise ValueError("Cannot calculate mode of empty list")

frequency = {}
for value in data:
frequency[value] = frequency.get(value, 0) + 1

max_freq = max(frequency.values())
modes = [value for value, freq in frequency.items() if freq == max_freq]

return modes

def variance(data: List[Number], sample: bool = True) -> float:
"""Calculate variance"""
if not data:
raise ValueError("Cannot calculate variance of empty list")
if len(data) == 1 and sample:
raise ValueError("Cannot calculate sample variance with single data point")

avg = mean(data)
squared_diffs = [(x - avg) ** 2 for x in data]

denominator = len(data) - 1 if sample else len(data)
return sum(squared_diffs) / denominator

def std_dev(data: List[Number], sample: bool = True) -> float:
"""Calculate standard deviation"""
return math.sqrt(variance(data, sample))

def range_stat(data: List[Number]) -> Number:
"""Calculate range (max - min)"""
if not data:
raise ValueError("Cannot calculate range of empty list")
return max(data) - min(data)

def quartiles(data: List[Number]) -> tuple:
"""Calculate Q1, Q2 (median), Q3"""
if not data:
raise ValueError("Cannot calculate quartiles of empty list")

sorted_data = sorted(data)
n = len(sorted_data)

q2 = median(sorted_data)

if n % 2 == 0:
lower_half = sorted_data[:n//2]
upper_half = sorted_data[n//2:]
else:
lower_half = sorted_data[:n//2]
upper_half = sorted_data[n//2 + 1:]

q1 = median(lower_half) if lower_half else sorted_data[0]
q3 = median(upper_half) if upper_half else sorted_data[-1]

return (q1, q2, q3)

def correlation(x_data: List[Number], y_data: List[Number]) -> float:
"""Calculate Pearson correlation coefficient"""
if len(x_data) != len(y_data):
raise ValueError("x_data and y_data must have same length")
if len(x_data) < 2:
raise ValueError("Need at least 2 data points")

n = len(x_data)
sum_x = sum(x_data)
sum_y = sum(y_data)
sum_xy = sum(x * y for x, y in zip(x_data, y_data))
sum_x2 = sum(x * x for x in x_data)
sum_y2 = sum(y * y for y in y_data)

numerator = n * sum_xy - sum_x * sum_y
denominator = math.sqrt((n * sum_x2 - sum_x**2) * (n * sum_y2 - sum_y**2))

if denominator == 0:
return 0 # No correlation if no variance

return numerator / denominator

class StatisticalSummary:
"""Statistical summary for a dataset"""

def __init__(self, data: List[Number]):
if not data:
raise ValueError("Dataset cannot be empty")

self.data = sorted(data)
self.count = len(data)
self.mean = mean(data)
self.median = median(data)
self.mode = mode(data)
self.std_dev = std_dev(data)
self.variance = variance(data)
self.min = min(data)
self.max = max(data)
self.range = range_stat(data)
self.q1, self.q2, self.q3 = quartiles(data)

def __str__(self):
return (f"Statistical Summary:\n"
f" Count: {self.count}\n"
f" Mean: {self.mean:.2f}\n"
f" Median: {self.median:.2f}\n"
f" Mode: {self.mode}\n"
f" Std Dev: {self.std_dev:.2f}\n"
f" Min: {self.min}\n"
f" Max: {self.max}\n"
f" Range: {self.range}\n"
f" Q1: {self.q1:.2f}\n"
f" Q3: {self.q3:.2f}")
# math_lib/geometry.py
"""Geometric calculations and shapes"""

import math
from typing import Tuple, Union
from .constants import PI

Point = Tuple[float, float]
Number = Union[int, float]

def distance(p1: Point, p2: Point) -> float:
"""Calculate distance between two points"""
return math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)

def midpoint(p1: Point, p2: Point) -> Point:
"""Calculate midpoint between two points"""
return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)

class Shape:
"""Base class for geometric shapes"""

def area(self) -> float:
"""Calculate area"""
raise NotImplementedError("Subclasses must implement area()")

def perimeter(self) -> float:
"""Calculate perimeter"""
raise NotImplementedError("Subclasses must implement perimeter()")

class Circle(Shape):
"""Circle shape"""

def __init__(self, radius: Number):
if radius <= 0:
raise ValueError("Radius must be positive")
self.radius = radius

def area(self) -> float:
"""Calculate circle area"""
return PI * self.radius ** 2

def perimeter(self) -> float:
"""Calculate circle circumference"""
return 2 * PI * self.radius

def diameter(self) -> float:
"""Calculate diameter"""
return 2 * self.radius

def __str__(self):
return f"Circle(radius={self.radius})"

class Rectangle(Shape):
"""Rectangle shape"""

def __init__(self, width: Number, height: Number):
if width <= 0 or height <= 0:
raise ValueError("Width and height must be positive")
self.width = width
self.height = height

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

def perimeter(self) -> float:
"""Calculate rectangle perimeter"""
return 2 * (self.width + self.height)

def diagonal(self) -> float:
"""Calculate diagonal length"""
return math.sqrt(self.width**2 + self.height**2)

def __str__(self):
return f"Rectangle(width={self.width}, height={self.height})"

class Triangle(Shape):
"""Triangle shape"""

def __init__(self, side_a: Number, side_b: Number, side_c: Number):
if side_a <= 0 or side_b <= 0 or side_c <= 0:
raise ValueError("All sides must be positive")

# Check triangle inequality
if (side_a + side_b <= side_c or
side_a + side_c <= side_b or
side_b + side_c <= side_a):
raise ValueError("Invalid triangle: sides don't satisfy triangle inequality")

self.side_a = side_a
self.side_b = side_b
self.side_c = side_c

def area(self) -> float:
"""Calculate triangle area using Heron's formula"""
s = self.perimeter() / 2 # semi-perimeter
return math.sqrt(s * (s - self.side_a) * (s - self.side_b) * (s - self.side_c))

def perimeter(self) -> float:
"""Calculate triangle perimeter"""
return self.side_a + self.side_b + self.side_c

def is_right_triangle(self) -> bool:
"""Check if triangle is a right triangle"""
sides = sorted([self.side_a, self.side_b, self.side_c])
return abs(sides[0]**2 + sides[1]**2 - sides[2]**2) < 1e-10

def is_equilateral(self) -> bool:
"""Check if triangle is equilateral"""
return (abs(self.side_a - self.side_b) < 1e-10 and
abs(self.side_b - self.side_c) < 1e-10)

def is_isosceles(self) -> bool:
"""Check if triangle is isosceles"""
return (abs(self.side_a - self.side_b) < 1e-10 or
abs(self.side_b - self.side_c) < 1e-10 or
abs(self.side_a - self.side_c) < 1e-10)

def __str__(self):
return f"Triangle(sides={self.side_a}, {self.side_b}, {self.side_c})"

def shapes_demo():
"""Demonstrate geometric shapes"""
print("Geometry Demo:")

# Circle
circle = Circle(5)
print(f"{circle}")
print(f" Area: {circle.area():.2f}")
print(f" Circumference: {circle.perimeter():.2f}")

# Rectangle
rectangle = Rectangle(4, 6)
print(f"\n{rectangle}")
print(f" Area: {rectangle.area():.2f}")
print(f" Perimeter: {rectangle.perimeter():.2f}")
print(f" Diagonal: {rectangle.diagonal():.2f}")

# Triangle
triangle = Triangle(3, 4, 5)
print(f"\n{triangle}")
print(f" Area: {triangle.area():.2f}")
print(f" Perimeter: {triangle.perimeter():.2f}")
print(f" Is right triangle: {triangle.is_right_triangle()}")
# test_math_lib.py - Using the math library package
from math_lib import (
add, multiply, factorial, mean, std_dev,
Circle, Rectangle, Triangle,
PI, E, StatisticalSummary
)
import math_lib

def test_math_library():
"""Test the complete math library package"""

print("Testing Math Library Package")
print("=" * 40)

# Test basic operations
print("Basic Operations:")
print(f" 5 + 3 = {add(5, 3)}")
print(f" 4 × 7 = {multiply(4, 7)}")
print(f" 5! = {factorial(5)}")

# Test statistics
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(f"\nStatistics for {data}:")
print(f" Mean: {mean(data):.2f}")
print(f" Std Dev: {std_dev(data):.2f}")

# Statistical summary
summary = StatisticalSummary(data)
print(f"\n{summary}")

# Test geometry
print(f"\nGeometry:")
circle = Circle(3)
rectangle = Rectangle(4, 5)
triangle = Triangle(3, 4, 5)

print(f" Circle (r=3): Area = {circle.area():.2f}")
print(f" Rectangle (4×5): Area = {rectangle.area():.2f}")
print(f" Triangle (3,4,5): Area = {triangle.area():.2f}")

# Test constants
print(f"\nConstants:")
print(f" π = {PI}")
print(f" e = {E}")

# Test package-level functions
print(f"\nPackage functions:")
print(f" calculate('2+3*4') = {math_lib.calculate('2+3*4')}")
print(f" distance_2d(0,0,3,4) = {math_lib.distance_2d(0, 0, 3, 4)}")

if __name__ == "__main__":
test_math_library()