Handler Modifiers
Handler modifiers are powerful tools that change how and where a handler operates. They allow you to apply validation, matching, or transformation logic to items within collections or to values from the pipeline context.
Available Modifiers
Item() - Process Collection Items
Apply a handler to each item in a list or dictionary.
Context() - Use Context Values
Use a value from the pipeline context as the handler's argument.
The Item() Modifier
The Item() modifier transforms a handler to operate on each element of a collection (list or dict) instead of the collection itself.
pipeline.handlers.base_handler.handler_modifiers.Item(handler, use_key=False, only_consider=None)
Modifier to ensure the handler is run in ITEM mode.
In ITEM mode, the handler is applied to each item in an iterable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler
|
Type[T] | partial[T]
|
The handler class or partial to modify. |
required |
use_key
|
Optional[bool]
|
If True, the handler uses the item's key (e.g., in a dictionary) instead of value. |
False
|
only_consider
|
Optional[type]
|
Specific type to filter items for processing. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
partial |
A partial application of the handler with ITEM mode settings. |
Source code in pipeline/handlers/base_handler/handler_modifiers.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
Example 1: Validate All Items in a List
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Item
# Validate that all emails in a list are valid
result = Pipe(
value=["john@example.com", "jane@example.com", "admin@company.org"],
type=list,
matches={
Item(Pipe.Match.Format.Email): None
}
).run()
print(result.value)
# ['john@example.com', 'jane@example.com', 'admin@company.org']
# If one email is invalid:
result = Pipe(
value=["john@example.com", "invalid-email", "admin@company.org"],
type=list,
matches={
Item(Pipe.Match.Format.Email): None
}
).run()
print(result.match_errors)
# [{1: {'id': 'email', 'msg': 'Invalid email address format.', 'value': 'invalid-email'}}]
Example 2: Transform All Items
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Item
# Capitalize all strings in a list
result = Pipe(
value=["hello", "world", "python"],
type=list,
transform={
Item(Pipe.Transform.Capitalize): None
}
).run()
print(result.value)
# ['Hello', 'World', 'Python']
Example 3: Using use_key with Dictionaries
The use_key parameter allows you to validate or transform dictionary keys instead of values.
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Item
# Validate that all dictionary keys match a pattern
data = {
"user_name": "John",
"user_email": "john@example.com",
"user_age": 30
}
result = Pipe(
value=data,
type=dict,
matches={
# Validate that all keys start with "user_"
Item(Pipe.Match.Regex.Search, use_key=True): r"^user_"
}
).run()
print(result.match_errors)
# [] - all keys match the pattern
# With invalid keys:
data_invalid = {
"user_name": "John",
"email": "john@example.com", # Missing "user_" prefix
"age": 30 # Missing "user_" prefix
}
result = Pipe(
value=data_invalid,
type=dict,
matches={
Item(Pipe.Match.Regex.Search, use_key=True): r"^user_"
}
).run()
print(result.match_errors)
# [
# {
# 'email': {
# 'id': 'search',
# 'msg': 'Invalid value. Valid pattern for value is ^user_.',
# 'value': 'email'
# },
# 'age': {
# 'id': 'search',
# 'msg': 'Invalid value. Valid pattern for value is ^user_.',
# 'value': 'age'
# }
# }
# ]
Example 4: Using only_consider for Type Filtering
The only_consider parameter allows you to apply a handler only to items of a specific type.
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Item
# Mixed-type list: only capitalize strings
result = Pipe(
value=["hello", 123, "world", 456, "python"],
type=list,
transform={
Item(Pipe.Transform.Multiply, only_consider=int): 10
}
).run()
print(result.value)
# ['hello', 1230, 'world', 4560, 'python']
# Only numbers were multiplied.
Mixed Types
By default, only_consider is set to None, which means it will consider all acceptable types for the handler. If the item's type is not acceptable, it will be skipped without any error.
Example 5: Complex Validation with Item()
from pipeline.core.pipeline.pipeline import Pipeline
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Item
# Validate a list of user objects
user_list_pipeline = Pipeline(
users={
"type": list,
"conditions": {
Pipe.Condition.MinLength: 1,
Pipe.Condition.MaxLength: 100
},
"matches": {
# Each item must be a valid email
Item(Pipe.Match.Format.Email): None
}
}
)
result = user_list_pipeline.run(data={
"users": [
"admin@company.com",
"user1company.com",
"user2@company.com"
]
})
print(result.errors)
# {
# "users": [
# {
# 1: {
# "id": "email",
# "msg": "Invalid email address format.",
# "value": "user1company.com",
# }
# }
# ]
# }
The Context() Modifier
The Context() modifier allows a handler to use a value from the pipeline context as its argument, enabling dynamic validation based on other fields.
pipeline.handlers.base_handler.handler_modifiers.Context(handler)
Modifier ensure the handler is run in CONTEXT mode.
In CONTEXT mode, the handler's argument is retrieved from the pipeline context using the provided argument as a key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler
|
Type[T]
|
The handler class to modify. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
partial |
A partial application of the handler with _mode=HandlerMode.CONTEXT. |
Source code in pipeline/handlers/base_handler/handler_modifiers.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Example 1: Password Confirmation
Validate that a password confirmation field matches the original password:
from pipeline.core.pipeline.pipeline import Pipeline
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Context
# Create a registration pipeline
registration_pipeline = Pipeline(
password={
"type": str,
"matches": {
Pipe.Match.Format.Password: Pipe.Match.Format.Password.STRICT
}
},
password_confirm={
"type": str,
"conditions": {
# Use the 'password' field from context as the comparison value
Context(Pipe.Condition.MatchesField): "password"
}
}
)
# Valid case: passwords match
result = registration_pipeline.run(data={
"password": "SecurePass123!",
"password_confirm": "SecurePass123!"
})
print(result.errors)
# None - passwords match
# Invalid case: passwords don't match
result = registration_pipeline.run(data={
"password": "SecurePass123!",
"password_confirm": "DifferentPass456!"
})
print(result.errors)
# {'password_confirm': [{'id': 'matches_field', 'msg': 'This must match the password field.', 'value': 'DifferentPass456!'}]}
Example 2: Dynamic Range Validation
Validate that a value is within a range defined by other fields:
from pipeline.core.pipeline.pipeline import Pipeline
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Context
# Product pricing pipeline
pricing_pipeline = Pipeline(
min_price={
"type": int,
"conditions": {Pipe.Condition.MinNumber: 0}
},
max_price={
"type": int,
"conditions": {Pipe.Condition.MinNumber: 0}
},
current_price={
"type": int,
"conditions": {
# Use min_price from context
Context(Pipe.Condition.MinNumber): "min_price",
# Use max_price from context
Context(Pipe.Condition.MaxNumber): "max_price"
}
}
)
# Valid case
result = pricing_pipeline.run(data={
"min_price": 10,
"max_price": 100,
"current_price": 50
})
print(result.errors)
# None - current_price is within range
# Invalid case
result = pricing_pipeline.run(data={
"min_price": 10,
"max_price": 100,
"current_price": 150 # Exceeds max_price
})
print(result.errors)
# {'current_price': [{'id': 'max_number', 'msg': 'Must be 100 or less.', 'value': 150}]}
Advanced Example: Nested Validation
from pipeline.core.pipeline.pipeline import Pipeline
from pipeline.core.pipe.pipe import Pipe
from pipeline.handlers.base_handler.handler_modifiers import Item
# Validate a complex nested structure
team_pipeline = Pipeline(
team_name={
"type": str,
"conditions": {Pipe.Condition.MaxLength: 50}
},
members={
"type": list,
"conditions": {
Pipe.Condition.MinLength: 1,
Pipe.Condition.MaxLength: 10,
# Each member must be a valid email
Item(Pipe.Match.Format.Email): None
},
"transform": {
# Lowercase all emails
Item(Pipe.Transform.Lowercase): None
}
}
)
result = team_pipeline.run(data={
"team_name": "Engineering",
"members": [
"Alice@Company.com",
"Bob@Company.com",
"Charlie@Company.com"
]
})
print(result.processed_data)
# {
# 'team_name': 'Engineering',
# 'members': ['alice@company.com', 'bob@company.com', 'charlie@company.com']
# }
Best Practices
When to Use Item()
- Validating all elements in a list or dictionary
- Transforming collection items uniformly
- Applying type-specific logic with
only_consider - Validating dictionary keys with
use_key
When to Use Context()
- Password confirmation fields
- Dynamic range validation
- Field interdependencies
- Conditional validation based on other fields
Context Availability
The Context() modifier requires that the referenced field exists in the pipeline context. If the field is missing, a HandlerModeMissingContextValue exception will be raised.
Error Reporting
When using Item(), errors include an index property indicating which item failed:
# For lists: index is an integer (0, 1, 2, ...)
# For dicts: index is the key name
Next Steps
- Learn about Pipeline Hooks for pre/post processing
- Explore Error Customization for custom error messages
- Check Examples for more real-world use cases