Getting VSCode to recognize keywords implemented in a remote library

I’m writing some RF tests where some of the keywords are implemented in a Robot Framework Remote Library. I’m having trouble getting VSCode to recognize those keywords for code completion/IntelliSense.

Both the .robot test suites and the python library is in the same git repo. At runtime, I copy the remote library to the target under test and execute it there and then

Import Library    Remote    http://${TARGET}:8270

The tests successfully use the keywords defined in remote library.

VSCode can do code completion for “normal” keywords implemented in .robot files, but not for the keywords implemented in the remote library.

I’ve tried using d-biehl.robotcode and robocorp.robotframework-lsp as the language servers. I also added the path to the python file where the remote library is implemented to the python path as follows, but that didn’t help either:

"robotcode.robot.pythonPath": [
        "./resources/remote/ROSRobotFrameworkRemoteLibrary.py"
    ]

Is it possible to get this working? I’m thinking it might not just be a python path problem, but also an issue with the VSCode extension not realizing that the keywords are implemented in that file since they are not imported at the top of the suite as usual, but will get imported with the Import Library keyword at runtime.

The code looks something like this:

./tests/MyTests.robot:

*** Settings ***
Resource            ../resources/Remote.resource
Suite Setup         Setup Suite

*** Test Cases ***
Hello Robot
    Robot should say hello    # implemented in the remote library. Runs successfully, but VSCode underlines in red

*** Keywords ***
Setup Suite
    Remote.Setup remote

./resources/Remote.resource:


*** Keywords ***
Copy files to remote
    # scp ROSRobotFrameworkRemoteLibrary.py to the remote

Start remote server
    # execute python3 /tmp/robot-framework/ROSRobotFrameworkRemoteLibrary.py on the remote machine

Setup remote
    Copy files to remote
    Start remote server
    Import Library    Remote    http://${TARGET}:8270
    Remote library should be available

./resources/ROSRobotFrameworkRemoteLibrary.py:

from robotremoteserver import RobotRemoteServer

class ROSRFRemoteLibrary:

    def __init__(self) -> None:
        pass

    def remote_library_should_be_available(self):
        print("Remote library is available")

    def robot_should_say_hello(self):
        print("Hello from remote library")

Hi Nick,

Welcome to the forum!

You are right, the issue is the dynamic library import that only happens during runtime.

The VSCode extensions cannot anticipate the execution flow. You may have exceptions and load different libraries in doing so, which makes any auto suggestion gambling.

You would have to import the library statically in *** Settings *** for the extensions to work.

Not sure how to solve the automated start and teardown of the remote server. A listen starting the remote server on library import and stopping on end_suite, maybe.

Regards,
Markus

Hi @Noordsestern , thanks for your response.

I’m don’t think a static import of the remote library as follows would work, since the language server would not know what keywords that remote provides without actually executing that import and establishing a connection with the remote (which in my case is not even running the remote server during development time).

*** Settings ***
Library    Remote    http://${TARGET}:8270

Did you mean an import of the remote library directly, as if it was not a remote library?

*** Settings ***
Library    ./resources/ROSRobotFrameworkRemoteLibrary.py

That should in theory work. But there are several problems with that:

  1. I can’t execute the test suite with that import present
  2. The linter is very unhappy about that library because man of the import statements don’t resolve as those dependencies are not present in my local environment, only on the target machine (robotremoteserver, ROS packages, …)

I was hoping there would be a way to force the language server to consider some library file to be imported without actually importing it in the suite. But given (2.) above, I’m not even sure that would help.

Just entering here a bit off-topic.
@ndepal Maybe you could confirm that RIDE receives the remote library keywords documentation.
Since RIDE does not use LSP, but instantiates libraries on its own, maybe you could try to use this technique to obtain the docs. Other possibility is to generate the docs with libdocand have it somehow pre-loaded in VSCode.

He @ndepal,

I can only speak for RobotCode (I wouldn’t even consider using the other one anymore, as it’s no longer maintained and effectively dead).

  1. As Markus already mentioned, the issue arises because you are dynamically loading the Remote Library. RobotCode, like all other Robot Framework IDEs and editor extensions, can only analyze code statically. Thus, the correct way would be:
*** Settings ***
Library    Remote    http://${TARGET}:8270
  1. Working with Remote Libraries can indeed be tricky, given the fundamental nature of how Robot Framework libraries function—particularly if multiple Remote Libraries are involved. A practical solution is to ensure the Remote Server is running at least once during development so RobotCode can query it and determine which keywords it provides. RobotCode does this query only once, caches the keyword definitions, after which you can safely shut down the Remote Server. However, performing this initial step at least once is necessary.

Should you subsequently modify the library, you’ll need to restart the Remote Server, clear the RobotCode cache, and restart RobotCode itself. You can conveniently do this in VSCode via the command:

RobotCode: Clear Cache and Restart Language Servers

@ndepal
Could you explain why you need dynamic loading and how you’re currently implementing it? There may be other possible solutions.

One possible approach could be to develop your own custom library that inherits from the Remote Library. This custom library could detect at instantiation whether Robot Framework is currently running (see documentation: Detecting if Robot Framework is running). If it is running, the custom library could connect to the target, copy required resources, and initiate the Remote Server. Otherwise, it would simply offer mocked keyword definitions for development.

Additionally, this GitHub discussion could provide further insight: https://github.com/robotcodedev/robotcode/issues/317

@HelioGuilherme66, please don’t take this the wrong way, but we’re currently in the VSCode channel. RIDE operates similarly under the hood regarding dynamic imports. According to its documentation (RIDE Keyword Completion), it also requires similar workarounds for dynamic imports. Hence, mentioning RIDE here doesn’t contribute constructively to addressing the specific issue at hand.

2 Likes

Thank you for your input @daniel

The reason for needing to dynamically load the remote library is as follows:

I’m writing tests for an actual robot. This robot is running ROS and some of the keywords I’m implementing are implemented in the remote library using rospy. I.e., the Robot Framework keywords subscribe to and publish ROS messages under the hood. Other keywords use the robot’s browser-based control interface or its REST API.

The production code running on the robot does not contain anything Robot Framework related.
All of the test code lives in the same repository.

At test execution time, the suite setup copies over the remote library to the target robot and starts it.

This has a couple of key advantages for our workflow:

  1. It’s easy to iterate on the test code. Rather than having to write some of the test code in the production repo for the robot and having to deploy an updated software version to the robot, all of my work is happening in the same code repo. Every time the tests are run, the latest version of the remote library is sent to the robot. The .robot tests being executed on the local machine are always in sync with the keywords provided by the robot through the remote library.
  2. It’s easy to target different robots as well as running a local simulation of the robot’s software, simply by providing a different robot --variable.

This setup works quite well with the exception of the convenience of IntelliSense.

For one, the keywords implemented in the remote library are not recognized by the language server (what I’m asking about here).

For another, the remote library code is a sea of red in VSCode because the python environment in which I’m writing/running the tests do not have the ROS libraries that the remote library uses (and will have when running on the actual robot).


Your idea to implement a custom remote library that changes behavior based on whether Robot Framework is running sounds interesting. I’ll look into that, thanks!

2 Likes