diff --git a/atest/DynamicTypesAnnotationsLibrary.py b/atest/DynamicTypesAnnotationsLibrary.py index 7a1eb6d..35c10b0 100644 --- a/atest/DynamicTypesAnnotationsLibrary.py +++ b/atest/DynamicTypesAnnotationsLibrary.py @@ -146,4 +146,5 @@ def enum_conversion(self, param: Optional[penum] = None): @keyword @_my_deco(old_args=("arg1", ), new_args=("arg2", )) 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)}" diff --git a/src/robotlibcore.py b/src/robotlibcore.py index 9b4f6d2..ae4f6ac 100644 --- a/src/robotlibcore.py +++ b/src/robotlibcore.py @@ -164,17 +164,22 @@ class KeywordBuilder(object): @classmethod def build(cls, function): - if not PY2: - function = inspect.unwrap(function) return KeywordSpecification( argument_specification=cls._get_arguments(function), documentation=inspect.getdoc(function) or '', argument_types=cls._get_types(function) ) + @classmethod + def unwrap(cls, function): + if PY2: + return function + return inspect.unwrap(function) + @classmethod def _get_arguments(cls, function): - arg_spec = cls._get_arg_spec(function) + unwrap_function = cls.unwrap(function) + arg_spec = cls._get_arg_spec(unwrap_function) argument_specification = cls._get_default_and_named_args( arg_spec, function ) @@ -255,6 +260,7 @@ def _get_types(cls, function): def _get_typing_hints(cls, function): if PY2: return {} + function = cls.unwrap(function) try: hints = typing.get_type_hints(function) except Exception: diff --git a/utest/run.py b/utest/run.py index 5588436..9c65721 100755 --- a/utest/run.py +++ b/utest/run.py @@ -25,6 +25,7 @@ '-p', 'no:cacheprovider', '--junitxml=%s' % xunit_report, '-o', 'junit_family=xunit2', + '--showlocals', curdir ] if args.cov: diff --git a/utest/test_keyword_builder.py b/utest/test_keyword_builder.py index eb58c4a..2cdd94c 100644 --- a/utest/test_keyword_builder.py +++ b/utest/test_keyword_builder.py @@ -3,8 +3,8 @@ from robotlibcore import PY2, RF31, KeywordBuilder from moc_library import MockLibrary if not PY2: - from typing import Union from moc_library_py3 import MockLibraryPy3 + from DynamicTypesAnnotationsLibrary import DynamicTypesAnnotationsLibrary @pytest.fixture @@ -17,6 +17,11 @@ def lib_py3(): return MockLibraryPy3() +@pytest.fixture +def dyn_types(): + return DynamicTypesAnnotationsLibrary(1) + + def test_documentation(lib): spec = KeywordBuilder.build(lib.positional_args) assert spec.documentation == 'Some documentation\n\nMulti line docs' @@ -109,3 +114,21 @@ def test_types(lib_py3): def test_optional_none(lib_py3): spec = KeywordBuilder.build(lib_py3.optional_none) assert spec.argument_types == {'arg1': str, 'arg2': str} + + +@pytest.mark.skipif(PY2, reason='Only for Python 3') +@pytest.mark.skipif(RF31, reason='For RF 3.2') +def test_complex_deco_rf32(dyn_types): + spec = KeywordBuilder.build(dyn_types.keyword_with_deco_and_signature) + assert spec.argument_types == {'arg1': bool, 'arg2': bool} + assert spec.argument_specification == [('arg1', False), ('arg2', False)] + assert spec.documentation == "Test me doc here" + + +@pytest.mark.skipif(PY2, reason='Only for Python 3') +@pytest.mark.skipif(not RF31, reason='For RF 3.2') +def test_complex_deco_rf31(dyn_types): + spec = KeywordBuilder.build(dyn_types.keyword_with_deco_and_signature) + assert spec.argument_types == {'arg1': bool, 'arg2': bool} + assert spec.argument_specification == ['arg1=False', 'arg2=False'] + assert spec.documentation == "Test me doc here" diff --git a/utest/test_robotlibcore.py b/utest/test_robotlibcore.py index b11003e..2a9e059 100644 --- a/utest/test_robotlibcore.py +++ b/utest/test_robotlibcore.py @@ -1,7 +1,7 @@ import sys import pytest -from robot import __version__ as robot__version +from robot import __version__ as robot_version from robotlibcore import HybridCore, PY2 from HybridLibrary import HybridLibrary @@ -99,7 +99,7 @@ def test_getattr(): "'%s' object has no attribute 'non_existing'" % type(lib).__name__ -@pytest.mark.skipif(robot__version >= '3.2', reason='For RF 3.1') +@pytest.mark.skipif(robot_version >= '3.2', reason='For RF 3.1') def test_get_keyword_arguments_rf31(): args = DynamicLibrary().get_keyword_arguments assert args('mandatory') == ['arg1', 'arg2'] @@ -112,7 +112,7 @@ def test_get_keyword_arguments_rf31(): args('__foobar__') -@pytest.mark.skipif(robot__version < '3.2', reason='For RF 3.2 or greater') +@pytest.mark.skipif(robot_version < '3.2', reason='For RF 3.2 or greater') def test_get_keyword_arguments_rf32(): args = DynamicLibrary().get_keyword_arguments assert args('mandatory') == ['arg1', 'arg2'] @@ -126,7 +126,7 @@ def test_get_keyword_arguments_rf32(): @pytest.mark.skipif(PY2, reason='Only for Python 3') -@pytest.mark.skipif(robot__version < '3.2', reason='For RF 3.2 or greater') +@pytest.mark.skipif(robot_version < '3.2', reason='For RF 3.2 or greater') def test_keyword_only_arguments_for_get_keyword_arguments_rf32(): args = DynamicTypesAnnotationsLibrary(1).get_keyword_arguments assert args('keyword_only_arguments') == ['*varargs', ('some', 111)] @@ -135,10 +135,11 @@ def test_keyword_only_arguments_for_get_keyword_arguments_rf32(): assert args('keyword_only_arguments_default_and_no_default') == ['*varargs', 'other', ('value', False)] all_args = ['mandatory', ('positional', 1), '*varargs', 'other', ('value', False), '**kwargs'] assert args('keyword_all_args') == all_args + assert args('keyword_with_deco_and_signature') == [('arg1', False), ('arg2', False)] @pytest.mark.skipif(PY2, reason='Only for Python 3') -@pytest.mark.skipif(robot__version >= '3.2', reason='For RF 3.1') +@pytest.mark.skipif(robot_version >= '3.2', reason='For RF 3.1') def test_keyword_only_arguments_for_get_keyword_arguments_rf31(): args = DynamicTypesAnnotationsLibrary(1).get_keyword_arguments assert args('keyword_only_arguments') == ['*varargs', 'some=111'] @@ -147,6 +148,7 @@ def test_keyword_only_arguments_for_get_keyword_arguments_rf31(): assert args('keyword_only_arguments_default_and_no_default') == ['*varargs', 'other', 'value=False'] all_args = ['mandatory', 'positional=1', '*varargs', 'other', 'value=False', '**kwargs'] assert args('keyword_all_args') == all_args + assert args('keyword_with_deco_and_signature') == ['arg1=False', 'arg2=False'] def test_get_keyword_documentation():