Guide Python Intermediate

9.16 Hands-on Exercises

Practice programs reinforcing Python string concepts -- a log-level analyzer, an IP address extractor, a config file parser, and a password strength checker.

3 min read

Log Level Analyzer

Build a function that scans a batch of log lines and tallies how many fall into each severity level — an at-a-glance health summary without opening a log viewer.

import re
from collections import Counter

def analyze_log_levels(lines):
    levels = [re.search(r"\b(INFO|WARNING|ERROR)\b", l).group(1) for l in lines]
    return dict(Counter(levels))

>>> analyze_log_levels([
...     "2026-07-13 10:00:01 INFO Service started",
...     "2026-07-13 10:00:05 ERROR Connection refused",
...     "2026-07-13 10:00:07 WARNING High memory usage",
... ])
{'INFO': 1, 'ERROR': 1, 'WARNING': 1}

IP Address Extractor

Pull every IPv4 address out of free-form text — the first step in building a blocklist, an allowlist audit, or a geo-lookup report from security logs.

>>> text = "Connections from 192.168.1.10 and 10.0.0.5 were blocked; 8.8.8.8 allowed"
>>> re.findall(r"\b(?:\d{1,3}\.){3}\d{1,3}\b", text)
['192.168.1.10', '10.0.0.5', '8.8.8.8']

Configuration File Parser

Read a simple key=value config format, with comment lines starting with # — a recurring task for any tool that needs its own settings file without pulling in a full config-parsing library.

def parse_config(text):
    result = {}
    for line in text.splitlines():
        line = line.strip()
        if not line or line.startswith("#"):
            continue
        key, _, value = line.partition("=")
        result[key] = value
    return result

>>> parse_config("# config\nhost=localhost\nport=8080\n\ndebug=true")
{'host': 'localhost', 'port': '8080', 'debug': 'true'}

Password Strength Checker

Enforce a minimum length plus a mix of character classes — a standard first line of defense against weak passwords during account signup.

def is_strong_password(pw):
    if len(pw) < 8:
        return False
    has_upper = any(c.isupper() for c in pw)
    has_lower = any(c.islower() for c in pw)
    has_digit = any(c.isdigit() for c in pw)
    has_special = any(not c.isalnum() for c in pw)
    return all([has_upper, has_lower, has_digit, has_special])

>>> is_strong_password("Weak1")
False
>>> is_strong_password("Str0ng!Pass")
True

Mini Projects

  • DevOps health report generator — turn raw HTTP status-code counts into a single human-readable error-rate line ("1000 requests, 2.0% error rate"), reusing Counter from earlier in this chapter.
  • CI/CD build summarizer — parse a Jenkins-style console line ("Build #42 SUCCESS in 3m 15s") into a structured {"build", "status", "duration"} result, ready to feed a Slack notification.
  • Kubernetes log analyzer — combine the pod-name parser from 9.12 Strings in DevOps and AWS with the log-level counter above to produce a per-deployment error-rate summary.

Quick Interview Answer

“These exercises tie the chapter’s tools together: the log analyzer combines regex extraction with Counter; the IP extractor is a single well-chosen regex; the config parser leans on partition() instead of a fragile manual split; and the password checker chains several is* validation methods with any(). The common thread is picking the right existing tool — regex, Counter, partition, is* — over hand-rolled character-by-character logic.”

Common Mistakes

  • Using split("=") instead of partition("=") in the config parser — split() breaks on a value that itself contains an = character, while partition() splits only on the first occurrence.
  • Forgetting the password checker’s has_special check needs not c.isalnum(), not a hardcoded set of symbols, so it correctly accepts any real special character.
  • Letting one malformed log line crash the whole analyzer instead of skipping or flagging lines that don’t match the expected pattern (re.search(...) returning None).

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form