Skip to content

Refactor feed types to use strongly-typed dataclasses (PP-2728)#3025

Merged
jonathangreen merged 11 commits intomainfrom
chore/feed-types-refactoring
Feb 5, 2026
Merged

Refactor feed types to use strongly-typed dataclasses (PP-2728)#3025
jonathangreen merged 11 commits intomainfrom
chore/feed-types-refactoring

Conversation

@jonathangreen
Copy link
Member

@jonathangreen jonathangreen commented Feb 4, 2026

Description

Refactors the feed types system to use strongly-typed dataclasses instead of the dynamic FeedEntryType base class. This replaces arbitrary attribute handling with explicit, typed fields for better IDE support, type checking, and maintainability.

Key changes:

  • Replace FeedEntryType and BaseModel with dedicated dataclasses: Series, Rating, Category, Distribution, PatronData, DRMLicensor, RichText
  • Convert attribute names from camelCase to snake_case throughout
  • Add slots=True to all dataclasses for memory efficiency
  • Replace dynamic add_attributes() / getattr() patterns with direct attribute access
  • Add TypedDict definitions (LinkKwargs, LinkAttributes) for typed kwargs
  • Convert DataEntryTypes to StrEnum
  • Add docstrings to all new types

Motivation and Context

This refactoring lays the groundwork for updating OPDS2 output to use Pydantic models. The existing FeedEntryType pattern with dynamic attributes made type checking impossible and the code difficult to understand. The new strongly-typed approach enables proper mypy validation and IDE autocompletion.

Relates to: PP-2728

How Has This Been Tested?

  • Existing test suite updated to use new types
  • All feed serializer tests pass
  • Type checking with mypy

Checklist

  • I have updated the documentation accordingly.
  • All new and existing tests passed.

@codecov
Copy link

codecov bot commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 94.02985% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.02%. Comparing base (b936a77) to head (b97c8eb).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/palace/manager/feed/serializer/opds.py 91.20% 3 Missing and 5 partials ⚠️
src/palace/manager/feed/annotator/circulation.py 93.97% 2 Missing and 3 partials ⚠️
src/palace/manager/feed/annotator/base.py 88.88% 1 Missing and 1 partial ⚠️
src/palace/manager/feed/annotator/loan_and_hold.py 83.33% 1 Missing and 1 partial ⚠️
src/palace/manager/feed/serializer/opds2.py 88.88% 0 Missing and 2 partials ⚠️
src/palace/manager/feed/acquisition.py 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3025      +/-   ##
==========================================
+ Coverage   93.00%   93.02%   +0.02%     
==========================================
  Files         479      479              
  Lines       43502    43527      +25     
  Branches     6047     6041       -6     
==========================================
+ Hits        40461    40493      +32     
+ Misses       1970     1965       -5     
+ Partials     1071     1069       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jonathangreen jonathangreen requested a review from a team February 4, 2026 18:00
Copy link
Contributor

@tdilauro tdilauro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 🚀

One minor typo/wordo. Also noticed that there might be some small type coverage gaps, but those may have already been in there and just swept up in this change.

jonathangreen and others added 11 commits February 5, 2026 09:08
Replace the dynamic FeedEntryType class with specific dataclasses for
improved type safety and IDE support:

- Add TextValue, Category, Rating, Series, Distribution, PatronData,
  and DRMLicensor dataclasses to replace generic FeedEntryType usage
- Add LicenseInfo and DrmInfo dataclasses in circulation annotator
- Change Link to inherit from BaseModel instead of FeedEntryType
- Add explicit facet attributes to Link (facetGroup, activeFacet, etc.)
- Use boolean values for activeFacet/defaultFacet instead of strings
- Remove FeedEntryType.create() factory and add_attributes() methods
- Update serializers to handle new type structure
- Update all tests for new type assertions
Update dataclass field names to follow Python naming conventions:
- facetGroup → facet_group
- activeFacet → active_facet
- defaultFacet → default_facet
- ratingValue → rating_value
- additionalType → additional_type
- clientToken → client_token
- authorizationIdentifier → authorization_identifier
- And other camelCase attributes

The serializers now handle the conversion to camelCase for OPDS output.
Update the type hint for DataEntry.type to use the new enum.
Co-authored-by: Tim DiLauro <tdilauro@users.noreply.github.com>
@jonathangreen jonathangreen force-pushed the chore/feed-types-refactoring branch from 572eecd to b97c8eb Compare February 5, 2026 13:08
@jonathangreen jonathangreen enabled auto-merge (squash) February 5, 2026 13:09
@jonathangreen jonathangreen merged commit 985e404 into main Feb 5, 2026
19 checks passed
@jonathangreen jonathangreen deleted the chore/feed-types-refactoring branch February 5, 2026 13:17
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.

2 participants