jnhyperion / HyperRobotFrameworkPlugin

Robot Framework plugin for PyCharm.
34 stars 5 forks source link

keywords collecting with Hybrid library #86

Open mfo7mfo7mfo7 opened 1 month ago

mfo7mfo7mfo7 commented 1 month ago

Hi @jnhyperion Thank you again for your support.

Please correct me if I'm wrong. Robot Hybrid library is a common way to implement keyword. https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#hybrid-library-api I found keywords are not parsed correctly, or checked improper way.

example1

BuiltIn Telnet library

image image image

Get Keyword Names is not a keyword but it checked as valid one.

example 2

i write a demo code for test hybrid library keyword collection.

image

Here are the demo code for your reference. Thank you. Cheers!

# demo.robot

*** Settings ***
Library    demo.Zoo    AS     Zoo

*** Test Cases ***
Test Zoo
    Zoo.Run
    ${name}=     Zoo.Get Name
    Log To Console    ${name}

    Zoo.Switch    dog
    ${name}=     Zoo.Get Name
    Log To Console    ${name}

    Zoo.Run
    Zoo.Sleep
# demo.py

class Animal:
    def __init__(self, name, speed):
        """Initialize an animal with a name and speed."""
        self.name = name
        self.speed = speed

    def get_name(self):
        """Returns the name of the animal."""
        return self.name

    def run(self):
        """Simulates the animal running."""
        return f"{self.name} runs at {self.speed} km/h."

    def sleep(self):
        """Simulates the animal sleeping."""
        return f"{self.name} is sleeping."

import inspect

class Zoo:
    def __init__(self):
        """Initialize the Zoo with a default animal and animal options."""
        self.animals = {
            "cat": Animal(name="Cat", speed=30),
            "dog": Animal(name="Dog", speed=40),
            "lion": Animal(name="Lion", speed=50),
            "elephant": Animal(name="Elephant", speed=25)
        }
        self.current_animal = self.animals["cat"]

    def switch(self, animal_name):
        """Switch the current animal to another animal in the zoo."""
        if animal_name in self.animals:
            self.current_animal = self.animals[animal_name]
        else:
            raise ValueError(f"Animal '{animal_name}' not found in the zoo.")

    def not_a_keyword(self):
        pass

    def get_keyword_names(self):
        """Returns the names of all keywords in this library and the current animal."""
        not_keyword_list = ['not_a_keyword', 'get_keyword_names']

        zoo_methods = [method for method in dir(self) if callable(getattr(self, method)) and not method.startswith("_")]
        for k in not_keyword_list:
            try:
                zoo_methods.remove(k)
            except ValueError:
                pass  # do nothing!

        animal_methods = [method for method in dir(self.current_animal) if callable(getattr(self.current_animal, method)) and not method.startswith("_")]
        publish_kws = zoo_methods + animal_methods
        print(f'publish_kws: {publish_kws}')
        return publish_kws

    def __getattr__(self, name):
        """Delegate attribute access to the current animal if not found in Zoo."""
        if hasattr(self.current_animal, name):
            return getattr(self.current_animal, name)
        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
jnhyperion commented 4 weeks ago
  1. For the special methods in the robot library class, plugin should not parse them as valid keywords, this can be improved.

    image
  2. For your example2, the keywords are generated dynamically during robot runtime, the plugin will never know the keyword names before the robot files are executed, in this case, the library should provide a pyi file to allow plugin to parse the keywords, please see the example of SeleniumLibrary, also refer to: https://github.com/jnhyperion/HyperRobotFrameworkPlugin/issues/67

mfo7mfo7mfo7 commented 3 weeks ago

Hi @jnhyperion thank you again for prompt reply. Basically Example 1 and 2 are same implemented by Hybrid library practices.

Telnet is also the Robot Framework standard libraries, but it doesn't provide pyi officially. (actually Telnet probably is also hard to provide its pyi file since it's grabbing telnetlib's function as its own keywords via get_keyword_names)

As you mentioned that it's generated runtime, sounds like it's hard to do it by HyperRobotFrameworkPlugin. I'm not sure why robocorp's plugin can parse runtime keywords from hybrid/dynamic library. ( just guess maybe you are using more pyCharm plugin way to implement plugin.) https://plugins.jetbrains.com/plugin/16086-robot-framework-language-server

Your plugin is way better better better than robocorp's. Truly! But without runtime keyword parsing, it's kind of struggle with some Robot standard libraries and custom hybrid libraries.

Please consider to add this feature. Thank you. Cheers!!!!

jnhyperion commented 3 weeks ago

@mfo7mfo7mfo7 great to know some other plugins can do this by magics, let me have some research anyway :)