2121
2222import inspect
2323import sys
24-
2524try :
26- from robot .api .deco import keyword
27- except ImportError : # Support RF < 2.9
28- def keyword (name = None , tags = ()):
29- if callable (name ):
30- return keyword ()(name )
25+ import typing
26+ except ImportError :
27+ typing = None
3128
32- def decorator (func ):
33- func .robot_name = name
34- func .robot_tags = tags
35- return func
36- return decorator
3729
30+ from robot .api .deco import keyword # noqa F401
3831
3932PY2 = sys .version_info < (3 ,)
4033
@@ -51,7 +44,7 @@ def __init__(self, library_components):
5144
5245 def add_library_components (self , library_components ):
5346 for component in library_components :
54- for name , func in self ._get_members (component ):
47+ for name , func in self .__get_members (component ):
5548 if callable (func ) and hasattr (func , 'robot_name' ):
5649 kw = getattr (component , name )
5750 kw_name = func .robot_name or name
@@ -60,7 +53,7 @@ def add_library_components(self, library_components):
6053 # method names as well as possible custom names.
6154 self .attributes [name ] = self .attributes [kw_name ] = kw
6255
63- def _get_members (self , component ):
56+ def __get_members (self , component ):
6457 if inspect .ismodule (component ):
6558 return inspect .getmembers (component )
6659 if inspect .isclass (component ):
@@ -70,9 +63,9 @@ def _get_members(self, component):
7063 raise TypeError ('Libraries must be modules or new-style class '
7164 'instances, got old-style class {!r} instead.'
7265 .format (component .__class__ .__name__ ))
73- return self ._get_members_from_instance (component )
66+ return self .__get_members_from_instance (component )
7467
75- def _get_members_from_instance (self , instance ):
68+ def __get_members_from_instance (self , instance ):
7669 # Avoid calling properties by getting members from class, not instance.
7770 cls = type (instance )
7871 for name in dir (instance ):
@@ -97,22 +90,22 @@ def get_keyword_names(self):
9790
9891
9992class DynamicCore (HybridCore ):
100- _get_keyword_tags_supported = False # get_keyword_tags is new in RF 3.0.2
93+ __get_keyword_tags_supported = False # get_keyword_tags is new in RF 3.0.2
10194
10295 def run_keyword (self , name , args , kwargs = None ):
10396 return self .keywords [name ](* args , ** (kwargs or {}))
10497
10598 def get_keyword_arguments (self , name ):
10699 kw = self .keywords [name ] if name != '__init__' else self .__init__
107- args , defaults , varargs , kwargs = self ._get_arg_spec (kw )
100+ args , defaults , varargs , kwargs = self .__get_arg_spec (kw )
108101 args += ['{}={}' .format (name , value ) for name , value in defaults ]
109102 if varargs :
110103 args .append ('*{}' .format (varargs ))
111104 if kwargs :
112105 args .append ('**{}' .format (kwargs ))
113106 return args
114107
115- def _get_arg_spec (self , kw ):
108+ def __get_arg_spec (self , kw ):
116109 if PY2 :
117110 spec = inspect .getargspec (kw )
118111 keywords = spec .keywords
@@ -127,7 +120,7 @@ def _get_arg_spec(self, kw):
127120 return mandatory , defaults , spec .varargs , keywords
128121
129122 def get_keyword_tags (self , name ):
130- self ._get_keyword_tags_supported = True
123+ self .__get_keyword_tags_supported = True
131124 return self .keywords [name ].robot_tags
132125
133126 def get_keyword_documentation (self , name ):
@@ -137,11 +130,50 @@ def get_keyword_documentation(self, name):
137130 return inspect .getdoc (self .__init__ ) or ''
138131 kw = self .keywords [name ]
139132 doc = inspect .getdoc (kw ) or ''
140- if kw .robot_tags and not self ._get_keyword_tags_supported :
133+ if kw .robot_tags and not self .__get_keyword_tags_supported :
141134 tags = 'Tags: {}' .format (', ' .join (kw .robot_tags ))
142135 doc = '{}\n \n {}' .format (doc , tags ) if doc else tags
143136 return doc
144137
138+ def get_keyword_types (self , keyword_name ):
139+ method = self .__get_keyword (keyword_name )
140+ if method == {}:
141+ return method
142+ types = getattr (method , 'robot_types' , ())
143+ if types is None :
144+ return types
145+ if not types :
146+ types = self .__get_typing_hints (method )
147+ types = self .__join_defaults_with_types (method , types )
148+ return types
149+
150+ def __get_keyword (self , keyword_name ):
151+ if keyword_name == '__init__' :
152+ return self .__init__
153+ if keyword_name .startswith ('__' ) and keyword_name .endswith ('__' ):
154+ return {}
155+ method = self .keywords .get (keyword_name )
156+ if not method :
157+ raise ValueError ('Keyword "%s" not found.' % keyword_name )
158+ return method
159+
160+ def __get_typing_hints (self , method ):
161+ if PY2 :
162+ return {}
163+ try :
164+ hints = typing .get_type_hints (method )
165+ except Exception :
166+ hints = method .__annotations__
167+ hints .pop ('return' , None )
168+ return hints
169+
170+ def __join_defaults_with_types (self , method , types ):
171+ _ , defaults , _ , _ = self .__get_arg_spec (method )
172+ for name , value in defaults :
173+ if name not in types and isinstance (value , (bool , type (None ))):
174+ types [name ] = type (value )
175+ return types
176+
145177
146178class StaticCore (HybridCore ):
147179
0 commit comments