From 73c6cf4bd5747756388c217ea0881114a324efdb Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 00:38:33 +0200 Subject: [PATCH 1/7] Support for get_keyword_source --- src/robotlibcore.py | 20 ++++++++++++++++++ utest/test_get_keyword_source.py | 36 ++++++++++++++++++++++++++++++++ utest/test_robotlibcore.py | 4 ++++ 3 files changed, 60 insertions(+) create mode 100644 utest/test_get_keyword_source.py diff --git a/src/robotlibcore.py b/src/robotlibcore.py index a2823e2..2a3125c 100644 --- a/src/robotlibcore.py +++ b/src/robotlibcore.py @@ -20,6 +20,7 @@ """ import inspect +import os import sys try: import typing @@ -174,6 +175,25 @@ def __join_defaults_with_types(self, method, types): types[name] = type(value) return types + def get_keyword_source(self, keyword_name): + method = self.__get_keyword(keyword_name) + try: + path = os.path.normpath(inspect.getfile(method)) + except TypeError: + path = '' + nro = self.__get_keyword_line(method) + return '%s:%s' % (path, nro) + + def __get_keyword_line(self, method): + try: + source, nro = inspect.getsourcelines(method) + except OSError: + return '' + for line in source: + if 'def' in line: + return nro + nro += 1 + class StaticCore(HybridCore): diff --git a/utest/test_get_keyword_source.py b/utest/test_get_keyword_source.py new file mode 100644 index 0000000..728c640 --- /dev/null +++ b/utest/test_get_keyword_source.py @@ -0,0 +1,36 @@ +from os import path + +import pytest +from DynamicLibrary import DynamicLibrary + + +@pytest.fixture(scope='module') +def lib(): + return DynamicLibrary() + + +@pytest.fixture(scope='module') +def lib_path(): + cur_dir = path.dirname(__file__) + return path.normpath(path.join(cur_dir, '..', 'atest', 'DynamicLibrary.py')) + + +@pytest.fixture(scope='module') +def lib_path_components(): + cur_dir = path.dirname(__file__) + return path.normpath(path.join(cur_dir, '..', 'atest', 'librarycomponents.py')) + + +def test_location_in_main(lib, lib_path): + source = lib.get_keyword_source('keyword_in_main') + assert source == '%s:20' % lib_path + + +def test_location_in_class(lib, lib_path_components): + source = lib.get_keyword_source('method') + assert source == '%s:15' % lib_path_components + + +def test_location_in_class_custom_keyword_name(lib, lib_path_components): + source = lib.get_keyword_source('Custom name') + assert source == '%s:19' % lib_path_components diff --git a/utest/test_robotlibcore.py b/utest/test_robotlibcore.py index 5897d69..ed8114e 100644 --- a/utest/test_robotlibcore.py +++ b/utest/test_robotlibcore.py @@ -31,6 +31,7 @@ def test_dir(): 'Embedded arguments "${here}"', '_DynamicCore__get_arg_spec', '_DynamicCore__get_keyword', + '_DynamicCore__get_keyword_line', '_DynamicCore__get_keyword_tags_supported', '_DynamicCore__get_typing_hints', '_DynamicCore__join_defaults_with_types', @@ -48,6 +49,7 @@ def test_dir(): 'get_keyword_arguments', 'get_keyword_documentation', 'get_keyword_names', + 'get_keyword_source', 'get_keyword_tags', 'get_keyword_types', 'instance_attribute', @@ -65,10 +67,12 @@ def test_dir(): expected = [e for e in expected if e not in ('_DynamicCore__get_typing_hints', '_DynamicCore__get_arg_spec', '_DynamicCore__get_keyword', + '_DynamicCore__get_keyword_line', '_DynamicCore__get_keyword_tags_supported', '_DynamicCore__join_defaults_with_types', 'get_keyword_arguments', 'get_keyword_documentation', + 'get_keyword_source', 'get_keyword_tags', 'run_keyword', 'get_keyword_types')] From 712b6602d66c72edf5d721e201a15b6c0c589ea3 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 01:17:31 +0200 Subject: [PATCH 2/7] Path or line not found tests --- requirements-dev.txt | 1 + src/robotlibcore.py | 31 ++++++++++++++++++++----------- utest/test_get_keyword_source.py | 20 ++++++++++++++++++++ utest/test_robotlibcore.py | 2 ++ 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 112c852..ca8a225 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,5 @@ pytest pytest-cov +pytest-mockito robotstatuschecker flake8 diff --git a/src/robotlibcore.py b/src/robotlibcore.py index 2a3125c..f21a31f 100644 --- a/src/robotlibcore.py +++ b/src/robotlibcore.py @@ -177,22 +177,31 @@ def __join_defaults_with_types(self, method, types): def get_keyword_source(self, keyword_name): method = self.__get_keyword(keyword_name) - try: - path = os.path.normpath(inspect.getfile(method)) - except TypeError: - path = '' - nro = self.__get_keyword_line(method) - return '%s:%s' % (path, nro) + path = self.__get_keyword_path(method) + line_number = self.__get_keyword_line(method) + if not path and not line_number: + return None + if not path: + return ':%s' % line_number + if not line_number: + return path + return '%s:%s' % (path, line_number) def __get_keyword_line(self, method): try: - source, nro = inspect.getsourcelines(method) - except OSError: - return '' + source, line_number = inspect.getsourcelines(method) + except (OSError, IOError): + return None for line in source: if 'def' in line: - return nro - nro += 1 + return line_number + line_number += 1 + + def __get_keyword_path(self, method): + try: + return os.path.normpath(inspect.getfile(method)) + except TypeError: + return None class StaticCore(HybridCore): diff --git a/utest/test_get_keyword_source.py b/utest/test_get_keyword_source.py index 728c640..24af534 100644 --- a/utest/test_get_keyword_source.py +++ b/utest/test_get_keyword_source.py @@ -2,6 +2,7 @@ import pytest from DynamicLibrary import DynamicLibrary +from mockito.matchers import Any @pytest.fixture(scope='module') @@ -34,3 +35,22 @@ def test_location_in_class(lib, lib_path_components): def test_location_in_class_custom_keyword_name(lib, lib_path_components): source = lib.get_keyword_source('Custom name') assert source == '%s:19' % lib_path_components + + +def test_no_line_number(lib, lib_path, when): + when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn('') + source = lib.get_keyword_source('keyword_in_main') + assert source == lib_path + + +def test_no_path(lib, when): + when(lib)._DynamicCore__get_keyword_path(Any()).thenReturn('') + source = lib.get_keyword_source('keyword_in_main') + assert source == ':20' + + +def test_no_path_and_no_line_number(lib, when): + when(lib)._DynamicCore__get_keyword_path(Any()).thenReturn('') + when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn('') + source = lib.get_keyword_source('keyword_in_main') + assert source is None diff --git a/utest/test_robotlibcore.py b/utest/test_robotlibcore.py index ed8114e..160a9ab 100644 --- a/utest/test_robotlibcore.py +++ b/utest/test_robotlibcore.py @@ -32,6 +32,7 @@ def test_dir(): '_DynamicCore__get_arg_spec', '_DynamicCore__get_keyword', '_DynamicCore__get_keyword_line', + '_DynamicCore__get_keyword_path', '_DynamicCore__get_keyword_tags_supported', '_DynamicCore__get_typing_hints', '_DynamicCore__join_defaults_with_types', @@ -68,6 +69,7 @@ def test_dir(): '_DynamicCore__get_arg_spec', '_DynamicCore__get_keyword', '_DynamicCore__get_keyword_line', + '_DynamicCore__get_keyword_path', '_DynamicCore__get_keyword_tags_supported', '_DynamicCore__join_defaults_with_types', 'get_keyword_arguments', From 3c0399a67d698bb4645ce5c8f845b73d23777ba0 Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 01:23:17 +0200 Subject: [PATCH 3/7] Fixed mocked return type --- utest/test_get_keyword_source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utest/test_get_keyword_source.py b/utest/test_get_keyword_source.py index 24af534..1691cc5 100644 --- a/utest/test_get_keyword_source.py +++ b/utest/test_get_keyword_source.py @@ -38,19 +38,19 @@ def test_location_in_class_custom_keyword_name(lib, lib_path_components): def test_no_line_number(lib, lib_path, when): - when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn('') + when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn(None) source = lib.get_keyword_source('keyword_in_main') assert source == lib_path def test_no_path(lib, when): - when(lib)._DynamicCore__get_keyword_path(Any()).thenReturn('') + when(lib)._DynamicCore__get_keyword_path(Any()).thenReturn(None) source = lib.get_keyword_source('keyword_in_main') assert source == ':20' def test_no_path_and_no_line_number(lib, when): - when(lib)._DynamicCore__get_keyword_path(Any()).thenReturn('') - when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn('') + when(lib)._DynamicCore__get_keyword_path(Any()).thenReturn(None) + when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn(None) source = lib.get_keyword_source('keyword_in_main') assert source is None From 8ff94e43ec989c637468002e090fcb382544d44b Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 21:53:33 +0200 Subject: [PATCH 4/7] Simplied logic for get_keyword_source --- src/robotlibcore.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/robotlibcore.py b/src/robotlibcore.py index f21a31f..668497c 100644 --- a/src/robotlibcore.py +++ b/src/robotlibcore.py @@ -179,13 +179,13 @@ def get_keyword_source(self, keyword_name): method = self.__get_keyword(keyword_name) path = self.__get_keyword_path(method) line_number = self.__get_keyword_line(method) - if not path and not line_number: - return None - if not path: - return ':%s' % line_number - if not line_number: + if path and line_number: + return '%s:%s' % (path, line_number) + if path: return path - return '%s:%s' % (path, line_number) + if line_number: + return ':%s' % line_number + return None def __get_keyword_line(self, method): try: From 2655b0e76550bd78c765411c96f22df19cf4e69d Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 22:21:32 +0200 Subject: [PATCH 5/7] Support decorator with def in the method name --- atest/DynamicTypesLibrary.py | 9 +++++++++ src/robotlibcore.py | 2 +- utest/test_get_keyword_source.py | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/atest/DynamicTypesLibrary.py b/atest/DynamicTypesLibrary.py index 3522813..3cae0a2 100644 --- a/atest/DynamicTypesLibrary.py +++ b/atest/DynamicTypesLibrary.py @@ -3,6 +3,10 @@ from robotlibcore import DynamicCore, keyword +def def_deco(func): + return func + + class DynamicTypesLibrary(DynamicCore): def __init__(self, arg=False): @@ -52,3 +56,8 @@ def keyword_none(self, arg=None): @keyword def is_python_3(self): return sys.version_info >= (3,) + + @keyword + @def_deco + def keyword_with_def_deco(self): + return 1 diff --git a/src/robotlibcore.py b/src/robotlibcore.py index 668497c..84cc48d 100644 --- a/src/robotlibcore.py +++ b/src/robotlibcore.py @@ -193,7 +193,7 @@ def __get_keyword_line(self, method): except (OSError, IOError): return None for line in source: - if 'def' in line: + if line.strip().startswith('def'): return line_number line_number += 1 diff --git a/utest/test_get_keyword_source.py b/utest/test_get_keyword_source.py index 1691cc5..c667202 100644 --- a/utest/test_get_keyword_source.py +++ b/utest/test_get_keyword_source.py @@ -2,6 +2,7 @@ import pytest from DynamicLibrary import DynamicLibrary +from DynamicTypesLibrary import DynamicTypesLibrary from mockito.matchers import Any @@ -54,3 +55,11 @@ def test_no_path_and_no_line_number(lib, when): when(lib)._DynamicCore__get_keyword_line(Any()).thenReturn(None) source = lib.get_keyword_source('keyword_in_main') assert source is None + + +def test_def_in_decorator(): + cur_dir = path.dirname(__file__) + lib_path = path.normpath(path.join(cur_dir, '..', 'atest', 'DynamicTypesLibrary.py')) + lib = DynamicTypesLibrary() + source = lib.get_keyword_source('keyword_with_def_deco') + assert source == '%s:62' % lib_path From be66b5b2509d5ece97c6340e0b62cb8f487f4fbd Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 22:21:52 +0200 Subject: [PATCH 6/7] Unit test refactoring --- utest/test_get_keyword_source.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/utest/test_get_keyword_source.py b/utest/test_get_keyword_source.py index c667202..551797b 100644 --- a/utest/test_get_keyword_source.py +++ b/utest/test_get_keyword_source.py @@ -12,17 +12,29 @@ def lib(): @pytest.fixture(scope='module') -def lib_path(): - cur_dir = path.dirname(__file__) +def lib_types(): + return DynamicTypesLibrary() + + +@pytest.fixture(scope='module') +def cur_dir(): + return path.dirname(__file__) + +@pytest.fixture(scope='module') +def lib_path(cur_dir): return path.normpath(path.join(cur_dir, '..', 'atest', 'DynamicLibrary.py')) @pytest.fixture(scope='module') -def lib_path_components(): - cur_dir = path.dirname(__file__) +def lib_path_components(cur_dir): return path.normpath(path.join(cur_dir, '..', 'atest', 'librarycomponents.py')) +@pytest.fixture(scope='module') +def lib_path_types(cur_dir): + return path.normpath(path.join(cur_dir, '..', 'atest', 'DynamicTypesLibrary.py')) + + def test_location_in_main(lib, lib_path): source = lib.get_keyword_source('keyword_in_main') assert source == '%s:20' % lib_path @@ -57,9 +69,6 @@ def test_no_path_and_no_line_number(lib, when): assert source is None -def test_def_in_decorator(): - cur_dir = path.dirname(__file__) - lib_path = path.normpath(path.join(cur_dir, '..', 'atest', 'DynamicTypesLibrary.py')) - lib = DynamicTypesLibrary() - source = lib.get_keyword_source('keyword_with_def_deco') - assert source == '%s:62' % lib_path +def test_def_in_decorator(lib_types, lib_path_types): + source = lib_types.get_keyword_source('keyword_with_def_deco') + assert source == '%s:62' % lib_path_types From cdf411b4d11e2ec61432c8c6ce16f4521c4a874d Mon Sep 17 00:00:00 2001 From: Tatu Aalto Date: Sat, 28 Mar 2020 22:36:14 +0200 Subject: [PATCH 7/7] Test errors in inspect --- src/robotlibcore.py | 2 +- utest/test_get_keyword_source.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/robotlibcore.py b/src/robotlibcore.py index 84cc48d..68ec838 100644 --- a/src/robotlibcore.py +++ b/src/robotlibcore.py @@ -190,7 +190,7 @@ def get_keyword_source(self, keyword_name): def __get_keyword_line(self, method): try: source, line_number = inspect.getsourcelines(method) - except (OSError, IOError): + except (OSError, IOError, TypeError): return None for line in source: if line.strip().startswith('def'): diff --git a/utest/test_get_keyword_source.py b/utest/test_get_keyword_source.py index 551797b..895aa96 100644 --- a/utest/test_get_keyword_source.py +++ b/utest/test_get_keyword_source.py @@ -1,3 +1,4 @@ +import inspect from os import path import pytest @@ -72,3 +73,15 @@ def test_no_path_and_no_line_number(lib, when): def test_def_in_decorator(lib_types, lib_path_types): source = lib_types.get_keyword_source('keyword_with_def_deco') assert source == '%s:62' % lib_path_types + + +def test_error_in_getfile(lib, when): + when(inspect).getfile(Any()).thenRaise(TypeError('Some message')) + source = lib.get_keyword_source('keyword_in_main') + assert source is None + + +def test_error_in_line_number(lib, when, lib_path): + when(inspect).getsourcelines(Any()).thenRaise(IOError('Some message')) + source = lib.get_keyword_source('keyword_in_main') + assert source == lib_path \ No newline at end of file