Skip to content

Allow extensions to register custom Click parameter types for Runner API#3005

Open
talsperre wants to merge 2 commits intomasterfrom
dev/click-type-extensions
Open

Allow extensions to register custom Click parameter types for Runner API#3005
talsperre wants to merge 2 commits intomasterfrom
dev/click-type-extensions

Conversation

@talsperre
Copy link
Collaborator

Summary

  • Move the hardcoded click_to_python_types dict from click_api.py into a get_click_to_python_types() function in metaflow_config.py
  • Extensions can now provide their own get_click_to_python_types to add custom type mappings (e.g. S3Type, TableType, DateType) which get merged with the base types
  • This enables the Runner/Deployer API to work with custom Click parameter types defined by extensions

Test plan

  • Verify existing Runner/Deployer tests pass (type map is functionally identical)
  • Verify extensions providing get_click_to_python_types have their types merged correctly

🤖 Generated with Claude Code

Move click_to_python_types mapping from click_api.py into a
get_click_to_python_types() function in metaflow_config.py. Extensions
can provide their own get_click_to_python_types to add custom type
mappings (e.g. S3Type, TableType) which get merged with the base types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR refactors the hardcoded click_to_python_types mapping in metaflow/runner/click_api.py into an extensible get_click_to_python_types() function in metaflow_config.py, allowing third-party extensions to contribute custom Click parameter type mappings that get merged at import time. The approach is consistent with how other extension hooks (e.g. get_pinned_conda_libs) already work in the config extension system.

Key changes:

  • JSON type alias is now defined once in metaflow_config.py and re-exported, eliminating the previous dual-definition issue in click_api.py
  • click_to_python_types in click_api.py is now populated by calling get_click_to_python_types() at module load time, picking up any extension-merged version
  • Two issues were found: _new_get_click_to_python_types is not listed in the finally cleanup block (leaking the name into the module namespace), and extension mappings can silently overwrite base type mappings without any warning

Confidence Score: 3/5

  • Safe to merge with one minor namespace-leak fix and a documentation/warning improvement for the extension merge strategy.
  • The core logic is sound and consistent with existing extension patterns. The missing cleanup entry for _new_get_click_to_python_types in the finally block is a real (but minor) bug — it leaves an unexpected name in the module namespace, which is the opposite of the block's intent. The silent override issue is a best-practice concern rather than a functional bug. No tests are included in the PR.
  • metaflow/metaflow_config.py — specifically the finally cleanup block and the extension merge function

Important Files Changed

Filename Overview
metaflow/metaflow_config.py Adds get_click_to_python_types() function and extension-merging logic. The temporary name _new_get_click_to_python_types is not cleaned up in the finally block, leaking into the module namespace. Extension mappings also silently override base types without any warning.
metaflow/runner/click_api.py Cleanly removes the hardcoded click_to_python_types dict and delegates to get_click_to_python_types() from metaflow_config. The JSON type is now sourced from a single location, resolving the previous dual-definition concern. No issues found in this file.

Sequence Diagram

sequenceDiagram
    participant CA as click_api.py
    participant MC as metaflow_config.py
    participant EXT as Extension module

    Note over MC: Module-level: define JSON type alias
    Note over MC: Define get_click_to_python_types()
    MC->>EXT: Load extension config modules
    EXT-->>MC: Provide get_click_to_python_types()
    Note over MC: Wrap into _new_get_click_to_python_types<br/>merging base + extension dicts (d1.update(d2))
    Note over MC: globals()[get_click_to_python_types] = wrapped fn

    CA->>MC: from metaflow_config import JSON, get_click_to_python_types
    MC-->>CA: Returns merged get_click_to_python_types (and JSON)
    Note over CA: click_to_python_types = get_click_to_python_types()
    Note over CA: Dict frozen at import time with all type mappings

    CA->>CA: get_annotation(param) uses click_to_python_types
    CA->>CA: _method_sanity_check() uses JSON for equality check
Loading

Comments Outside Diff (1)

  1. metaflow/metaflow_config.py, line 739-746 (link)

    _new_get_click_to_python_types leaks into module namespace

    The finally block is designed to erase all temporary names introduced during extension loading, and it already includes _new_get_pinned_conda_libs. However, _new_get_click_to_python_types (defined via def at line 717) is not listed here. After the try/finally completes, the name _new_get_click_to_python_types will remain as a module-level attribute on metaflow_config, which is inconsistent with the intent of this cleanup block.

Last reviewed commit: c8e4a81

…imports

Move JSON type alias to module-level in metaflow_config.py so it is
defined in one place. Import it in click_api.py instead of redefining.
Add comment explaining why get_click_to_python_types uses local imports
(circular dependency: metaflow_config -> includefile -> ... -> debug ->
metaflow_config).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment on lines +717 to +723
def _new_get_click_to_python_types(f1=globals()[n], f2=o):
d1 = f1()
d2 = f2()
d1.update(d2)
return d1

globals()[n] = _new_get_click_to_python_types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extension types silently shadow base type mappings

The merge uses d1.update(d2), meaning extension-provided mappings unconditionally overwrite base mappings. If an extension accidentally re-maps an existing Click type (e.g. StringParamType) to a different Python type, it will silently change behaviour with no warning.

For comparison, the get_pinned_conda_libs merge at least concatenates conflicting version strings rather than performing a silent overwrite. Consider logging or warning when an extension key collides with a base key, to make such overrides visible and intentional.

)
from metaflow.parameters import flow_context
from metaflow.user_configs.config_options import (
ConfigValue,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ConfigValue needed now in this file? Otherwise LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants