MarketSquare / robotframework-robocop

Tool for static code analysis of Robot Framework language
Apache License 2.0
186 stars 38 forks source link

[Bug] Can't do relative imports in custom rules folder #1122

Open Lakitna opened 2 weeks ago

Lakitna commented 2 weeks ago

What happened?

I have a bunch of custom rules that only make sense for our organization/situation. But it can be a real pain to structure code written for custom rules.

As an example: I'm working on custom rules related to keyword names. For this, I wrote a keyword name parser to simplify the rule code. I want to use this like so:

robocop_rules/
- keyword_name_checker.py
- keyword_name_parser.py
# keyword_name_checker.py

from .keyword_name_parser import KeywordNameParser
# keyword_name_parser.py

class KeywordNameParser:
  pass

Running Robocop with this code will cause Python to raise the following error:

ImportError: attempted relative import with no known parent package

Changing the import line to the following also does not work:

# keyword_name_checker.py

from robocop_rules.keyword_name_parser import KeywordNameParser
ModuleNotFoundError: No module named 'robocop_rules'

And this is also no dice

# keyword_name_checker.py

from keyword_name_parser import KeywordNameParser
ModuleNotFoundError: No module named 'keyword_name_parser'

What command/code did you try to run?

robocop .

What is the full error message?

See above

What did you expect to happen instead?

I expect the ability to import python files.

Operating System

Windows

Robocop version

5.4.0

Lakitna commented 2 weeks ago

I have looked into this a bit and found a potential solution to this issue. However, it does not solve the relative import version.

The solution adds a few things to this function: https://github.com/MarketSquare/robotframework-robocop/blob/master/robocop/checkers/__init__.py#L200-L201

    def get_external_modules(self):
+        for ext_rule_path in self.external_rules_paths:
+            sys.path.append(str(Path(ext_rule_path).parent))
+            sys.path.append(ext_rule_path)
+
        return self.modules_from_paths([*self.external_rules_paths], recursive=True)

It's a bit of a hacky solution though. With this change, the following imports are supported:

# keyword_name_checker.py

import robocop_rules.sub_folder.keyword_name_parser
import sub_folder.keyword_name_parser
import keyword_name_parser

Where robocop_rules is my external rules folder.


If you can't think of a nicer solution, I can make a PR to see if this change impacts any tests.