Skip to content

Commit

Permalink
add a "resolve from config" wrapper around the resolve decorator (#828)
Browse files Browse the repository at this point in the history
Add a small wrapper around resolve function modifier to "resolve from config".

This is a quality of life addition from Jan Hurst! Something he uses and helps
to cut down on the purposeful verbosity of the prior way.

* add resolve from config wrapper

* fix import lint

* add some docstrings
  • Loading branch information
janhurst authored Apr 22, 2024
1 parent 0772e04 commit 4a0694e
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 1 deletion.
1 change: 1 addition & 0 deletions hamilton/function_modifiers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@

resolve = delayed.resolve
ResolveAt = delayed.ResolveAt
resolve_from_config = delayed.resolve_from_config

# materialization stuff
load_from = adapters.load_from
Expand Down
15 changes: 15 additions & 0 deletions hamilton/function_modifiers/delayed.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,18 @@ def resolve(self, config: Dict[str, Any], fn: Callable) -> NodeTransformLifecycl
if key in config:
kwargs[key] = config[key]
return self.decorate_with(**kwargs)


class resolve_from_config(resolve):
"""Decorator class to delay evaluation of decorators until after the configuration is available.
Note: this is a power-user feature, and you have to enable power-user mode! To do so, you have
to add the configuration hamilton.enable_power_user_mode=True to the config you pass into the
driver.
This is a convenience decorator that is a subclass of `resolve` and passes
`ResolveAt.CONFIG_AVAILABLE` to the `when` argument such that the decorator is resoled at
compile time, E.G. when the driver is instantiated.
"""

def __init__(self, *, decorate_with: Callable[..., NodeTransformLifecycle]):
super().__init__(when=ResolveAt.CONFIG_AVAILABLE, decorate_with=decorate_with)
21 changes: 20 additions & 1 deletion tests/function_modifiers/test_delayed.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
import pytest

from hamilton import settings
from hamilton.function_modifiers import ResolveAt, base, extract_columns, resolve
from hamilton.function_modifiers import (
ResolveAt,
base,
extract_columns,
resolve,
resolve_from_config,
)

CONFIG_WITH_POWER_MODE_ENABLED = {
settings.ENABLE_POWER_USER_MODE: True,
Expand Down Expand Up @@ -58,6 +64,19 @@ def test_dynamic_resolves():
assert decorator_resolved.columns == ("a", "b")


def test_dynamic_resolve_with_configs():
decorator = resolve_from_config(
decorate_with=lambda cols_to_extract: extract_columns(*cols_to_extract),
)
decorator_resolved = decorator.resolve(
{"cols_to_extract": ["a", "b"], **CONFIG_WITH_POWER_MODE_ENABLED}, fn=test_dynamic_resolves
)
# This uses an internal component of extract_columns
# We may want to add a little more comprehensive testing
# But for now this will work
assert decorator_resolved.columns == ("a", "b")


def test_dynamic_fails_without_power_mode_fails():
decorator = resolve(
when=ResolveAt.CONFIG_AVAILABLE,
Expand Down

0 comments on commit 4a0694e

Please sign in to comment.