Skip to content

Type not yet supported: <class 'datetime.datetime'> when using freezegun #282

Description

@paxcodes

Describe the bug

Typer doesn't support freezegun's FakeDateTime. I am getting a RuntimeError: Type not yet supported: <class 'datetime.datetime'> when I use freezegun to freeze the time for testing purposes and have datetime type in my argument.

To Reproduce

  • Create a file main.py with:
from datetime import datetime
import typer

app = typer.Typer()

@app.command()
def data(
    year_month: datetime = typer.Argument(
        f"{datetime.today():%Y-%m}", formats=["%Y-%m"]
    )
):
    typer.echo(f"Data for {year_month:%Y-%b}")


if __name__ == "__main__":
    app()
  • Install pytest-freezegun
  • Have a test:
from typer.testing import CliRunner
from main import app

runner = CliRunner()

def test_data(freezer):
    freezer.move_to("2020-06-01")
    result = runner.invoke(app)
    assert "Data for 2020-June" in result.stdout
  • Run the test:
pytest -k test_data
  • It outputs:
>       raise RuntimeError(f"Type not yet supported: {annotation}")  # pragma no cover
E       RuntimeError: Type not yet supported: <class 'datetime.datetime'>

~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/main.py:587: RuntimeError
Complete Stacktrace
-> % pytest -k test_data  
=================================================================================================== test session starts ===================================================================================================
platform darwin -- Python 3.8.7, pytest-5.4.3, py-1.8.1, pluggy-0.13.1
rootdir: ~/my_project, inifile: pytest.ini, testpaths: tests
plugins: pylama-7.7.1, cov-2.10.0, freezegun-0.4.1, mock-3.1.0, recording-0.8.1, socket-0.4.0, spec-3.2.0, testmon-1.1.0
collected 31 items / 30 deselected / 1 selected                                                                                                                                                                           

tests/cli/data/test_defaults.py:
  ✗ Data                                                                                                                                                                                                            [100%]

======================================================================================================== FAILURES =========================================================================================================
________________________________________________________________________________________________________ test_data ________________________________________________________________________________________________________

freezer = <freezegun.api.FrozenDateTimeFactory object at 0x10be4ba30>

    def test_data(freezer):
        freezer.move_to("2020-06-01")
>       result = runner.invoke(app)

tests/cli/data/test_defaults.py:10: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/testing.py:20: in invoke
    use_cli = _get_command(app)
~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/main.py:239: in get_command
    click_command = get_command_from_info(typer_instance.registered_commands[0])
~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/main.py:423: in get_command_from_info
    ) = get_params_convertors_ctx_param_name_from_function(command_info.callback)
~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/main.py:404: in get_params_convertors_ctx_param_name_from_function
    click_param, convertor = get_click_param(param)
~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/main.py:656: in get_click_param
    parameter_type = get_click_type(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def get_click_type(
        *, annotation: Any, parameter_info: ParameterInfo
    ) -> click.ParamType:
        if annotation == str:
            return click.STRING
        elif annotation == int:
            if parameter_info.min is not None or parameter_info.max is not None:
                min_ = None
                max_ = None
                if parameter_info.min is not None:
                    min_ = int(parameter_info.min)
                if parameter_info.max is not None:
                    max_ = int(parameter_info.max)
                return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp)
            else:
                return click.INT
        elif annotation == float:
            if parameter_info.min is not None or parameter_info.max is not None:
                return click.FloatRange(
                    min=parameter_info.min,
                    max=parameter_info.max,
                    clamp=parameter_info.clamp,
                )
            else:
                return click.FLOAT
        elif annotation == bool:
            return click.BOOL
        elif annotation == UUID:
            return click.UUID
        elif annotation == datetime:
            return click.DateTime(formats=parameter_info.formats)
        elif (
            annotation == Path
            or parameter_info.allow_dash
            or parameter_info.path_type
            or parameter_info.resolve_path
        ):
            return click.Path(  # type: ignore
                exists=parameter_info.exists,
                file_okay=parameter_info.file_okay,
                dir_okay=parameter_info.dir_okay,
                writable=parameter_info.writable,
                readable=parameter_info.readable,
                resolve_path=parameter_info.resolve_path,
                allow_dash=parameter_info.allow_dash,
                path_type=parameter_info.path_type,
            )
        elif lenient_issubclass(annotation, FileTextWrite):
            return click.File(
                mode=parameter_info.mode or "w",
                encoding=parameter_info.encoding,
                errors=parameter_info.errors,
                lazy=parameter_info.lazy,
                atomic=parameter_info.atomic,
            )
        elif lenient_issubclass(annotation, FileText):
            return click.File(
                mode=parameter_info.mode or "r",
                encoding=parameter_info.encoding,
                errors=parameter_info.errors,
                lazy=parameter_info.lazy,
                atomic=parameter_info.atomic,
            )
        elif lenient_issubclass(annotation, FileBinaryRead):
            return click.File(
                mode=parameter_info.mode or "rb",
                encoding=parameter_info.encoding,
                errors=parameter_info.errors,
                lazy=parameter_info.lazy,
                atomic=parameter_info.atomic,
            )
        elif lenient_issubclass(annotation, FileBinaryWrite):
            return click.File(
                mode=parameter_info.mode or "wb",
                encoding=parameter_info.encoding,
                errors=parameter_info.errors,
                lazy=parameter_info.lazy,
                atomic=parameter_info.atomic,
            )
        elif lenient_issubclass(annotation, Enum):
            return click.Choice(
                [item.value for item in annotation],
                case_sensitive=parameter_info.case_sensitive,
            )
>       raise RuntimeError(f"Type not yet supported: {annotation}")  # pragma no cover
E       RuntimeError: Type not yet supported: <class 'datetime.datetime'>

~/Library/Caches/pypoetry/virtualenvs/my_project-BAzU1U47-py3.8/lib/python3.8/site-packages/typer/main.py:587: RuntimeError
================================================================================================= short test summary info =================================================================================================
FAILED tests/cli/data/test_defaults.py::test_data - RuntimeError: Type not yet supported: <class 'datetime.datetime'>
============================================================================================ 1 failed, 30 deselected in 0.44s =============================================================================================
  • But I expected it to output:
✅  Pass

Expected behaviour

Freezing times in tests are common. I expect that the test pass.

Environment

  • OS: macOS
  • Typer Version: 0.3.2
  • Python version: Python 3.8.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionQuestion or problem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions