These guidelines instruct how to submit issues and contribute code or documentation to the Robot Framework project. There are also many other projects in the larger Robot Framework ecosystem that you can contribute to. If you notice a library or tool missing, there is hardly any better way to contribute than creating your own project. Other great ways to contribute include answering questions and participating discussion on our Slack, Forum, LinkedIn group, or other such discussion forum, speaking at conferences or local events, and spreading the word about the framework otherwise.
These guidelines expect readers to have a basic knowledge about open source as well as why and how to contribute to an open source project. If you are new to these topics, it may be a good idea to look at the generic Open Source Guides first.
Bugs and enhancements are tracked in the issue tracker. If you are unsure
if something is a bug or is a feature worth implementing, you can
first ask on the #devel channel on our Slack. Slack and other such forums,
not the issue tracker, are also places where to ask general questions about
the framework.
Before submitting a new issue, it is always a good idea to check if the same bug or enhancement is already reported. If it is, please add your comments to the existing issue instead of creating a new one.
Explain the bug you have encountered so that others can understand it and preferably also reproduce it. Key things to include in good bug report:
Version information
- Robot Framework version
- Python interpreter version
- Operating system and its version
Typically including the output of
robot --versionis enough.Steps to reproduce the problem. With more complex problems it is often a good idea to create a short, self contained, correct example (SSCCE).
Possible error message and traceback.
Notice that all information in the issue tracker is public. Do not include any confidential information there.
Describe the new feature and use cases for it in as much detail as possible. Especially with larger enhancements, be prepared to contribute the code in the form of a pull request as explained below. If you would like to sponsor a development of a certain feature, you can contact the Robot Framework Foundation. Consider also would it be better to implement new functionality as a separate library or tool outside the core framework.
If you have fixed a bug or implemented an enhancement, you can contribute your changes via GitHub's pull requests. This is not restricted to code, on the contrary, fixes and enhancements to documentation and tests alone are also very valuable.
Often you already have a bug or an enhancement you want to work on in your mind, but you can also look at the issue tracker to find bugs and enhancements submitted by others. The issues vary significantly in complexity and difficulty, so you can try to find something that matches your skill level and knowledge. There are two specific labels to look for when looking for something to contribute:
- good first issue
- These issues typically do not require any knowledge of Robot Framework internals and are generally easy to implement or fix. Thus these issues are especially good for new contributors.
- help wanted
- These issues require external help to get implemented or fixed.
On GitHub pull requests are the main mechanism to contribute code. They are easy to use both for the contributor and for the person accepting the contribution, and with more complex contributions it is easy also for others to join the discussion. Preconditions for creating pull requests are having a GitHub account, installing Git and forking the Robot Framework project.
GitHub has good articles explaining how to set up Git, fork a repository and use pull requests and we do not go through them in more detail. We do, however, recommend to create dedicated topic branches for pull requests instead of creating them based on the master branch. This is especially important if you plan to work on multiple pull requests at the same time.
Code formatting and other tasks require external tools to be installed. All of them are listed in the requirements-dev.txt file and you can install them by running:
pip install -r requirements-dev.txt
Robot Framework follows the general Python code conventions defined in PEP-8. Code is automatically formatted, but manual adjustments may sometimes be needed.
The code is automatically linted and formatted using a combination of tools that are driven by an Invoke task:
invoke format
Make sure to run this command before creating a pull request!
By default the task formats Python code under src, atest and utest
directories, but it can be configured to format only certain directories
or files:
invoke format -t src
Formatting is done in multiple phases:
- Code is linted using Ruff . If linting fails, the formatting process is stopped.
- Code is formatted code using Black. We plan to switch to Ruff as soon as they stop removing the empty row after the class declaration.
- Multiline imports are reformatted using isort. We use the "hanging grid grouped" style to use less vertical space compared to having each imported item on its own row. Public APIs using redundant import aliases are not reformatted, though.
Tool configurations are in the pyproject.toml file.
Automatic formatting works pretty well, but there are some cases where the results are suboptimal and manual adjustments are needed.
Note
As a contributor, you do not need to care about this if you do not want to.
Maintainers can fix these issues themselves after merging your pull request.
Just running the aforementioned invoke format is enough.
Automatic formatting has three modes how to handle lists:
- Short lists are formatted on a single row. This includes list items and opening and closing braces and other markers.
- If all list items fit into a single row, but the whole list with opening and closing markers does not, items are placed into a single row and opening and closing markers are on their own rows.
- Long lists are formatted so that all list items are own their own rows and opening and closing markers are on their own rows as well.
In addition to lists and other containers, the above applies also to function calls and function signatures:
def short(first_arg: Iterable[int], second_arg: int = 0) -> int:
...
def medium(
first_arg: Iterable[float], second_arg: float = 0.0, third_arg: bool = True
) -> int:
...
def long(
first_arg: Iterable[float],
second_arg: float = 0.0,
third_arg: bool = True,
fourth_arg: bool = False,
) -> int:
...This formatting is typically fine, but similar code being formatted differently
in a single file can look inconsistent. Having multiple items in a single row, as in
the medium example above, can also make the code hard to read. A simple fix
is forcing list items to own rows by adding a magic trailing comma and running
auto-formatter again:
def short(first_arg: Iterable[int], second_arg: int = 0) -> int:
...
def medium(
first_arg: Iterable[float],
second_arg: float = 0.0,
third_arg: bool = True,
) -> int:
...
def long(
first_arg: Iterable[float],
second_arg: float = 0.0,
third_arg: bool = True,
fourth_arg: bool = False,
) -> int:
...Lists and signatures fitting into a single line, such as the short example above,
should typically not be forced to multiple lines.
Automatically formatting all list items into own rows uses a lot of vertical space. This is typically not a problem, but with long lists having simple items it can be somewhat annoying:
class Branches(
BaseBranches[
'Keyword',
'For',
'While',
'Group',
'If',
'Try',
'Var',
'Return',
'Continue',
'Break',
'Message',
'Error',
IT,
]
):
__slots__ = ()
added_in_rf60 = {
"bg",
"bs",
"cs",
"de",
"en",
"es",
"fi",
"fr",
"hi",
"it",
"nl",
"pl",
"pt",
"pt-BR",
"ro",
"ru",
"sv",
"th",
"tr",
"uk",
"zh-CN",
"zh-TW",
}The best way to fix this is disabling formatting altogether with the # fmt: skip
pragma. The code should be formatted so that opening and closing list markers
are on their own rows, list items are wrapped, and the # fmt: skip pragma
is placed after the closing list marker:
class Branches(BaseBranches[
"Keyword", "For", "While", "Group", "If", "Try", "Var", "Return", "Continue",
"Break", "Message", "Error", IT,
]): # fmt: skip
__slots__ = ()
added_in_rf60 = {
"bg", "bs", "cs", "de", "en", "es", "fi", "fr", "hi", "it", "nl", "pl",
"pt", "pt-BR", "ro", "ru", "sv", "th", "tr", "uk", "zh-CN", "zh-TW",
} # fmt: skipAutoformatting handles Boolean expressions having two items that do not fit into a single line really strangely:
ext = getattr(self.parser, 'EXTENSION', None) or getattr(
self.parser, 'extension', None
)
return self._get_runner_from_resource_files(
name
) or self._get_runner_from_libraries(name)Expressions having three or more items would be grouped with parentheses and
there is an issue about doing that also if there are two items. A workaround
is using parentheses and disabling formatting with the # fmt: skip pragma:
ext = (
getattr(self.parser, 'EXTENSION', None)
or getattr(self.parser, 'extension', None)
) # fmt: skip
return (
self._get_runner_from_resource_files(name)
or self._get_runner_from_libraries(name)
) # fmt: skipAutoformatting normalizes the number of spaces before an inline comment into two. That is typically fine, but if subsequent lines use inline comments, the result can be suboptimal:
TypeHint = Union[
type, # Actual type.
str, # Type name or alias.
UnionType, # Union syntax (e.g. `int | float`).
'tuple[TypeHint, ...]', # Tuple of type hints. Behaves like a union.
]A solution is manually aligning comments and disabling autoformatting:
TypeHint = Union[
type, # Actual type.
str, # Type name or alias.
UnionType, # Union syntax (e.g. `int | float`).
"tuple[TypeHint, ...]" # Tuple of type hints. Behaves like a union.
] # fmt: skipIn the above example formatting is disabled with the # fmt: skip pragma, but
it does not work if inline comments are not related to a single statement. In such
cases the # fmt: off and # fmt: on pair can be used instead. In this example
formatting is disabled to allow aligning constant values in addition to comments:
# fmt: off
INFO_PRINTED = 251 # --help or --version
DATA_ERROR = 252 # Invalid data or cli args
STOPPED_BY_USER = 253 # KeyboardInterrupt or SystemExit
FRAMEWORK_ERROR = 255 # Unexpected error
# fmt: onDocstrings should be added to public APIs, but they are not generally needed in internal code. When docstrings are added, they should follow PEP-257. See API documentation section below for more details about documentation syntax, generating API docs, etc.
All public APIs must have type hints and adding type hints also to new internal code is recommended. Full type coverage is not a goal at the moment, though.
Type hints should follow the Python Typing Best Practices with the following exceptions:
- Annotation features are restricted to the minimum Python version supported by Robot Framework.
- Annotations should use the stringified format for annotations not supported
by the minimum supported Python version. For example,
"int | float"instead ofUnion[int, float]and"list[int]"instead ofList[int]. Type aliases are an exception to this rule. - Keywords accepting either an integer or a float should typically be annotated as
int | floatinstead of justfloat. This way argument conversion tries to first convert arguments to an integer and only converts to a float if that fails. - No
-> Noneannotation on functions that do not explicitly return anything.
With new features adequate documentation is as important as the actual functionality. Different documentation is needed depending on the issue.
Robot Framework's features are explained in the User Guide. It is generated using a custom script based on the source in reStructuredText format. For more details about editing and generating it see doc/userguide/README.rst.
If standard libraries distributed with Robot Framework are enhanced, also their documentation needs to be updated. Keyword documentation is created from docstrings using the Libdoc tool. Documentation must use Robot Framework's own documentation formatting and follow these guidelines:
- All new enhancements or changes should have a note telling when the change
was introduced. Often adding something like
New in Robot Framework 7.3.is enough. - Other keywords and sections in the library introduction can be referenced
with internal links created with backticks like
`Example Keyword`. - When referring to arguments, argument names must use inline code style
created with double backticks like
``argument``. - Examples are recommended whenever the new keyword or enhanced functionality is not trivial.
Library documentation can be generated using Invoke by running command
invoke library-docs <name>
where <name> is the name of the library or its unique prefix. Run
invoke --help library-docs
for more information.
Modules and classes defined to be public should have API documentation. We do not generally use API docs with internal code because it is so hard to keep the docs in sync with the code. Instead we try to keep the code as clean and easy to understand as possible.
API docs are created using docstrings following guidelines defined in PEP-257. They are converted to HTML using Sphinx and its autodoc extension. Documentation can be created locally using doc/api/generate.py script that unfortunately creates a lot of errors on the console. Releases API docs are visible at https://robot-framework.readthedocs.org/.
When submitting a pull request with a new feature or a fix, you should always include tests for your changes. These tests prove that your changes work, help prevent bugs in the future, and help document what your changes do. Depending on the change, you may need acceptance tests, unit tests or both.
Make sure to run all of the tests before submitting a pull request to be sure that your changes do not break anything. If you can, test in multiple environments and interpreters (Windows, Linux, OS X, different Python versions etc). Pull requests are also automatically tested by GitHub Actions.
If you want to manually verify the changes, an easy approach is directly
running the src/robot/run.py script that is part of Robot Framework
itself. Alternatively, you can use the rundevel.py script that sets
some command line options and environment variables to ease executing tests
under the atest/testdata directory. It also automatically creates a
tmp directory in the project root and writes all outputs there.
Most of Robot Framework's testing is done using acceptance tests that naturally use Robot Framework itself for testing. Every new functionality or fix should generally get one or more acceptance tests. See atest/README.rst for more details about creating and executing them.
Unit tests are great for testing internal logic and should be added when appropriate. For more details see utest/README.rst.