Skip to content

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
def Item(
    handler: Type[T] | partial[T],
    use_key: Optional[bool] = False,
    only_consider: Optional[type] = None
):
    """
    Modifier to ensure the handler is run in ITEM mode.

    In ITEM mode, the handler is applied to each item in an iterable.

    Args:
        handler (Type[T] | partial[T]): The handler class or partial to modify.
        use_key (Optional[bool]): If True, the handler uses the item's key (e.g., in a dictionary) instead of value.
        only_consider (Optional[type]): Specific type to filter items for processing.

    Returns:
        partial: A partial application of the handler with ITEM mode settings.
    """
    return partial(
        handler,
        _mode=HandlerMode.ITEM,
        _item_use_key=use_key,
        _preferred_value_type=only_consider
    )

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
def Context(handler: Type[T]):
    """
    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.

    Args:
        handler (Type[T]): The handler class to modify.

    Returns:
        partial: A partial application of the handler with _mode=HandlerMode.CONTEXT.
    """
    return partial(handler, _mode=HandlerMode.CONTEXT)

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