Skip to content

Session 8: ConfigurationManager & File I/O

Week: 8 Element: ICTPRG430 Element 2.2 Duration: 4 hours Phase: Advanced OOP - Data Management


Session Introduction

In this session, you'll learn how to manage robot configurations through professional file I/O patterns. Building on your robot hierarchy from Session 6 (LineFollowerRobot, GripperRobot, ArmRobot), you'll design a ConfigurationManager class that separates configuration concerns from robot logic. This follows the separation of concerns principle—a professional design pattern where configuration management is its own responsibility, independent of robot behavior.

You'll support multiple configuration formats (JSON, XML, YAML), implement robust error handling, and integrate your ConfigurationManager with your S6 robot classes. This foundational work prepares you for Session 9, where you'll use ConfigurationManager with the Factory Pattern.


Learning Objectives

By the end of this session, you will be able to:

  • Implement file I/O operations using Python context managers safely
  • Parse and validate configuration data from multiple formats (JSON, XML, YAML)
  • Design a ConfigurationManager class that decouples configuration from robot logic
  • Handle file errors gracefully with appropriate exception handling
  • Integrate ConfigurationManager with your S6 robot hierarchy
  • Support dynamic robot configuration driven by external data files

Session Structure

  1. Theory - File I/O patterns and configuration formats
  2. Custom Exceptions & Logging - ConfigurationError hierarchy, Python logging module
  3. ConfigurationManager Design - Class design, format support, validation
  4. Hands-on Exercise - Build ConfigurationManager and integrate with robots
  5. Testing & Validation - Verify configuration loading with multiple formats
  6. Assessment Preparation - Ready for AT2-S8 submission

Session Slides

View the presentation slides for this session:

📊 Open Slides in New Window

The slides contain: - Why configuration files matter for robotics - File I/O fundamentals and context managers - Configuration formats: JSON, XML, YAML - Custom exception hierarchy for ConfigurationManager - Python logging for professional diagnostics - Separation of concerns with ConfigurationManager - Integration with S6 robot hierarchy - Testing strategies


Pre-Session Preparation

Required

  • Python 3.9+ installed
  • VS Code with Python extension
  • Completed Session 7 (Refactoring & OOP integration)

Review

  • Class design and inheritance from S4-6
  • Exception handling basics
  • Dictionary and list data structures

1. File I/O Fundamentals for Robotics

Why Configuration Files?

Robots need flexibility. Rather than hardcoding robot parameters (sensor count, max speed, gripper strength), configuration files allow:

  • Dynamic behavior - Change robot settings without recompiling code
  • Testing multiple configurations - Test different sensor counts, speeds, settings
  • Professional deployment - Non-programmers can adjust robot behavior
  • Data persistence - Save sensor readings, mission logs, calibration data

Context Managers & Safe File Handling

Always use context managers (with statement) for file operations:

# Correct: Context manager ensures file closes automatically
with open('robot_config.json', 'r') as file:
    config_data = json.load(file)

# Avoid: Manual file handling can leak resources if errors occur
file = open('robot_config.json', 'r')
config_data = json.load(file)  # If error here, file never closes!
file.close()

File Operation Patterns

# Reading configuration files
with open('config.json', 'r') as file:
    config = json.load(file)

# Writing configuration files (creates or overwrites)
with open('config.json', 'w') as file:
    json.dump(config, file, indent=2)

# Appending sensor logs
with open('sensor_log.txt', 'a') as file:
    file.write(f"[{timestamp}] {sensor_reading}\n")

Error Handling for File Operations

Build on Session 7

In Session 7 you stopped using print() for error handling and built custom exception hierarchies. Session 8 continues that — see sections 3 and 4 for the ConfigurationManager exception hierarchy and logging pattern. The snippet below shows the professional usage:

# ✅ Professional: use custom exceptions + logging (covered in sections 3 & 4)
try:
    config_mgr = ConfigurationManager('robot_config.json')
    config_mgr.validate()
except ConfigFileNotFoundError as e:
    logger.warning("No config file — using defaults. (%s)", e.file_path)
    config_mgr = ConfigurationManager.from_defaults()
except ConfigParseError as e:
    logger.error("Corrupted config file: %s", e)
    raise  # Surface this — don't silently swallow it
except ConfigValidationError as e:
    logger.error("Invalid config: %s", e)
    raise

2. Configuration File Formats

Robots often use structured data formats. Understanding their trade-offs is professional practice.

JSON (JavaScript Object Notation)

Pros: - Lightweight, human-readable - Native Python support via json module - Good for hierarchical data (nested objects) - Wide tool support

Example:

{
  "robot_id": "go2_explorer_01",
  "sensors": {
    "ir_count": 4,
    "has_camera": true
  },
  "movement": {
    "max_speed": 2.5,
    "battery_drain_per_meter": 1.2
  }
}

XML (eXtensible Markup Language)

Pros: - Self-documenting (tags describe data) - Widely used in robotics systems - Supports complex hierarchies - Schema validation possible

Example:

<?xml version="1.0" encoding="UTF-8"?>
<robot>
  <id>go2_explorer_01</id>
  <sensors>
    <ir_count>4</ir_count>
    <camera>true</camera>
  </sensors>
  <movement>
    <max_speed>2.5</max_speed>
    <battery_drain>1.2</battery_drain>
  </movement>
</robot>

YAML (YAML Ain't Markup Language)

Pros: - Very human-readable (minimal syntax) - Whitespace-based (clean appearance) - Good for configuration files - Python support via pyyaml package

Example:

robot_id: go2_explorer_01
sensors:
  ir_count: 4
  has_camera: true
movement:
  max_speed: 2.5
  battery_drain_per_meter: 1.2

Format Comparison

Feature JSON XML YAML
Human-readable Good Good Excellent
Parsing simplicity Easy Medium Easy
Hierarchical support Yes Yes Yes
Schema validation No Yes Limited
Python native support Yes Yes Via pyyaml
Robotics industry use Common Very common Growing

3. Custom Exception Hierarchy

In Session 7, you built a custom exception hierarchy for the coffee shop. Session 8 applies the same pattern to file-loading classes — this is how professionals signal what went wrong rather than printing a message and hoping.

Example: Sensor Calibration Reader

To see the pattern clearly, here is a teaching example using a different domain — a CalibrationReader that loads sensor calibration data from a file. Your exercise will apply the same structure to ConfigurationManager.

class CalibrationError(Exception):
    """Base exception for all calibration errors.

    All CalibrationReader exceptions inherit from this,
    allowing callers to catch broadly or specifically.
    """
    pass


class CalibrationFileNotFoundError(CalibrationError):
    """Raised when the calibration file cannot be found.

    Args:
        file_path: The path that was searched.
    """
    def __init__(self, file_path: str):
        self.file_path = file_path
        super().__init__(f"Calibration file not found: {file_path}")


class CalibrationParseError(CalibrationError):
    """Raised when a calibration file exists but cannot be parsed.

    Args:
        file_path: The file that failed to parse.
        reason: Human-readable description of the parse failure.
    """
    def __init__(self, file_path: str, reason: str):
        self.file_path = file_path
        self.reason = reason
        super().__init__(f"Failed to parse '{file_path}': {reason}")


class CalibrationValidationError(CalibrationError):
    """Raised when calibration data fails validation checks.

    Args:
        errors: List of validation error messages.
    """
    def __init__(self, errors: list):
        self.errors = errors
        error_list = "\n  - ".join(errors)
        super().__init__(f"Calibration validation failed:\n  - {error_list}")

Why Use a Hierarchy?

Catch broadly or narrowly — the caller decides:

# Catch ALL calibration problems in one place
try:
    cal = CalibrationReader('ir_sensor_cal.json')
    cal.validate()
except CalibrationError as e:
    logger.error("Calibration problem: %s", e)

# OR catch specific problems to handle differently
try:
    cal = CalibrationReader('ir_sensor_cal.json')
    cal.validate()
except CalibrationFileNotFoundError:
    cal = CalibrationReader.from_defaults()         # Fallback to defaults
except CalibrationParseError as e:
    logger.error("Corrupted calibration file: %s", e.file_path)
    raise                                           # Re-raise — needs attention
except CalibrationValidationError as e:
    for error in e.errors:
        logger.warning("Calibration issue: %s", error)

The Pattern Across Domains

Notice how the structure is identical regardless of domain:

Coffee Shop (S7) Calibration Reader (this example) Your ConfigurationManager
CoffeeShopError CalibrationError ConfigurationError (you design)
OrderError CalibrationFileNotFoundError ConfigFileNotFoundError (you design)
PaymentError CalibrationParseError ConfigParseError (you design)
InsufficientPointsError CalibrationValidationError ConfigValidationError (you design)

Your task is to design the ConfigurationError hierarchy yourself — same pattern, applied to the robot configuration domain. Think about what can go wrong when loading a robot config file and what a caller would need to distinguish between.


4. Professional Logging

In Session 7 you stopped using print() for errors—you raised exceptions instead. Now meet Python's logging module: the professional tool for diagnostic messages (startup info, debug traces, status updates) that aren't errors.

Why Logging Instead of Print?

print() logging
Severity levels ❌ None ✅ DEBUG / INFO / WARNING / ERROR / CRITICAL
Timestamps ❌ Manual ✅ Automatic
Output control ❌ Always shows ✅ Toggle per level
Module filtering ❌ All or nothing ✅ Per-module control
Production-safe ❌ Clutters output ✅ Can silence without code change

Configuring a Logger

import logging

# Get a logger named after this module (best practice)
logger = logging.getLogger(__name__)

# Basic configuration — done once at the application entry point
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)

Log Levels in Practice

logger.debug("Loading config from: %s", self.config_file)                     # Dev detail
logger.info("Configuration loaded successfully (%d keys)", len(self.config))   # Normal ops
logger.warning("Optional field 'description' missing from config")             # Unusual but ok
logger.error("Failed to open config file: %s", self.config_file)              # Something broke
logger.critical("Config validation failed — robot cannot start")               # Fatal

CalibrationReader with Logging

Here is the CalibrationReader example class — showing how logging and exceptions combine in a real file-loading class. Your exercise will apply this same integration to ConfigurationManager.

import logging
import json
import os

logger = logging.getLogger(__name__)


class CalibrationReader:
    """Loads sensor calibration data from a JSON file."""

    def __init__(self, cal_file: str) -> None:
        self.cal_file = cal_file
        self.data = {}
        logger.debug("CalibrationReader initialised: %s", cal_file)
        self.load()

    def load(self) -> None:
        """Load calibration data, raising typed exceptions on failure."""
        if not os.path.exists(self.cal_file):
            logger.error("Calibration file not found: %s", self.cal_file)
            raise CalibrationFileNotFoundError(self.cal_file)
        try:
            with open(self.cal_file, 'r') as f:
                self.data = json.load(f)
            logger.info("Calibration loaded: %s (%d keys)", self.cal_file, len(self.data))
        except json.JSONDecodeError as e:
            logger.error("Parse error in %s: %s", self.cal_file, e)
            raise CalibrationParseError(self.cal_file, str(e)) from e

    def validate(self) -> None:
        """Validate calibration data, raising CalibrationValidationError if invalid."""
        errors = []
        if 'sensor_name' not in self.data:
            errors.append("Missing required field: sensor_name")
        if 'channels' in self.data:
            if not isinstance(self.data['channels'], int):
                errors.append("channels must be an integer")
            elif self.data['channels'] <= 0:
                errors.append("channels must be greater than zero")
        if errors:
            logger.warning("Validation failed with %d error(s)", len(errors))
            raise CalibrationValidationError(errors)
        logger.debug("Calibration validation passed")

Notice how every method follows the same structure:

  • logger.debug() / logger.info() for normal operations
  • logger.error() / logger.warning() before raising an exception
  • Typed exceptions propagate to the caller — never silent failure

Key Rule: Exceptions for Errors, Logging for Diagnostics

Rule of Thumb

  • Raise exceptions when something is wrong and the caller needs to handle it
  • Log messages to record what happened for diagnostics and debugging
  • Never silently swallow errors — the robot will run with empty config and fail mysteriously
# ❌ Wrong: Silent failure
except FileNotFoundError:
    print("File not found")
    self.config = {}   # Robot runs with empty config!

# ✅ Right: Raise + log for full context
if not os.path.exists(self.config_file):
    logger.error("Config file not found: %s", self.config_file)
    raise ConfigFileNotFoundError(self.config_file)

5. Separation of Concerns Principle

This is the key design concept for Session 8.

Problem: Coupled Design

class LineFollowerRobot:
    def __init__(self, config_file):
        self.name = None
        self.ir_sensors = None
        # Robot handles BOTH behavior AND configuration
        self.load_config(config_file)  # ← Mixing concerns

    def load_config(self, config_file):
        # Robot class tied to specific file format
        with open(config_file) as f:
            config = json.load(f)
        self.name = config['name']
        self.ir_sensors = config['ir_count']

Problems: - Robot class knows about JSON file format - Can't change config format without changing robot code - Robot responsible for both behavior AND data loading - Testing requires actual files

Solution: Separation of Concerns

class ConfigurationManager:
    """Handles all configuration file operations."""

    def __init__(self, config_file):
        self.config_file = config_file
        self.config = self.load()

    def load(self):
        """Load configuration from file."""
        # ConfigurationManager handles file I/O, not robot

class LineFollowerRobot:
    """Focuses only on behavior."""

    def __init__(self, name, ir_sensors):
        self.name = name
        self.ir_sensors = ir_sensors

    def read_ir_sensors(self):
        """Behavior - independent of how config was loaded"""
        return [0 if i % 2 == 0 else 1 for i in range(self.ir_sensors)]

# Usage: Separate concerns
config_manager = ConfigurationManager('robot_config.json')
robot = LineFollowerRobot(
    name=config_manager.get('name'),
    ir_sensors=config_manager.get('sensors.ir_count')
)

Benefits: - Robot class focuses on behavior only - ConfigurationManager handles all file operations - Can change file format without touching robot code - Each class has single responsibility - Easier to test (mock ConfigurationManager)


6. ConfigurationManager Class Design

Core Responsibilities

A ConfigurationManager should:

  1. Load configurations from different file formats
  2. Parse and validate the loaded data
  3. Provide safe access to configuration values
  4. Handle errors gracefully (missing files, bad format, invalid data)
  5. Support querying with dot notation (e.g., get('sensors.ir_count'))

Suggested Interface

class ConfigurationManager:
    """Manages robot configurations from files."""

    def __init__(self, config_file):
        """Initialize with configuration file path."""
        self.config_file = config_file
        self.config = {}
        self.load()

    def load(self):
        """Load configuration from file (auto-detect format)."""
        # Determine format from file extension
        # Parse appropriate format
        # Store in self.config

    def get(self, key, default=None):
        """Safely retrieve configuration value.

        Supports dot notation: get('sensors.ir_count')
        Returns default if key not found.
        """

    def save(self, output_file=None):
        """Save current configuration to file."""
        # Save to file (auto-detect format from extension)

    def validate(self) -> None:
        """Validate configuration data.

        Raises:
            ConfigValidationError: If any validation checks fail.
        """
        # Check required fields exist
        # Check data types are correct
        # Check values are in valid ranges
        # Raise ConfigValidationError with all errors if any fail

Validation Pattern

Here is the same validation pattern applied to CalibrationReader — you will implement this pattern inside your own ConfigurationManager.validate():

def validate(self) -> None:
    """Validate calibration data — same pattern you will apply to ConfigurationManager."""
    errors = []

    # Check required fields exist
    if 'sensor_name' not in self.data:
        errors.append("Missing required field: sensor_name")

    # Check data types
    if 'channels' in self.data:
        if not isinstance(self.data['channels'], int):
            errors.append("channels must be integer")
        elif self.data['channels'] <= 0:
            errors.append("channels must be greater than zero")

    # Raise with all errors at once — don't stop at the first failure
    if errors:
        raise CalibrationValidationError(errors)

Key points to carry into your implementation:

  • Collect all errors before raising — don't fail on the first one
  • The exception message should list every problem
  • Calling code can then report all issues to the user at once

7. Hands-On Exercise: Build ConfigurationManager

Competency Outcomes

You will demonstrate competency when you can:

  • Design and implement a custom exception hierarchy for configuration errors (without copying the calibration example — your hierarchy, your names, your decisions)
  • Load a JSON configuration file using a context manager and handle file-not-found and parse errors with typed exceptions
  • Load at least one additional format (XML or YAML) with auto-detection from file extension
  • Retrieve a value using a simple key: config.get('robot_id')
  • Retrieve a nested value using dot notation: config.get('sensors.ir_count')
  • Return a default value when a key is absent: config.get('unknown', default=0)
  • Validate a loaded config and raise a typed exception listing all validation failures
  • Distinguish clearly between a parse error (bad syntax) and a validation error (valid syntax, wrong data)
  • Log at DEBUG, INFO, WARNING, and ERROR levels at appropriate points
  • Save a config back to file in the detected format
  • Load a robot config file and use it to instantiate a robot object from your S6 hierarchy

Deliverables

Submit these files:

  1. configuration_manager.py — your ConfigurationManager class and your exception hierarchy
  2. example_configs/ — at least 3 config files: one JSON, one in your second format, one for a different robot type
  3. test_configuration.py — your test suite (skeleton provided below)

Part A: Basic Implementation

Build a ConfigurationManager class that:

  1. Loads JSON and one other format (XML or YAML — your choice)
  2. Auto-detect format from file extension
  3. Raise your ConfigFileNotFoundError when file is missing
  4. Raise your ConfigParseError for corrupted or invalid files

  5. Supports value access

  6. Simple keys: get('robot_id')
  7. Nested keys with dot notation: get('sensors.ir_count')
  8. Default values: get('unknown_key', default=0)

  9. Validates configuration

  10. Required fields check
  11. Type validation
  12. Range validation (e.g., sensor count > 0)
  13. Raises your ConfigValidationError listing all failures

  14. Saves configuration

  15. Write back to file in detected format
  16. Pretty-printed for readability

Part B: Integration with S6 Robot Hierarchy

Create your own configuration files for at least two of your S6 robot types. Decide what fields each robot needs — there is no single correct answer.

Your goal is to load one of these files and use the data to instantiate the correct robot:

# This is the target — your code should achieve this result
config = ConfigurationManager('my_robot_config.json')
config.validate()
robot = LineFollowerRobot(
    name=config.get('name'),
    battery_level=config.get('battery_level'),
    ir_sensors=config.get('sensors.ir_count')
)

What config fields you define, and what validation rules you apply, are your design decisions.

Part C: Testing & Validation

Test your ConfigurationManager with the scenarios below. Use the skeleton provided — fill in the test bodies.

# test_configuration.py — fill in the test bodies

import unittest
import os
import json
from configuration_manager import ConfigurationManager


class TestConfigurationManagerLoad(unittest.TestCase):

    def setUp(self):
        """Create a minimal valid config file for use in tests."""
        self.valid_file = 'test_valid_config.json'
        with open(self.valid_file, 'w') as f:
            json.dump({'robot_id': 'test_robot', 'battery_level': 100}, f)

    def tearDown(self):
        """Remove test files created during tests."""
        if os.path.exists(self.valid_file):
            os.remove(self.valid_file)

    def test_load_valid_json(self):
        """ConfigurationManager loads a valid JSON file without error."""
        # YOUR CODE HERE
        pass

    def test_load_missing_file_raises_error(self):
        """ConfigFileNotFoundError raised when file does not exist."""
        # YOUR CODE HERE — hint: use self.assertRaises()
        pass

    def test_load_corrupted_file_raises_error(self):
        """ConfigParseError raised when file contains invalid JSON/XML/YAML."""
        # YOUR CODE HERE — create a temp file with invalid content, then load it
        pass


class TestConfigurationManagerGet(unittest.TestCase):

    def setUp(self):
        self.valid_file = 'test_get_config.json'
        with open(self.valid_file, 'w') as f:
            json.dump({
                'robot_id': 'test_robot',
                'sensors': {'ir_count': 4, 'threshold': 0.5}
            }, f)
        self.config = ConfigurationManager(self.valid_file)

    def tearDown(self):
        if os.path.exists(self.valid_file):
            os.remove(self.valid_file)

    def test_get_simple_key(self):
        """get() returns correct value for a top-level key."""
        # YOUR CODE HERE
        pass

    def test_get_nested_key_dot_notation(self):
        """get() returns correct value for a nested key using dot notation."""
        # YOUR CODE HERE — e.g., config.get('sensors.ir_count') should return 4
        pass

    def test_get_missing_key_returns_default(self):
        """get() returns the provided default when key does not exist."""
        # YOUR CODE HERE
        pass


class TestConfigurationManagerValidate(unittest.TestCase):

    def test_valid_config_passes_validation(self):
        """validate() does not raise when config data is valid."""
        # YOUR CODE HERE
        pass

    def test_missing_required_field_raises_validation_error(self):
        """ConfigValidationError raised when a required field is absent."""
        # YOUR CODE HERE — create a config missing a required field
        pass

    def test_invalid_field_type_raises_validation_error(self):
        """ConfigValidationError raised when a field has the wrong type."""
        # YOUR CODE HERE
        pass


if __name__ == '__main__':
    unittest.main()

8. Key Design Patterns

Separation of Concerns

  • ConfigurationManager: "How do we load and manage configuration data?"
  • Robot classes: "What behavior does each robot type have?"
  • Neither class needs to know about the other's implementation details

Type Hints for Clarity

from typing import Dict, Optional, Any

class ConfigurationManager:
    def __init__(self, config_file: str) -> None:
        """Initialize with configuration file path."""

    def get(self, key: str, default: Optional[Any] = None) -> Any:
        """Safely retrieve configuration value."""

    def validate(self) -> list:
        """Validate configuration. Returns list of error strings."""

Error Handling Strategy

def load(self) -> None:
    """Load configuration, raising typed exceptions on failure.

    Raises:
        ConfigFileNotFoundError: If the config file does not exist.
        ConfigParseError: If the file exists but cannot be parsed.
    """
    if not os.path.exists(self.config_file):
        logger.error("Config file not found: %s", self.config_file)
        raise ConfigFileNotFoundError(self.config_file)

    try:
        with open(self.config_file, 'r') as f:
            self.config = self._parse_file(f)
        logger.info("Loaded config: %s", self.config_file)
    except (json.JSONDecodeError, ValueError) as e:
        logger.error("Parse error in '%s': %s", self.config_file, e)
        raise ConfigParseError(self.config_file, str(e)) from e

9. Professional Standards

Configuration File Organization

Store configurations in a consistent directory:

my_robot_project/
├── src/
│   └── robot_config.py
├── configs/
│   ├── lineFollower_config.json
│   ├── gripper_config.xml
│   └── arm_config.yaml
├── tests/
│   └── test_configuration.py
└── README.md

Documentation

Document your ConfigurationManager:

class ConfigurationManager:
    """Manages robot configuration files.

    Supports JSON, XML, and YAML formats. Auto-detects format
    from file extension. Provides safe access to nested
    configuration values with validation.

    Example:
        config_mgr = ConfigurationManager('robot_config.json')
        robot_name = config_mgr.get('name')
        ir_count = config_mgr.get('sensors.ir_count', default=4)

        config_mgr.validate()  # Raises ConfigValidationError if invalid
        print("Configuration is valid")
    """

Check Your Understanding

Q1: Separation of Concerns

Why should ConfigurationManager be a separate class rather than part of the robot classes?

Answer
  • Robot classes focus on behavior (what the robot does)
  • ConfigurationManager focuses on data persistence (how configs are loaded)
  • Separating concerns makes code easier to test, modify, and reuse
  • Robot behavior doesn't change if we switch from JSON to YAML format

Q2: Nested Configuration Access

How would you support get('sensors.ir_count') for nested dictionaries?

Answer

Split the key by '.' and navigate through the nested structure:

def get(self, key, default=None):
    keys = key.split('.')
    value = self.config
    for k in keys:
        if isinstance(value, dict) and k in value:
            value = value[k]
        else:
            return default
    return value

Q3: Error Handling

What errors should ConfigurationManager handle gracefully?

Answer

Use custom exceptions (see Sections 3 & 4), not bare built-in exceptions:

  • ConfigFileNotFoundError: File doesn't exist — raise, let caller decide on fallback
  • ConfigParseError: File is corrupted/malformed — raise, this needs attention
  • ConfigValidationError: Data is invalid — raise with the full list of errors
  • Log context at each point with logger.error() or logger.warning()
  • Never silently swallow errors with except ...: self.config = {} — the robot will run with empty config and fail mysteriously later

Q4: Testing ConfigurationManager

How would you test ConfigurationManager without using actual files?

Answer

Use mock files or string buffers:

from io import StringIO

# Create mock config in memory
mock_config = StringIO('{"name": "test"}')
# Pass to ConfigurationManager for testing

Session Summary

You've learned:

  1. File I/O best practices - Context managers, safe resource cleanup
  2. Configuration file formats - JSON, XML, YAML comparison and trade-offs
  3. Custom exceptions - ConfigurationError hierarchy extending S7 patterns
  4. Professional logging - logging module, log levels, diagnostics vs errors
  5. Separation of concerns - Keep configuration management separate from robot behavior
  6. ConfigurationManager design - Professional configuration system class
  7. Validation and error handling - Robust, defensive programming with typed exceptions
  8. Integration with S6 hierarchy - Load robot configurations from files

This foundation prepares you for Session 9: Factory Pattern, where you'll use ConfigurationManager to elegantly manage robot creation.


Assessment: AT2-S7

See the AT2-S7-ConfigurationManager assessment document for submission requirements.


Navigation: ← Week 7 | Learning Plan | Week 9 →