# SeleniumLibrary keywords via Python

**URL:** <https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91>\
**Category:** SeleniumLibrary-Dev\
**Created:** [1 May 2020 17:00 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91 "2020-05-01T17:00:13Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [1 May 2020 17:00 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/1 "2020-05-01T17:00:13Z")

</div>

Hi @aaltat Tatu,

A feature i would really like to see, is the possibility to call SeleniumLibrary keyword from python including code completion.

I think this is nothing the language server can help with because we are talking about pure python.

Use case: when we work with our customers we prefer to offer higher level keywords as library. These keywords are directly related to the elements on the page and all identifiers and so on are in python. Users do not see anything about Selenium, so they can concentrate on their job. Specifying test cases…

I don’t know how to solf this, but it would be nice to get the library instance from Robot Framework BuiltIn and be able to get the keywords completed…

Maybe you can help with an example if you think it is possible.

Thx

---

<div class="post-metadata">

**Author:** ![aaltat](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/aaltat/32/15_2.png) [@aaltat](https://forum.robotframework.org/u/aaltat)\
**Post date:** [1 May 2020 17:59 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/2 "2020-05-01T17:59:36Z")

</div>

Calling keywords from the instance should be already be possible, but the IDE support is more complex. And there is also a limitation in the SeleniumLibrary side too.

First problem is that when one calls `get_library_instance("SeleniumLibrary")` Python does not know what the method returns. That can be solved by example giving type hints. But SeleniumLibrary instance doesn’t know what methods are available as keywords. Most likely this could be solved by implementing ` __dir__ ` in the SeleniumLibrary or in the PythonLibCore side.

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [1 May 2020 18:06 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/3 "2020-05-01T18:06:36Z")

</div>

I was thinking about a static wrapper class.  
A class that has each keyword as static function and calls the real SeleniumLibrary keyword if a keyword is called.

Could be generated from libdoc.xml.

---

<div class="post-metadata">

**Author:** ![aaltat](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/aaltat/32/15_2.png) [@aaltat](https://forum.robotframework.org/u/aaltat)\
**Post date:** [1 May 2020 18:28 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/4 "2020-05-01T18:28:09Z")

</div>

Sounds complicated, implementing Python ` __dir__ ` should be just few lines of code.

---

<div class="post-metadata">

**Author:** ![hobet](https://avatars.discourse-cdn.com/v4/letter/h/51bf81/32.png) [@hobet](https://forum.robotframework.org/u/hobet)\
**Post date:** [1 May 2020 19:01 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/5 "2020-05-01T19:01:55Z")

</div>

Wouldn’t implementing a wapping static class around the dynamic nature of SeleniumLibrary sort of be a step backwards? Plus, wouldn’t a static wrapper then require constant maintenance to keep it in synchronization with the SeleniumLibrary itself?

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [1 May 2020 19:06 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/6 "2020-05-01T19:06:20Z")

</div>

Normally yes.  
That´s why i would plan to generate this wrapper code from dynamically generated LibDoc xml output.  
So each keyword had its documentation and all arguments.

When you install a new SeleniumLibrary the wrapper code can just be regenerated.

The biggest advantage of the dynamic library api (aka dynamic lib core) is, that you can organize your keyword implementations in multiple ‘sub classes’. This advantage would still be there.

---

<div class="post-metadata">

**Author:** ![aaltat](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/aaltat/32/15_2.png) [@aaltat](https://forum.robotframework.org/u/aaltat)\
**Post date:** [2 May 2020 21:26 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/7 "2020-05-02T21:26:17Z")

</div>

I did look into the matter. SeleniumLibrary, or more accurately PythonLibCore, already has [` __dir__ `](https://github.com/robotframework/PythonLibCore/blob/master/src/robotlibcore.py#L84) method, but example PyCharm is not able to use ` __dir__ ` (for some [unknown reason](https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000665110-auto-completion-for-dynamic-module-attributes-in-python)) to retrieve the attributes from class. One possible solution is to generate [sub file](https://www.python.org/dev/peps/pep-0484/#stub-files) which would provide automatic completion to IDE’s like PyCharm. Mypy has a [stubgen tool](https://mypy.readthedocs.io/en/stable/stubgen.html) that allows generation stub files. With some [scripting](https://github.com/robotframework/SeleniumLibrary/pull/1591), it looks [pretty good](https://gist.github.com/aaltat/390021306b14fd6feca21a6286bf480b).

@René would you like to try it out? Copy the ` __init__.pyi` file to the same folder where you have [SeleniumLibrary init](https://github.com/robotframework/SeleniumLibrary/blob/master/src/SeleniumLibrary/ __init__.py) installed. Then in your IDE, do:

```python
from robot.libraries.BuiltIn import BuiltIn

from SeleniumLibrary import SeleniumLibrary

sl: SeleniumLibrary = BuiltIn().get_library_instance('SeleniumLibrary')
sl.open_browser() # open_browser should work with IDE automatic completion.

```

Note requires Python 3.6 or greater.

Is that makes also you happy, I could think how to distribute the ` __init__.pyi` file and polish the script.

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [2 May 2020 22:08 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/8 "2020-05-02T22:08:04Z")

</div>

i will try it out tomorrow.  
Look promising !!! 😍  
Thanks Pal!!

---

<div class="post-metadata">

**Author:** ![hobet](https://avatars.discourse-cdn.com/v4/letter/h/51bf81/32.png) [@hobet](https://forum.robotframework.org/u/hobet)\
**Post date:** [6 May 2020 16:00 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/9 "2020-05-06T16:00:25Z")

</div>

Hello @aaltat I followed up on your suggestions and I must say it looks good! I just did a basic test like you described…nothing elaborate to test it out. But nice work!! ![_2020May06105648](https://europe1.discourse-cdn.com/flex005/uploads/robotframework1/original/1X/896e087cd7f317b7dc4a1fbea16772925777758e.png)

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [6 May 2020 16:51 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/10 "2020-05-06T16:51:20Z")

</div>

Works well!!!

i maybe were a little stupid, but after a few minutes i relized to install mypy and download this stuff instead of installing with pip from git and then run the script and then it works.

Only one thing should be done.  
Arguments that are optional should be show as such.

Like with `Open Browser` i would expect to see the default values.

But really nice!

How would you distribute it?  
Put the Pyi files into the wheel?

---

<div class="post-metadata">

**Author:** ![hobet](https://avatars.discourse-cdn.com/v4/letter/h/51bf81/32.png) [@hobet](https://forum.robotframework.org/u/hobet)\
**Post date:** [6 May 2020 17:17 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/11 "2020-05-06T17:17:49Z")

</div>

I did not install mypy. How was it relevant or needed?

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [6 May 2020 17:24 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/12 "2020-05-06T17:24:44Z")

</div>

As i said, maybe just because i was too stupid. Where did you get the pyi files?

Did you just download the file from tatus github?  
🤦‍♂️

---

<div class="post-metadata">

**Author:** ![hobet](https://avatars.discourse-cdn.com/v4/letter/h/51bf81/32.png) [@hobet](https://forum.robotframework.org/u/hobet)\
**Post date:** [6 May 2020 17:28 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/13 "2020-05-06T17:28:11Z")

</div>

Correct, I just used his. Ah, so you generated your own .pyi file yourself? I thought he said he also used a script that would require some polishing up before it would be ready for “production” usage?

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [6 May 2020 18:25 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/14 "2020-05-06T18:25:46Z")

</div>

@aaltat

I do not really understand what the pyi file is, but for me i looks like it is just a list of functionnames.  
There is no real functionality behind, right?  
You can not Go To Definition etc. So:Wouldn’t it be the same effort to just call the `get_keyword_names` and `get_keyword_arguments` of the Hybrid/DynamicCore, and write this file with default values by your own, without this Strange `url: Optional[Any] = ...` stuff?

This happens when i write the original method header into the pyi file.

```
def open_browser(self, url=None, browser='firefox', alias=None, remote_url=False, desired_capabilities=None, ff_profile_dir=None, options=None, service_log_path=None, executable_path=None): ...

```

![image](https://europe1.discourse-cdn.com/flex005/uploads/robotframework1/original/1X/382c3809e53767f2bad609326264b64393fbd713.png)

much better than this generated thing:

```
def open_browser(self, url: Optional[Any] = ..., browser: str = ..., alias: Optional[Any] = ..., remote_url: bool = ..., desired_capabilities: Optional[Any] = ..., ff_profile_dir: Optional[Any] = ..., options: Optional[Any] = ..., service_log_path: Optional[Any] = ..., executable_path: Optional[Any] = ...): ...

```

![image](https://europe1.discourse-cdn.com/flex005/uploads/robotframework1/original/1X/9852af34f9a00056bf9781a400b812b7d8be8f8c.png)

---

<div class="post-metadata">

**Author:** ![aaltat](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/aaltat/32/15_2.png) [@aaltat](https://forum.robotframework.org/u/aaltat)\
**Post date:** [6 May 2020 18:50 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/15 "2020-05-06T18:50:30Z")

</div>

Stub files, the `.pyi` extension, are meant for type checking and what you see is coming out of the MyPy implementation. But I understand your point, missing the default values is problem. Also when there is not default value available in the argument, the MyPy implementation doesn’t know what is the argument type. Then MyPy adds that `Any` value, as the pep says.

The later part can be improved by adding typing hints to the keyword and then the stub file will look more realistic. Also I did not try will MyPy create better stub file for default values if typing hints are done properly. But let’s see what can be done with the stub files, but at least it’s better than nothing.

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [6 May 2020 18:53 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/16 "2020-05-06T18:53:57Z")

</div>

Thats right.

but again. Wouldn´t it “easy” to just ask the PythonLibCore for its keywords, arguments and Documentation and just write this “text-file” with a small script, than relying on a third party library that does it not perfect?

Could be just a small script that ships with PythonLibCore.

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [6 May 2020 19:19 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/17 "2020-05-06T19:19:38Z")

</div>

@aaltat  
This is far from ready, but this superstupid script just lists the keyword names and arguments and print it in form of a method definition:

```
from SeleniumLibrary import SeleniumLibrary

keywords = SeleniumLibrary().get_keyword_names()
for keyword in keywords:
    args = SeleniumLibrary().get_keyword_arguments(keyword)
    args_str = ""
    for arg in args:
        if isinstance(arg, tuple):
            arg_str = f"{arg[0]}='{arg[1]}'"
        else:
            arg_str = str(arg)
        args_str = f'{args_str}, {arg_str}'
    print(f' def {keyword}(self{args_str}): ...')

```

This is the result. I was first a little bit confused about the `Get WebElements` but i saw why…

```python
    def Get WebElement(self, locator): ...
    def Get WebElements(self, locator): ...
    def add_cookie(self, name, value, path='None', domain='None', secure='None', expiry='None'): ...
    def add_location_strategy(self, strategy_name, strategy_keyword, persist='False'): ...
    def alert_should_be_present(self, text='', action='ACCEPT', timeout='None'): ...
    def alert_should_not_be_present(self, action='ACCEPT', timeout='0'): ...
    def assign_id_to_element(self, locator, id): ...
    def capture_element_screenshot(self, locator, filename='selenium-element-screenshot-{index}.png'): ...
    def capture_page_screenshot(self, filename='selenium-screenshot-{index}.png'): ...
    def checkbox_should_be_selected(self, locator): ...
    def checkbox_should_not_be_selected(self, locator): ...
    def choose_file(self, locator, file_path): ...
    def clear_element_text(self, locator): ...
...
...
    def mouse_out(self, locator): ...
    def mouse_over(self, locator): ...
    def mouse_up(self, locator): ...
    def open_browser(self, url='None', browser='firefox', alias='None', remote_url='False', desired_capabilities='None', ff_profile_dir='None', options='None', service_log_path='None', executable_path='None'): ...
    def open_context_menu(self, locator): ...
    def page_should_contain(self, text, loglevel='TRACE'): ...
    def page_should_contain_button(self, locator, message='None', loglevel='TRACE'): ...

```

---

<div class="post-metadata">

**Author:** ![aaltat](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/aaltat/32/15_2.png) [@aaltat](https://forum.robotframework.org/u/aaltat)\
**Post date:** [6 May 2020 19:41 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/18 "2020-05-06T19:41:21Z")

</div>

I like your idea, it’s worth of exploring. Also would like to how adding proper typing hints change the picture.

---

<div class="post-metadata">

**Author:** ![René](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/ren%C3%A9/32/7_2.png) [@René](https://forum.robotframework.org/u/Ren%C3%A9)\
**Post date:** [6 May 2020 19:56 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/19 "2020-05-06T19:56:10Z")

</div>

One question about HybridCore here:

```python
1 def add_library_components(self, library_components):
2 for component in library_components:
3 for name, func in self.__get_members(component):
4 if callable(func) and hasattr(func, 'robot_name'):
5 kw = getattr(component, name)
6 kw_name = func.robot_name or name 
7 self.keywords[kw_name] = kw
8 self.attributes[name] = self.attributes[kw_name] = kw

```

If a keyword has a decorator and assigns a fixed name to the method, the `self.keywords` key is the `func.robot:name`. Like “Get WebElements”

In `self.keywords` only the `robot_name` is stored, but not the `method_name` which could now be very helpful. Would you thing that this could be added?

---

<div class="post-metadata">

**Author:** ![aaltat](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.robotframework.org/aaltat/32/15_2.png) [@aaltat](https://forum.robotframework.org/u/aaltat)\
**Post date:** [8 May 2020 11:59 UTC](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91/20 "2020-05-08T11:59:16Z")

</div>

It is stored in the attributes:

```python
self.attributes[name] = self.attributes[kw_name] = kw

```

[Next page](https://forum.robotframework.org/t/seleniumlibrary-keywords-via-python/91.md?page=2)
