Python Session 06: Infrastructure Patterns
Production patterns. This session covers HTTP requests, CLI argument parsing, configuration files, and logging.
Pre-Session State
-
Can run subprocess commands
-
Understand environment variables
-
Know functions and classes
Lesson 1: HTTP Requests (urllib)
Concept: Use urllib.request for HTTP without external deps.
Exercise 1.1: Basic GET
from urllib.request import urlopen
import json
# Note: In production, use 'requests' library
url = "https://api.github.com/users/octocat"
with urlopen(url) as response:
data = json.loads(response.read())
print(f"Name: {data['name']}")
print(f"Repos: {data['public_repos']}")
Exercise 1.2: With headers
from urllib.request import Request, urlopen
url = "https://api.github.com/user"
token = "ghp_xxxx" # Your token
req = Request(url)
req.add_header("Authorization", f"token {token}")
req.add_header("Accept", "application/json")
try:
with urlopen(req) as response:
print(response.read().decode())
except Exception as e:
print(f"Error: {e}")
Exercise 1.3: POST request
from urllib.request import Request, urlopen
import json
url = "https://httpbin.org/post"
data = {"name": "kvm-01", "ip": "10.50.1.110"}
req = Request(url, data=json.dumps(data).encode(), method='POST')
req.add_header("Content-Type", "application/json")
with urlopen(req) as response:
result = json.loads(response.read())
print(result)
Lesson 2: Argument Parsing
Concept: Use argparse for CLI tools.
Exercise 2.1: Basic arguments
import argparse
parser = argparse.ArgumentParser(description='Host management tool')
parser.add_argument('hostname', help='Target hostname')
parser.add_argument('-p', '--port', type=int, default=22, help='SSH port')
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
print(f"Connecting to {args.hostname}:{args.port}")
if args.verbose:
print("Verbose mode enabled")
Exercise 2.2: Subcommands
import argparse
parser = argparse.ArgumentParser(description='Server tool')
subparsers = parser.add_subparsers(dest='command')
# 'list' subcommand
list_parser = subparsers.add_parser('list', help='List servers')
list_parser.add_argument('--format', choices=['table', 'json'], default='table')
# 'ping' subcommand
ping_parser = subparsers.add_parser('ping', help='Ping server')
ping_parser.add_argument('host', help='Host to ping')
args = parser.parse_args()
if args.command == 'list':
print(f"Listing servers in {args.format} format")
elif args.command == 'ping':
print(f"Pinging {args.host}")
Exercise 2.3: Required vs optional
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--config', required=True, help='Config file (required)')
parser.add_argument('--dry-run', action='store_true', help='Don\'t make changes')
parser.add_argument('--count', type=int, default=1, help='Number of iterations')
args = parser.parse_args()
Lesson 3: Configuration Files
Concept: Use configparser for INI-style configs.
Exercise 3.1: Read config
import configparser
# Create test config
config_content = """
[server]
hostname = kvm-01
ip = 10.50.1.110
port = 22
[credentials]
username = admin
"""
with open('/tmp/server.ini', 'w') as f:
f.write(config_content)
# Read config
config = configparser.ConfigParser()
config.read('/tmp/server.ini')
print(config['server']['hostname'])
print(config.get('server', 'port'))
print(config.getint('server', 'port'))
Exercise 3.2: Write config
import configparser
config = configparser.ConfigParser()
config['server'] = {
'hostname': 'kvm-01',
'ip': '10.50.1.110',
'port': '22'
}
config['logging'] = {
'level': 'INFO',
'file': '/var/log/app.log'
}
with open('/tmp/app.ini', 'w') as f:
config.write(f)
Lesson 4: Logging
Concept: Use logging for structured output.
Exercise 4.1: Basic logging
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
logging.debug("Debug message (won't show)")
logging.info("Info message")
logging.warning("Warning message")
logging.error("Error message")
Exercise 4.2: Logger with file output
import logging
# Create logger
logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)
# Console handler
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# File handler
file_handler = logging.FileHandler('/tmp/myapp.log')
file_handler.setLevel(logging.DEBUG)
# Formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console.setFormatter(formatter)
file_handler.setFormatter(formatter)
logger.addHandler(console)
logger.addHandler(file_handler)
logger.info("Application started")
logger.debug("Debug info (file only)")
logger.error("Something went wrong")
Exercise 4.3: Structured logging context
import logging
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - [%(host)s] %(message)s')
logger = logging.getLogger()
# Add context with extra
logger.info("Connection established", extra={'host': 'kvm-01'})
logger.warning("High CPU usage", extra={'host': 'kvm-02'})
Summary: What You Learned
| Concept | Syntax | Example |
|---|---|---|
HTTP GET |
|
Returns response object |
HTTP headers |
|
Add auth, content-type |
argparse |
|
|
Subcommands |
|
|
ConfigParser |
|
INI file handling |
Logging |
|
Configure level, format |
Logger |
|
Named logger instance |
Exercises to Complete
-
[ ] Build a CLI tool with list/show/ping subcommands
-
[ ] Read server config from INI file and ping each server
-
[ ] Fetch GitHub user info and log the results
-
[ ] Create a tool with --verbose flag that adjusts log level
Related Resources
-
Drill 06 - Practice these patterns
-
Python docs: argparse, logging, configparser
Session Log
| Timestamp | Notes |
|---|---|
Start |
<Record when you started> |
End |
<Record when you finished> |