Contribution guidelines ======================= 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. .. contents:: :depth: 2 :local: Submitting issues ----------------- 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. Reporting bugs ~~~~~~~~~~~~~~ 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: 1. Version information - Robot Framework version - Python interpreter version - Operating system and its version Typically including the output of ``robot --version`` is enough. 2. Steps to reproduce the problem. With more complex problems it is often a good idea to create a `short, self contained, correct example (SSCCE) `_. 3. Possible error message and traceback. Notice that all information in the issue tracker is public. Do not include any confidential information there. Enhancement requests ~~~~~~~~~~~~~~~~~~~~ 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. Code contributions ------------------ 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. Choosing something to work on ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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. __ https://github.com/robotframework/robotframework/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22 __ https://github.com/robotframework/robotframework/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22 Pull requests ~~~~~~~~~~~~~ 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. Development dependencies ~~~~~~~~~~~~~~~~~~~~~~~~ Code formatting and other tasks require external tools to be installed. All of them are listed in the ``_ file and you can install them by running:: pip install -r requirements-dev.txt Coding conventions ~~~~~~~~~~~~~~~~~~ Robot Framework follows the general Python code conventions defined in `PEP-8 `_. Code is `automatically formatted`__, but `manual adjustments`__ may sometimes be needed. __ `Automatic formatting`_ __ `Manual formatting adjustments`_ Automatic formatting '''''''''''''''''''' 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: 1. Code is linted using `Ruff `_ . If linting fails, the formatting process is stopped. 2. 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`__. 3. 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 ``_ file. __ https://github.com/astral-sh/ruff/issues/9745 __ https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html#5-hanging-grid-grouped __ https://typing.python.org/en/latest/spec/distributing.html#import-conventions Manual formatting adjustments ''''''''''''''''''''''''''''' 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. Force lists to have one item per row ```````````````````````````````````` 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: .. sourcecode:: python 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: .. sourcecode:: python 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. __ https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#the-magic-trailing-comma Force multi-line lists to have multiple items per row ````````````````````````````````````````````````````` 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: .. sourcecode:: python 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: .. sourcecode:: python 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: skip Handle Boolean expressions `````````````````````````` Autoformatting handles Boolean expressions having two items that do not fit into a single line *really* strangely: .. sourcecode:: python 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: .. sourcecode:: python 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: skip __ https://github.com/psf/black/issues/2156 Inline comment handling ``````````````````````` Autoformatting 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__: .. sourcecode:: python 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: .. sourcecode:: python 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: skip In 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: .. sourcecode:: python # 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: on __ https://github.com/psf/black/issues/4651 Docstrings '''''''''' Docstrings 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. Type hints '''''''''' 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 of ``Union[int, float]`` and ``"list[int]"`` instead of ``List[int]``. Type aliases are an exception to this rule. - Keywords accepting either an integer or a float should typically be annotated as ``int | float`` instead of just ``float``. This way argument conversion tries to first convert arguments to an integer and only converts to a float if that fails. - No ``-> None`` annotation on functions that do not explicitly return anything. Documentation ~~~~~~~~~~~~~ With new features adequate documentation is as important as the actual functionality. Different documentation is needed depending on the issue. User Guide '''''''''' 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 ``_. Libraries ''''''''' 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 where ```` is the name of the library or its unique prefix. Run :: invoke --help library-docs for more information. API documentation ''''''''''''''''' 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 ``_ script that unfortunately creates a lot of errors on the console. Releases API docs are visible at https://robot-framework.readthedocs.org/. Tests ~~~~~ 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. Executing changed code '''''''''''''''''''''' If you want to manually verify the changes, an easy approach is directly running the ``_ script that is part of Robot Framework itself. Alternatively, you can use the ``_ script that sets some command line options and environment variables to ease executing tests under the ``_ directory. It also automatically creates a ``tmp`` directory in the project root and writes all outputs there. Acceptance tests '''''''''''''''' 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 ``_ for more details about creating and executing them. Unit tests '''''''''' Unit tests are great for testing internal logic and should be added when appropriate. For more details see ``_.