leroyvn opened a new issue, #1409:
URL: https://github.com/apache/hamilton/issues/1409

   # Current behavior
   
   My project uses Hamilton to manage a DAG-based processing pipeline that 
mainly manipulates xarray datasets. The DAG terminal nodes change based on user 
configuration. To implement this, I use the `resolve` decorator, combined with 
an `extract_field` wrapper that implements field selection based on 
configuration. The following reproducer illustrates this:
   
   **definitions.py**
   
   ```python
   import hamilton
   from hamilton.function_modifiers import ResolveAt, extract_fields, resolve
   
   hamilton.enable_power_user_mode = True
   
   
   def _extract_fields(field_name: str):
       return extract_fields({field_name: int})
   
   
   @resolve(when=ResolveAt.CONFIG_AVAILABLE, decorate_with=_extract_fields)
   def a() -> dict:
       return {"b": 1, "c": 2}
   ```
   
   **main.py** (I actually run this in a notebook)
   
   ```python
   import definitions
   
   from hamilton import driver
   
   drv = (
       driver.Builder()
       .with_config({"hamilton.enable_power_user_mode": True, "field_name": 
"b"})
       .with_modules(definitions)
       .build()
   )
   
   drv.display_all_functions()
   ```
   
   With Hamilton v1.88 and old, that runs just fine. With Hamilton v1.89.0, it 
raises an `AttributeError` with the following stack trace:
   
   <details><summary><strong>Stack trace</strong></summary>
   
   ```
   ---------------------------------------------------------------------------
   AttributeError                            Traceback (most recent call last)
   Cell In[1], line 6
         1 import definitions
         3 from hamilton import driver
         5 drv = (
   ----> 6     driver.Builder()
         7     .with_config({"hamilton.enable_power_user_mode": True, 
"field_name": "b"})
         8     .with_modules(definitions)
         9     .build()
        10 )
        12 drv.display_all_functions()
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/driver.py:2189,
 in Builder.build(self)
      2182     grouping_strategy = self.grouping_strategy or 
grouping.GroupByRepeatableBlocks()
      2183     graph_executor = TaskBasedGraphExecutor(
      2184         execution_manager=execution_manager,
      2185         grouping_strategy=grouping_strategy,
      2186         adapter=lifecycle_base.LifecycleAdapterSet(*adapter),
      2187     )
   -> 2189 return Driver(
      2190     self.config,
      2191     *self.modules,
      2192     adapter=adapter,
      2193     _materializers=self.materializers,
      2194     _graph_executor=graph_executor,
      2195     _use_legacy_adapter=False,
      2196     allow_module_overrides=self._allow_module_overrides,
      2197 )
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/driver.py:492,
 in Driver.__init__(self, config, adapter, allow_module_overrides, 
_materializers, _graph_executor, _use_legacy_adapter, *modules)
       490     error = telemetry.sanitize_error(*sys.exc_info())
       491     logger.error(SLACK_ERROR_MESSAGE)
   --> 492     raise e
       493 finally:
       494     # TODO -- update this to use the lifecycle methods
       495     self.capture_constructor_telemetry(error, modules, config, 
adapter)
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/driver.py:466,
 in Driver.__init__(self, config, adapter, allow_module_overrides, 
_materializers, _graph_executor, _use_legacy_adapter, *modules)
       464 self.graph_modules = modules
       465 try:
   --> 466     self.graph = graph.FunctionGraph.from_modules(
       467         *modules,
       468         config=config,
       469         adapter=adapter,
       470         allow_module_overrides=allow_module_overrides,
       471     )
       472     if _materializers:
       473         materializer_factories, extractor_factories = 
self._process_materializers(
       474             _materializers
       475         )
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/graph.py:753,
 in FunctionGraph.from_modules(config, adapter, allow_module_overrides, 
*modules)
       734 @staticmethod
       735 def from_modules(
       736     *modules: ModuleType,
      (...)
       739     allow_module_overrides: bool = False,
       740 ):
       741     """Initializes a function graph from the specified modules. Note 
that this was the old
       742     way we constructed FunctionGraph -- this is not a public-facing 
API, so we replaced it
       743     with a constructor that takes in nodes directly. If you hacked 
in something using
      (...)
       750     :return: a function graph.
       751     """
   --> 753     nodes = create_function_graph(
       754         *modules, config=config, adapter=adapter, 
allow_module_overrides=allow_module_overrides
       755     )
       756     return FunctionGraph(nodes, config, adapter)
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/graph.py:188,
 in create_function_graph(config, adapter, fg, allow_module_overrides, *modules)
       186 # create non-input nodes -- easier to just create this in one loop
       187 for _func_name, f in functions:
   --> 188     for n in fm_base.resolve_nodes(f, config):
       189         if n.name in config:
       190             continue  # This makes sure we overwrite things if 
they're in the config...
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/function_modifiers/base.py:850,
 in resolve_nodes(fn, config)
       848 except Exception as e:
       849     logger.exception(_resolve_nodes_error(fn))
   --> 850     raise e
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/function_modifiers/base.py:843,
 in resolve_nodes(fn, config)
       841 node_transformers = 
function_decorators[NodeTransformer.get_lifecycle_name()]
       842 for dag_modifier in node_transformers:
   --> 843     nodes = dag_modifier.transform_dag(nodes, filter_config(config, 
dag_modifier), fn)
       844 function_decorators = 
function_decorators[NodeDecorator.get_lifecycle_name()]
       845 for node_decorator in function_decorators:
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/function_modifiers/base.py:532,
 in NodeTransformer.transform_dag(self, nodes, config, fn)
       530 nodes_to_keep = self.compliment(nodes, nodes_to_transform)
       531 out = list(nodes_to_keep)
   --> 532 out += self.transform_targets(nodes_to_transform, config, fn)
       533 return out
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/function_modifiers/base.py:584,
 in SingleNodeNodeTransformer.transform_targets(self, targets, config, fn)
       579 if len(targets) != 1:
       580     raise InvalidDecoratorException(
       581         f"Expected a single node to transform, but got 
{len(targets)}. {self.__class__} "
       582         f" can only operate on a single node, but multiple nodes 
were created by {fn.__qualname__}"
       583     )
   --> 584 return super().transform_targets(targets, config, fn)
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/function_modifiers/base.py:515,
 in NodeTransformer.transform_targets(self, targets, config, fn)
       513 out = []
       514 for node_to_transform in targets:
   --> 515     out += list(self.transform_node(node_to_transform, config, fn))
       516 return out
   
   File 
~/Documents/src/rayference/rtm/eradiate/.pixi/envs/dev/lib/python3.9/site-packages/hamilton/function_modifiers/expanders.py:918,
 in extract_fields.transform_node(self, node_, config, fn)
       914         return dict_generated
       916 output_nodes = [node_.copy_with(callabl=dict_generator)]
   --> 918 for field, field_type in self.resolved_fields.items():
       919     doc_string = base_doc  # default doc string of base function.
       921     # This extractor is constructed to avoid closure issues.
   
   AttributeError: 'extract_fields' object has no attribute 'resolved_fields'
   ```
   
   </details>
   
   Is this expected?
   
   ## Workaround
   
   *Based on a Claude Code suggestion*
   
   Is the following, updated code, fine?
   
   ```python
   import hamilton
   from hamilton.function_modifiers import ResolveAt, extract_fields, resolve
   
   hamilton.enable_power_user_mode = True
   
   
   def _extract_fields(field_name: str):
       # Create the decorator
       fields = {field_name: int}
       decorator = extract_fields(fields)
   
       # WORKAROUND for Hamilton v1.89:
       # The extract_fields decorator needs its resolved_fields and output_type
       # attributes set before it can be used with @resolve(decorate_with=...).
       # Normally these are set in validate(), but that hasn't been called yet.
       # We set them manually here by mimicking what validate() does.
       if hamilton.__version__ >= (1, 89, 0):
           from hamilton.function_modifiers.expanders import 
_determine_fields_to_extract
   
           decorator.output_type = dict
           decorator.resolved_fields = _determine_fields_to_extract(fields, 
dict)
   
       return decorator
   
   
   @resolve(when=ResolveAt.CONFIG_AVAILABLE, decorate_with=_extract_fields)
   def a() -> dict:
       return {"b": 1, "c": 2}
   ```
   
   ## Library & System Information
   
   * Python: 3.9
   * Hamilton: 1.89.0
   * System: macOS 14.7 and Ubuntu 24.04
   
   # Expected behavior
   
   With Hamilton 1.88, this runs without problem and I get the following graph:
   
   <img width="450" height="399" alt="Image" 
src="https://github.com/user-attachments/assets/e896bd39-da28-4eac-9561-b3a556946fb1";
 />


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to