Summary: Two Approaches for Type Matching¶
I’ve successfully implemented both approaches you asked about. Here’s a comprehensive comparison:
Approach 1: Inheritance-Based (Current Implementation)¶
How it works:
@presenter
def images(att: Attachment, pil_image: 'PIL.Image.Image') -> Attachment:
"""Uses inheritance: PngImageFile inherits from PIL.Image.Image"""
# Implementation...
Advantages:
- ✅ Pythonic: Uses Python’s built-in inheritance system
- ✅ Robust: Works automatically for all subclasses
- ✅ No regex knowledge needed: Contributors just use normal type annotations
- ✅ IDE support: Type hints work naturally
- ✅ Future-proof: New PIL image formats automatically work
How inheritance works in our context:
# PIL Image inheritance chain:
PngImageFile → ImageFile → Image → object
JpegImageFile → ImageFile → Image → object
GifImageFile → ImageFile → Image → object
# isinstance(png_obj, PIL.Image.Image) returns True for all!
Approach 2: Regex-Based (Also Implemented)¶
How it works:
@presenter
def images(att: Attachment, pil_image: r'.*ImageFile$') -> Attachment:
"""Uses regex: matches PngImageFile, JpegImageFile, etc."""
# Implementation...
Advantages:
- ✅ Flexible: Can match complex patterns
- ✅ No imports needed: Works without importing the actual classes
- ✅ Pattern-based: Can match multiple unrelated types with one pattern
Examples of regex patterns:
# Match any PIL image type
pil_image: r'.*ImageFile$'
# Match pandas DataFrames
df: r'.*DataFrame$'
# Match BeautifulSoup objects
soup: r'.*BeautifulSoup$'
# Match multiple types
data: r'(DataFrame|Series|ndarray)$'
My Recommendation: Use Inheritance¶
For the attachments library, I recommend sticking with the inheritance approach for these reasons:
1. Better Developer Experience¶
# Inheritance (recommended)
@presenter
def images(att: Attachment, pil_image: 'PIL.Image.Image') -> Attachment:
# Clear, standard Python type annotation
# vs Regex (more complex)
@presenter
def images(att: Attachment, pil_image: r'.*ImageFile$') -> Attachment:
# Requires regex knowledge
2. Automatic Future Compatibility¶
When PIL adds new image formats (like WebpImageFile
), inheritance automatically works:
# Inheritance: automatically works ✅
isinstance(webp_obj, PIL.Image.Image) # True
# Regex: needs pattern update ❌
r'.*ImageFile$' # Might not match new formats
3. Type Safety¶
# Inheritance: IDE knows the exact type
def images(att: Attachment, pil_image: 'PIL.Image.Image') -> Attachment:
pil_image.save(...) # IDE autocomplete works
# Regex: IDE doesn't know what type it is
def images(att: Attachment, pil_image: r'.*ImageFile$') -> Attachment:
pil_image.save(...) # No autocomplete
When to Use Regex Patterns¶
Regex patterns are useful for edge cases where inheritance doesn’t work:
# When you need to match multiple unrelated types
@presenter
def data_summary(att: Attachment, obj: r'(DataFrame|Series|ndarray)$') -> Attachment:
# Matches pandas DataFrame, Series, and numpy ndarray
# When you need partial matching
@presenter
def web_content(att: Attachment, obj: r'.*Soup.*') -> Attachment:
# Matches BeautifulSoup, NavigableString, etc.
The Enhanced Type Dispatch System¶
The system now supports both approaches automatically:
- Detects regex patterns:
r'pattern'
or patterns with metacharacters - Falls back to inheritance: For normal type strings like
'PIL.Image.Image'
- Provides multiple matching strategies: Exact match → Class name match → Inheritance → Regex
This gives contributors the flexibility to choose the best approach for their specific use case, while keeping the simple inheritance approach as the default.