Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions atest/DynamicTypesAnnotationsLibrary.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,7 @@ def enum_conversion(self, param: Optional[penum] = None):
def keyword_with_deco_and_signature(self, arg1: bool = False, arg2: bool = False):
"""Test me doc here"""
return f"{arg1}: {type(arg1)}, {arg2}: {type(arg2)}"

@keyword
def keyword_optional_with_none(self, arg: Optional[str] = None):
return f"arg: {arg}, type: {type(arg)}"
11 changes: 11 additions & 0 deletions atest/tests_types.robot
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*** Settings ***
Library DynamicTypesLibrary.py
Library DynamicTypesAnnotationsLibrary.py xxx
Suite Setup Import DynamicTypesAnnotationsLibrary In Python 3 Only

*** Test Cases ***
Expand Down Expand Up @@ -73,6 +74,16 @@ Enum Conversion To Invalid Value Should Fail
Run Keyword And Expect Error ValueError: Argument 'param' got value 'not ok' that*
... Enum Conversion not ok

Type Conversion With Optional And None
${types} = Keyword Optional With None
Should Contain ${types} arg: None,
Should Contain ${types} <class 'NoneType'>
${types} = Keyword Optional With None None
Should Contain ${types} arg: None,
Should Contain ${types} <class 'str'>
${types} = Keyword Optional With None ${None}
Should Contain ${types} arg: None,
Should Contain ${types} <class 'NoneType'>

*** Keywords ***
Import DynamicTypesAnnotationsLibrary In Python 3 Only
Expand Down
16 changes: 9 additions & 7 deletions src/robotlibcore.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,17 @@
examples see the project pages at
https://github.com/robotframework/PythonLibCore
"""

import inspect
import os
import typing

from robot import __version__ as robot_version
from robot.utils import PY_VERSION

try:
import typing
except ImportError:
typing = None

from robot.api.deco import keyword # noqa F401

RF32 = robot_version < '4.'

__version__ = '2.2.2.dev1'

Expand Down Expand Up @@ -246,8 +244,10 @@ def _get_typing_hints(cls, function):
# remove return and self statements
if arg_with_hint not in all_args:
hints.pop(arg_with_hint)
default = cls._get_defaults(arg_spec)
return cls._remove_optional_none_type_hints(hints, default)
if RF32:
default = cls._get_defaults(arg_spec)
return cls._remove_optional_none_type_hints(hints, default)
return hints

@classmethod
def _args_as_list(cls, function, arg_spec):
Expand All @@ -260,6 +260,7 @@ def _args_as_list(cls, function, arg_spec):
function_args.append(arg_spec.varkw)
return function_args

# TODO: Remove when support RF 3.2 is dropped
# Copied from: robot.running.arguments.argumentparser
@classmethod
def _remove_optional_none_type_hints(cls, type_hints, defaults):
Expand All @@ -274,6 +275,7 @@ def _remove_optional_none_type_hints(cls, type_hints, defaults):
type_hints[arg] = types[0]
return type_hints

# TODO: Remove when support RF 3.2 is dropped
# Copied from: robot.running.arguments.argumentparser
@classmethod
def _is_union(cls, typing_type):
Expand Down
19 changes: 17 additions & 2 deletions utest/test_get_keyword_types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from typing import List, Union

import pytest
import typing

from robotlibcore import RF32

from typing import List, Union

from DynamicTypesAnnotationsLibrary import DynamicTypesAnnotationsLibrary
from DynamicTypesAnnotationsLibrary import CustomObject
Expand Down Expand Up @@ -180,3 +183,15 @@ def test_keyword_self_and_keyword_only_types(lib_types):
def test_keyword_with_decorator_arguments(lib_types):
types = lib_types.get_keyword_types('keyword_with_deco_and_signature')
assert types == {'arg1': bool, 'arg2': bool}


@pytest.mark.skipif(RF32, reason='Only for RF4+')
def test_keyword_optional_with_none_rf4(lib_types):
types = lib_types.get_keyword_types('keyword_optional_with_none')
assert types == {'arg': typing.Union[str, type(None)]}


@pytest.mark.skipif(not RF32, reason='Only for RF3.2+')
def test_keyword_optional_with_none_rf32(lib_types):
types = lib_types.get_keyword_types('keyword_optional_with_none')
assert types == {'arg': str}
12 changes: 10 additions & 2 deletions utest/test_keyword_builder.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import typing

from robotlibcore import KeywordBuilder
from robotlibcore import KeywordBuilder, RF32
from moc_library import MockLibrary
from DynamicTypesAnnotationsLibrary import DynamicTypesAnnotationsLibrary

Expand Down Expand Up @@ -77,11 +78,18 @@ def test_types(lib):
assert spec.argument_types == {'varargs': int, 'other': bool, 'kwargs': int}


def test_optional_none(lib):
@pytest.mark.skipif(not RF32, reason='Only for RF3.2+')
def test_optional_none_rf32(lib):
spec = KeywordBuilder.build(lib.optional_none)
assert spec.argument_types == {'arg1': str, 'arg2': str}


@pytest.mark.skipif(RF32, reason='Only for RF4')
def test_optional_none_rf4(lib):
spec = KeywordBuilder.build(lib.optional_none)
assert spec.argument_types == {'arg1': typing.Union[str, None], 'arg2': typing.Union[str, None]}


def test_complex_deco(dyn_types):
spec = KeywordBuilder.build(dyn_types.keyword_with_deco_and_signature)
assert spec.argument_types == {'arg1': bool, 'arg2': bool}
Expand Down