Skip to content

Manager Infrastructure Coupling #45

@plutopulp

Description

@plutopulp

Manager Infrastructure Coupling

Problem

DownloadManager.__aenter__() currently has too much knowledge about infrastructure concerns that aren't related to download orchestration:

  1. HTTP Client Creation - SSL context, certifi certificates, TCPConnector configuration
  2. Filesystem Operations - Creating directories with aiofiles

This makes the manager harder to:

  • Test in isolation (need to mock SSL, certifi, aiofiles)
  • Understand (infrastructure mixed with orchestration)
  • Maintain (changes to SSL/filesystem affect manager)
  • Reuse (client creation logic trapped in manager)

Current Code

async def __aenter__(self) -> "DownloadManager":
    # Filesystem concern
    await aiofiles.os.makedirs(self.download_dir, exist_ok=True)

    if self._client is None:
        # SSL/HTTP concern
        ssl_context = ssl.create_default_context(cafile=certifi.where())
        connector = aiohttp.TCPConnector(ssl=ssl_context)
        self._client = await aiohttp.ClientSession(connector=connector).__aenter__()
        self._owns_client = True

    await self.start_workers()
    return self

Proposed Solution

1. Extract HTTP Client Creation

Create src/rheo/infrastructure/http/__init__.py:

"""HTTP client utilities for secure HTTPS connections."""

import ssl
import aiohttp
import certifi


async def create_default_client() -> aiohttp.ClientSession:
    """Create ClientSession with secure defaults for all platforms.

    Configures SSL using certifi's certificate bundle to ensure
    proper HTTPS verification on all platforms (including macOS
    with Python 3.14+ where system certs may not be used by default).

    Returns:
        Configured ClientSession ready for HTTPS requests
    """
    ssl_context = ssl.create_default_context(cafile=certifi.where())
    connector = aiohttp.TCPConnector(ssl=ssl_context)
    return aiohttp.ClientSession(connector=connector)

Or consider creating a client factory class, as we will most likely want to support proxy support, auth helpers, custom timeout configuration etc..

2. Extract Filesystem Utilities

Create src/rheo/infrastructure/filesystem.py:

"""Filesystem utilities for async file operations."""

from pathlib import Path
import aiofiles.os


async def ensure_directory_exists(path: Path) -> None:
    """Ensure directory exists, creating it if necessary.

    Args:
        path: Directory path to create
    """
    await aiofiles.os.makedirs(path, exist_ok=True)

Or evaluate if attaching to settings class is appropriate as we will be injecting these in the manager in the future.

3. Simplify Manager

from ..infrastructure.http import create_default_client
from ..infrastructure.filesystem import ensure_directory_exists

class DownloadManager:
    async def __aenter__(self) -> "DownloadManager":
        """Enter async context manager."""
        await ensure_directory_exists(self.download_dir)

        if self._client is None:
            self._client = await create_default_client()
            await self._client.__aenter__()
            self._owns_client = True

        await self.start_workers()
        return self

References

  • Current code: src/rheo/downloads/manager.py lines 132-154
  • Related pattern: WorkerPool abstraction (already successfully extracted)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions