Skip to content

Commit

Permalink
Fix type annotations on template decorators
Browse files Browse the repository at this point in the history
Use `Concatenate` to add the missing `self` parameter to the class methods
annotated with `@_add_type_hints` in `TemplateDecoratorFuncsMixin`. Without
this, mypy raises a `misc` error on usage, complaining that the method "does
not accept self argument".

Signed-off-by: Alice Purcell <alicederyn@gmail.com>
  • Loading branch information
alicederyn committed Sep 25, 2024
1 parent 6dea908 commit 02f7c4e
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 3 deletions.
9 changes: 6 additions & 3 deletions src/hera/workflows/_meta_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
if sys.version_info >= (3, 10):
from inspect import get_annotations
from types import NoneType
from typing import Concatenate, ParamSpec
else:
from hera.shared._inspect import get_annotations
from typing_extensions import Concatenate, ParamSpec

NoneType = type(None)


from typing_extensions import ParamSpec

from hera.shared import BaseMixin, global_config
from hera.shared._global_config import _DECORATOR_SYNTAX_FLAG, _flag_enabled
from hera.shared._pydantic import BaseModel, get_fields, root_validator
Expand Down Expand Up @@ -474,7 +474,10 @@ def _add_type_hints(
) -> Callable[
...,
Callable[
PydanticKwargs, # this adds type hints to the underlying *library* function kwargs
Concatenate[
object, # this is the `self` parameter
PydanticKwargs, # this adds type hints to the underlying *library* function kwargs
],
Callable[ # we will return a function that is a decorator
[Callable[FuncIns, FuncR]], # taking underlying *user* function
Callable[FuncIns, FuncR], # and returning it
Expand Down
55 changes: 55 additions & 0 deletions tests/typehints/test_template_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from pathlib import Path
from subprocess import run
from tempfile import TemporaryDirectory
from textwrap import dedent
from typing import Iterator, Tuple

import pytest

from hera.workflows import Step, Task

COMMON_SETUP = """
from hera.shared import global_config
from hera.workflows import Input, Output, Workflow
w = Workflow()
"""


def run_mypy(python_code: str):
with TemporaryDirectory() as d:
python_file = Path(d) / "example.py"
python_file.write_text(python_code)
mypy_cmd = ["mypy", "--config-file", "tests/typehints/test-mypy.toml", str(python_file)]
result = run(mypy_cmd, check=False, capture_output=True, encoding="utf-8")
if result.returncode != 0:
msg = f"Error calling {' '.join(mypy_cmd)}:\n{result.stderr}{result.stdout}"
raise AssertionError(msg)
return result.stdout.replace(d, "")


def test_script_decoration_no_arguments():
"""Verify a script can be decorated with no extra arguments."""
SCRIPT = """
@w.script()
def simple_script(_: Input) -> Output:
return Output()
reveal_type(simple_script(Input()))
"""
result = run_mypy(COMMON_SETUP + dedent(SCRIPT))
assert 'Revealed type is "hera.workflows.io.v2.Output"' in result


def test_script_decoration_accepts_name_argument():
"""Verify the script decorator can be passed a name."""
SCRIPT = """
@w.script(name = "some_script")
def simple_script(_: Input) -> Output:
return Output()
reveal_type(simple_script(Input()))
"""
result = run_mypy(COMMON_SETUP + dedent(SCRIPT))
assert 'Revealed type is "hera.workflows.io.v2.Output"' in result

0 comments on commit 02f7c4e

Please sign in to comment.