PythonRemoteServer icon indicating copy to clipboard operation
PythonRemoteServer copied to clipboard

Error importing SeleniumLibrary

Open hskn opened this issue 6 years ago • 5 comments

As a background, I have to use .net Robot Framework, because some complex testing libraries are .net based. So my idea was to use robotremoteserver to also utilize SeleniumLibrary (not supported by .net rf).

import sys
from robotremoteserver import RobotRemoteServer
from SeleniumLibrary import SeleniumLibrary

RobotRemoteServer(SeleniumLibrary())

This yields

RobotRemoteServer(SeleniumLibrary()) File "/usr/local/lib/python3.7/site-packages/robotremoteserver.py", line 73, in __init__ self._library = RemoteLibraryFactory(library) File "/usr/local/lib/python3.7/site-packages/robotremoteserver.py", line 259, in RemoteLibraryFactory return DynamicRemoteLibrary(library, get_keyword_names, run_keyword) File "/usr/local/lib/python3.7/site-packages/robotremoteserver.py", line 362, in __init__ HybridRemoteLibrary.__init__(self, library, get_keyword_names) File "/usr/local/lib/python3.7/site-packages/robotremoteserver.py", line 355, in __init__ StaticRemoteLibrary.__init__(self, library) File "/usr/local/lib/python3.7/site-packages/robotremoteserver.py", line 280, in __init__ self._names, self._robot_name_index = self._get_keyword_names(library) File "/usr/local/lib/python3.7/site-packages/robotremoteserver.py", line 285, in _get_keyword_names for name, kw in inspect.getmembers(library): File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/inspect.py", line 341, in getmembers value = getattr(object, key) File "/usr/local/lib/python3.7/site-packages/SeleniumLibrary/__init__.py", line 415, in driver raise NoOpenBrowser('No browser is open.') SeleniumLibrary.errors.NoOpenBrowser: No browser is open.

Similar error also with python 2.7. According to Tatu Aaltonen and Pekka Klärck, there's a bug in remote server.

hskn avatar Jun 18 '19 10:06 hskn

There are actually two bugs here: blindly using inspect.getmembers() with static libraries and the fact that this method is called at all with hybrid and dynamic libraries such as SeleniumLibrary. I explain them below.

= Using inspect.getmembers() with static libraries =

StaticRemoteLibrary uses inspect.getmembers(library) to find out what attributes the library has to check which of those attributes are actually method implementing keywords. The problem is that this method blindly accesses all properties of the library instance and SeleniunLibrary has some properties that can be successfully used only if a browser is open.

The problem can be avoided by not using inspect.getmembers() and instead getting attributes defined in the class (like properties) directly from the class, not from the instance. Both Robot Framework itself and PythonLibCore project do that and the same approach as used by the latter would probably work here as well.

One thing to take into account is that is that PythonLibCore only allows using new style classes (and modules) as libraries but there are no such restriction here. Thus the exact code linked above doesn't work, but using library.__class__ instead of type(instance) with old style classes ought to fix that.

= Hybdic and dynamic libraries calling inspect.getmembers() =

SeleniumLibrary is a dynamic library and they, similarly as hybrid libraries, expose their keywords using get_keyword_names. With them there's thus no need to get attributes from the library instance in the first place.

This problem happens because DynamicRemoteLibrary inherits HybridRemoteLibrary which inherits StaticRemoteLibrary, and StaticRemoteLibrary ends up calling inspect.getmembers() in its __init__. It's somewhat questionable is this whole inheritance hierarchy useful, but at least code should be changed so that attributes are only accessed with static libraries.

pekkaklarck avatar Aug 08 '19 10:08 pekkaklarck

Because there are two bugs, we probably should also have two bug reports. Fixing the problem with inspect.getmembers() would fix the issue with SeleniumLibrary, but avoiding calling that method in the first place with hybrid and dynamic libraries would be even better fix.

pekkaklarck avatar Aug 08 '19 10:08 pekkaklarck

Linking to forum post https://forum.robotframework.org/t/one-suite-setup-parallel-testlevelsplit/274/6?u=mkorpela

mkorpela avatar May 29 '20 03:05 mkorpela

281c282,283
<         self._names, self._robot_name_index = self._get_keyword_names(library)
---
>         self._names = None
>         self._robot_name_index = None
283c285
<     def _get_keyword_names(self, library):
---
>     def _construct_keyword_names(self):
286c288
<         for name, kw in inspect.getmembers(library):
---
>         for name, kw in inspect.getmembers(self._library):
295a298,299
>         if self._names is None:
>             self._names, self._robot_name_index = self._construct_keyword_names()
302a307,308
>         if self._names is None:
>             self._names, self._robot_name_index = self._construct_keyword_names()
378c384
<         args = [name, args, kwargs] if kwargs else [name, args]
---
>         args = [name, args, kwargs] if kwargs else [name, args, {}]

mkorpela avatar May 31 '20 20:05 mkorpela

Thats the changes I did to make dynamic libs work.

mkorpela avatar May 31 '20 20:05 mkorpela