@@ -1713,7 +1713,7 @@ async def _find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
17131713 result = await self ._get_explicit_keyword (name )
17141714
17151715 if not result :
1716- result = await self ._get_implicit_keyword (name )
1716+ result = self ._get_implicit_keyword (name )
17171717
17181718 if not result and self .handle_bdd_style :
17191719 result = await self ._get_bdd_style_keyword (name )
@@ -1723,27 +1723,54 @@ async def _find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
17231723 async def _get_keyword_from_self (self , name : str ) -> Optional [KeywordDoc ]:
17241724 if self .self_library_doc is None :
17251725 self .self_library_doc = await self .namespace .get_library_doc ()
1726- try :
1727- return self .self_library_doc .keywords .get (name , None )
1728- except KeywordError as e :
1729- self .diagnostics .append (
1730- DiagnosticsEntry (
1731- str (e ),
1732- DiagnosticSeverity .ERROR ,
1733- "KeywordError" ,
1726+
1727+ if get_robot_version () >= (6 , 0 , 0 ):
1728+ found : List [Tuple [Optional [LibraryEntry ], KeywordDoc ]] = [
1729+ (None , v ) for v in self .self_library_doc .keywords .get_all (name )
1730+ ]
1731+ if len (found ) > 1 :
1732+ found = self ._select_best_matches (found )
1733+ if len (found ) > 1 :
1734+ self .diagnostics .append (
1735+ DiagnosticsEntry (
1736+ self ._create_multiple_keywords_found_message (name , found , implicit = False ),
1737+ DiagnosticSeverity .ERROR ,
1738+ "KeywordError" ,
1739+ )
1740+ )
1741+ raise CancelSearchError ()
1742+
1743+ if len (found ) == 1 :
1744+ # TODO warning if keyword found is defined in resource and suite
1745+ return found [0 ][1 ]
1746+
1747+ return None
1748+ else :
1749+ try :
1750+ return self .self_library_doc .keywords .get (name , None )
1751+ except KeywordError as e :
1752+ self .diagnostics .append (
1753+ DiagnosticsEntry (
1754+ str (e ),
1755+ DiagnosticSeverity .ERROR ,
1756+ "KeywordError" ,
1757+ )
17341758 )
1735- )
1736- raise CancelSearchError () from e
1759+ raise CancelSearchError () from e
17371760
17381761 async def _yield_owner_and_kw_names (self , full_name : str ) -> AsyncGenerator [Tuple [str , ...], None ]:
17391762 tokens = full_name .split ("." )
17401763 for i in range (1 , len (tokens )):
17411764 yield "." .join (tokens [:i ]), "." .join (tokens [i :])
17421765
17431766 async def _get_explicit_keyword (self , name : str ) -> Optional [KeywordDoc ]:
1744- found : List [Tuple [LibraryEntry , KeywordDoc ]] = []
1767+ found : List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]] = []
17451768 async for owner_name , kw_name in self ._yield_owner_and_kw_names (name ):
17461769 found .extend (await self .find_keywords (owner_name , kw_name ))
1770+
1771+ if get_robot_version () >= (6 , 0 , 0 ) and len (found ) > 1 :
1772+ found = self ._select_best_matches (found )
1773+
17471774 if len (found ) > 1 :
17481775 self .diagnostics .append (
17491776 DiagnosticsEntry (
@@ -1756,36 +1783,50 @@ async def _get_explicit_keyword(self, name: str) -> Optional[KeywordDoc]:
17561783
17571784 return found [0 ][1 ] if found else None
17581785
1759- async def find_keywords (self , owner_name : str , name : str ) -> Sequence [Tuple [LibraryEntry , KeywordDoc ]]:
1786+ async def find_keywords (self , owner_name : str , name : str ) -> List [Tuple [LibraryEntry , KeywordDoc ]]:
17601787 if self ._all_keywords is None :
17611788 self ._all_keywords = [
17621789 v for v in chain (self .namespace ._libraries .values (), self .namespace ._resources .values ())
17631790 ]
1764- return [
1765- (v , v .library_doc .keywords [name ])
1766- for v in self ._all_keywords
1767- if eq (v .alias or v .name , owner_name ) and name in v .library_doc .keywords
1768- ]
1791+
1792+ if get_robot_version () >= (6 , 0 , 0 ):
1793+ result : List [Tuple [LibraryEntry , KeywordDoc ]] = []
1794+ for v in self ._all_keywords :
1795+ if eq (v .alias or v .name , owner_name ):
1796+ result .extend ((v , kw ) for kw in v .library_doc .keywords .get_all (name ))
1797+ return result
1798+ else :
1799+ result = []
1800+ for v in self ._all_keywords :
1801+ if eq (v .alias or v .name , owner_name ):
1802+ kw = v .library_doc .keywords .get (name , None )
1803+ if kw is not None :
1804+ result .append ((v , kw ))
1805+ return result
17691806
17701807 def _create_multiple_keywords_found_message (
1771- self , name : str , found : Sequence [Tuple [LibraryEntry , KeywordDoc ]], implicit : bool = True
1808+ self , name : str , found : Sequence [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]], implicit : bool = True
17721809 ) -> str :
1810+ if any (e [1 ].is_embedded for e in found ):
1811+ error = f"Multiple keywords matching name '{ name } ' found"
1812+ else :
1813+ error = f"Multiple keywords with name '{ name } ' found"
17731814
1774- error = "Multiple keywords with name '%s' found" % name
1775- if implicit :
1776- error += ". Give the full name of the keyword you want to use"
1777- names = sorted (f"{ e [0 ] .alias or e [0 ].name } .{ e [1 ].name } " for e in found )
1815+ if implicit :
1816+ error += ". Give the full name of the keyword you want to use"
1817+
1818+ names = sorted (f"{ e [1 ]. name if e [ 0 ] is None else f' { e [ 0 ] .alias or e [0 ].name } .{ e [1 ].name } ' } " for e in found )
17781819 return "\n " .join ([error + ":" ] + names )
17791820
1780- async def _get_implicit_keyword (self , name : str ) -> Optional [KeywordDoc ]:
1781- result = await self ._get_keyword_from_resource_files (name )
1821+ def _get_implicit_keyword (self , name : str ) -> Optional [KeywordDoc ]:
1822+ result = self ._get_keyword_from_resource_files (name )
17821823 if not result :
1783- result = await self ._get_keyword_from_libraries (name )
1824+ result = self ._get_keyword_from_libraries (name )
17841825 return result
17851826
17861827 def _prioritize_same_file_or_public (
1787- self , entries : List [Tuple [LibraryEntry , KeywordDoc ]]
1788- ) -> List [Tuple [LibraryEntry , KeywordDoc ]]:
1828+ self , entries : List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]
1829+ ) -> List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]:
17891830
17901831 matches = [h for h in entries if h [1 ].source == self .namespace .source ]
17911832 if matches :
@@ -1796,8 +1837,8 @@ def _prioritize_same_file_or_public(
17961837 return matches or entries
17971838
17981839 def _select_best_matches (
1799- self , entries : List [Tuple [LibraryEntry , KeywordDoc ]]
1800- ) -> List [Tuple [LibraryEntry , KeywordDoc ]]:
1840+ self , entries : List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]
1841+ ) -> List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]:
18011842
18021843 normal = [hand for hand in entries if not hand [1 ].is_embedded ]
18031844 if normal :
@@ -1807,7 +1848,9 @@ def _select_best_matches(
18071848 return matches or entries
18081849
18091850 def _is_worse_match_than_others (
1810- self , candidate : Tuple [LibraryEntry , KeywordDoc ], alternatives : List [Tuple [LibraryEntry , KeywordDoc ]]
1851+ self ,
1852+ candidate : Tuple [Optional [LibraryEntry ], KeywordDoc ],
1853+ alternatives : List [Tuple [Optional [LibraryEntry ], KeywordDoc ]],
18111854 ) -> bool :
18121855 for other in alternatives :
18131856 if (
@@ -1819,31 +1862,47 @@ def _is_worse_match_than_others(
18191862 return False
18201863
18211864 def _is_better_match (
1822- self , candidate : Tuple [LibraryEntry , KeywordDoc ], other : Tuple [LibraryEntry , KeywordDoc ]
1865+ self , candidate : Tuple [Optional [ LibraryEntry ] , KeywordDoc ], other : Tuple [Optional [ LibraryEntry ] , KeywordDoc ]
18231866 ) -> bool :
18241867 return (
18251868 other [1 ].matcher .embedded_arguments .match (candidate [1 ].name ) is not None
18261869 and candidate [1 ].matcher .embedded_arguments .match (other [1 ].name ) is None
18271870 )
18281871
1829- async def _get_keyword_from_resource_files (self , name : str ) -> Optional [KeywordDoc ]:
1872+ def _get_keyword_from_resource_files (self , name : str ) -> Optional [KeywordDoc ]:
18301873 if self ._resource_keywords is None :
18311874 self ._resource_keywords = [v for v in chain (self .namespace ._resources .values ())]
18321875
1833- found : List [Tuple [LibraryEntry , KeywordDoc ]] = [
1834- (v , v .library_doc .keywords [name ]) for v in self ._resource_keywords if name in v .library_doc .keywords
1835- ]
1876+ if get_robot_version () >= (6 , 0 , 0 ):
1877+ found : List [Tuple [Optional [LibraryEntry ], KeywordDoc ]] = []
1878+ for v in self ._resource_keywords :
1879+ r = v .library_doc .keywords .get_all (name )
1880+ if r :
1881+ found .extend ([(v , k ) for k in r ])
1882+ else :
1883+ found = []
1884+ for k in self ._resource_keywords :
1885+ s = k .library_doc .keywords .get (name , None )
1886+ if s is not None :
1887+ found .append ((k , s ))
1888+
18361889 if not found :
18371890 return None
1891+
18381892 if get_robot_version () >= (6 , 0 , 0 ):
18391893 if len (found ) > 1 :
18401894 found = self ._prioritize_same_file_or_public (found )
18411895
1896+ if len (found ) > 1 :
1897+ found = self ._select_best_matches (found )
1898+
1899+ if len (found ) > 1 :
1900+ found = self ._get_keyword_based_on_search_order (found )
1901+
1902+ else :
18421903 if len (found ) > 1 :
1843- found = self ._select_best_matches (found )
1904+ found = self ._get_keyword_based_on_search_order (found )
18441905
1845- if len (found ) > 1 :
1846- found = await self ._get_keyword_based_on_search_order (found )
18471906 if len (found ) == 1 :
18481907 return found [0 ][1 ]
18491908
@@ -1856,27 +1915,49 @@ async def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordD
18561915 )
18571916 raise CancelSearchError ()
18581917
1859- async def _get_keyword_based_on_search_order (
1860- self , entries : List [Tuple [LibraryEntry , KeywordDoc ]]
1861- ) -> List [Tuple [LibraryEntry , KeywordDoc ]]:
1918+ def _get_keyword_based_on_search_order (
1919+ self , entries : List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]
1920+ ) -> List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]:
18621921
18631922 for libname in self .namespace .search_order :
18641923 for e in entries :
1865- if eq (libname , e [0 ].alias or e [0 ].name ):
1924+ if e [ 0 ] is not None and eq (libname , e [0 ].alias or e [0 ].name ):
18661925 return [e ]
18671926
18681927 return entries
18691928
1870- async def _get_keyword_from_libraries (self , name : str ) -> Optional [KeywordDoc ]:
1929+ def _get_keyword_from_libraries (self , name : str ) -> Optional [KeywordDoc ]:
18711930 if self ._library_keywords is None :
18721931 self ._library_keywords = [v for v in chain (self .namespace ._libraries .values ())]
1873- found = [(v , v .library_doc .keywords [name ]) for v in self ._library_keywords if name in v .library_doc .keywords ]
1932+
1933+ if get_robot_version () >= (6 , 0 , 0 ):
1934+ found : List [Tuple [Optional [LibraryEntry ], KeywordDoc ]] = []
1935+ for v in self ._library_keywords :
1936+ r = v .library_doc .keywords .get_all (name )
1937+ if r :
1938+ found .extend ([(v , k ) for k in r ])
1939+ else :
1940+ found = []
1941+
1942+ for k in self ._library_keywords :
1943+ s = k .library_doc .keywords .get (name , None )
1944+ if s is not None :
1945+ found .append ((k , s ))
1946+
18741947 if not found :
18751948 return None
1876- if len (found ) > 1 :
1877- found = await self ._get_keyword_based_on_search_order (found )
1878- if len (found ) == 2 :
1879- found = await self ._filter_stdlib_runner (* found )
1949+
1950+ if get_robot_version () >= (6 , 0 , 0 ):
1951+ if len (found ) > 1 :
1952+ found = self ._select_best_matches (found )
1953+ if len (found ) > 1 :
1954+ found = self ._get_keyword_based_on_search_order (found )
1955+ else :
1956+ if len (found ) > 1 :
1957+ found = self ._get_keyword_based_on_search_order (found )
1958+ if len (found ) == 2 :
1959+ found = self ._filter_stdlib_runner (* found )
1960+
18801961 if len (found ) == 1 :
18811962 return found [0 ][1 ]
18821963
@@ -1889,15 +1970,15 @@ async def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
18891970 )
18901971 raise CancelSearchError ()
18911972
1892- async def _filter_stdlib_runner (
1893- self , entry1 : Tuple [LibraryEntry , KeywordDoc ], entry2 : Tuple [LibraryEntry , KeywordDoc ]
1894- ) -> List [Tuple [LibraryEntry , KeywordDoc ]]:
1973+ def _filter_stdlib_runner (
1974+ self , entry1 : Tuple [Optional [ LibraryEntry ] , KeywordDoc ], entry2 : Tuple [Optional [ LibraryEntry ] , KeywordDoc ]
1975+ ) -> List [Tuple [Optional [ LibraryEntry ] , KeywordDoc ]]:
18951976 from robot .libraries import STDLIBS
18961977
18971978 stdlibs_without_remote = STDLIBS - {"Remote" }
1898- if entry1 [0 ].name in stdlibs_without_remote :
1979+ if entry1 [0 ] is not None and entry1 [ 0 ] .name in stdlibs_without_remote :
18991980 standard , custom = entry1 , entry2
1900- elif entry2 [0 ].name in stdlibs_without_remote :
1981+ elif entry2 [0 ] is not None and entry2 [ 0 ] .name in stdlibs_without_remote :
19011982 standard , custom = entry2 , entry1
19021983 else :
19031984 return [entry1 , entry2 ]
@@ -1913,19 +1994,21 @@ async def _filter_stdlib_runner(
19131994 return [custom ]
19141995
19151996 def _create_custom_and_standard_keyword_conflict_warning_message (
1916- self , custom : Tuple [LibraryEntry , KeywordDoc ], standard : Tuple [LibraryEntry , KeywordDoc ]
1997+ self , custom : Tuple [Optional [ LibraryEntry ] , KeywordDoc ], standard : Tuple [Optional [ LibraryEntry ] , KeywordDoc ]
19171998 ) -> str :
19181999 custom_with_name = standard_with_name = ""
1919- if custom [0 ].alias is not None :
2000+ if custom [0 ] is not None and custom [ 0 ] .alias is not None :
19202001 custom_with_name = " imported as '%s'" % custom [0 ].alias
1921- if standard [0 ].alias is not None :
2002+ if standard [0 ] is not None and standard [ 0 ] .alias is not None :
19222003 standard_with_name = " imported as '%s'" % standard [0 ].alias
19232004 return (
19242005 f"Keyword '{ standard [1 ].name } ' found both from a custom test library "
1925- f"'{ custom [0 ].name } '{ custom_with_name } and a standard library '{ standard [1 ].name } '{ standard_with_name } . "
2006+ f"'{ '' if custom [0 ] is None else custom [0 ].name } '{ custom_with_name } "
2007+ f"and a standard library '{ standard [1 ].name } '{ standard_with_name } . "
19262008 f"The custom keyword is used. To select explicitly, and to get "
1927- f"rid of this warning, use either '{ custom [0 ].alias or custom [0 ].name } .{ custom [1 ].name } ' "
1928- f"or '{ standard [0 ].alias or standard [0 ].name } .{ standard [1 ].name } '."
2009+ f"rid of this warning, use either "
2010+ f"'{ '' if custom [0 ] is None else custom [0 ].alias or custom [0 ].name } .{ custom [1 ].name } ' "
2011+ f"or '{ '' if standard [0 ] is None else standard [0 ].alias or standard [0 ].name } .{ standard [1 ].name } '."
19292012 )
19302013
19312014 async def _get_bdd_style_keyword (self , name : str ) -> Optional [KeywordDoc ]:
0 commit comments