Skip to content

Make Fetcher Protocol generic on the media type#163

Merged
IrishPrime merged 1 commit into
mainfrom
161/generic-fetcher-protocol
May 9, 2026
Merged

Make Fetcher Protocol generic on the media type#163
IrishPrime merged 1 commit into
mainfrom
161/generic-fetcher-protocol

Conversation

@IrishPrime

Copy link
Copy Markdown
Owner

Parameterize Fetcher on the media subtype it handles so subclass-specific implementations (currently TVMaze, future fetchers for other media types) can declare narrower fetch signatures without failing type checks.

The previous declaration:

class Fetcher(Protocol):
    def fetch(self, media: Media) -> None: ...

required every implementation to accept any Media. TVMaze.fetch narrows to TV, which means it does not satisfy the protocol; passing a generic Media to TVMaze.fetch would be a type error. mypy was (correctly) flagging this with a [arg-type] error at the PROCESSOR_FACTORIES dict literal.

The new declaration uses Python 3.12 generic class syntax:

class Fetcher[M: Media](Protocol):
    def fetch(self, media: M) -> None: ...

Processor and ProcessorFactory are parameterized to match, so the type relationship between a factory's media_type and its fetcher is now expressed in the type system instead of being implicit. The heterogeneous PROCESSOR_FACTORIES dict uses ProcessorFactory[Any] as its value type.

Internally, Processor.process now returns M instead of Media (callers see a narrower type at no cost) and the local annotation on media was tightened from Media to M so the narrowing carries through to self.fetcher.fetch(media).

Resolve #161.

Parameterize `Fetcher` on the media subtype it handles so
subclass-specific implementations (currently `TVMaze`, future fetchers
for other media types) can declare narrower `fetch` signatures without
failing type checks.

The previous declaration:

    class Fetcher(Protocol):
        def fetch(self, media: Media) -> None: ...

required every implementation to accept any `Media`. `TVMaze.fetch`
narrows to `TV`, which means it does not satisfy the protocol; passing
a generic `Media` to `TVMaze.fetch` would be a type error. `mypy` was
(correctly) flagging this with a `[arg-type]` error at the
`PROCESSOR_FACTORIES` `dict` literal.

The new declaration uses Python 3.12 generic class syntax:

    class Fetcher[M: Media](Protocol):
        def fetch(self, media: M) -> None: ...

`Processor` and `ProcessorFactory` are parameterized to match, so the
type relationship between a factory's `media_type` and its `fetcher` is
now expressed in the type system instead of being implicit. The
heterogeneous `PROCESSOR_FACTORIES` `dict` uses `ProcessorFactory[Any]`
as its value type.

Internally, `Processor.process` now returns `M` instead of `Media`
(callers see a narrower type at no cost) and the local annotation on
`media` was tightened from `Media` to `M` so the narrowing carries
through to `self.fetcher.fetch(media)`.

Resolve #161.
@IrishPrime IrishPrime self-assigned this May 9, 2026
@IrishPrime IrishPrime merged commit bad25ad into main May 9, 2026
3 checks passed
@IrishPrime IrishPrime deleted the 161/generic-fetcher-protocol branch May 9, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Fetcher Protocol generic on the media type

1 participant