12. Contributing: Developer’s Guide¶
Everybody is encouraged to contribute to the RAFCON project. However, collaboration needs some guidelines. We try to collect all this information in this document, so please stick to this.
When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
Please note that we have a code of conduct, please follow it in all your interactions with the project.
Please check our code style, before making any changes to the code. Please read the commit guidelines, before submitting any commit.
12.1. Getting Started¶
For an introduction of how to install RAFCON as a user, have a look at the website. Also, to get the correct dependencies, follow the instruction given on this site.
The following describes how to get the latest RAFCON version per GitHub.
First, change to the directory in which you want to clone RAFCON:
$ cd /some/personal/path/
Next, clone the RAFCON repository. You can either use the HTTPS URL:
$ git clone https://github.com/DLR-RM/RAFCON.git
or the SSH URL:
$ git clone git@github.com:DLR-RM/RAFCON.git
This must of course only be done once. If you want to get the latest commits after you have cloned the repository, use
$ cd /some/personal/path/rafcon
$ git pull
In order to run RAFCON from the local code base, you have to setup the environment:
$ export PYTHONPATH=/some/personal/path/rafcon/source:$PYTHONPATH
$ export PATH=/some/personal/path/rafcon/bin:$PATH
Now you can run rafcon
to start the RAFCON-GUI or just run rafcon_core
to only launch the core. Hereby,
rafcon
just links to the file /some/personal/path/rafcon/source/rafcon/gui/start.py
and rafcon_core
points to /some/personal/path/rafcon/source/rafcon/core/start.py
, so you could also call these files directly.
IMPORTANT: If you start rafcon for the first time start it this way:
$ RAFCON_CHECK_INSTALLATION=True rafcon
This will install all fonts and gtk-styles.
12.2. Install RAFCON from Sources¶
RAFCON can be also installed from our GitHub releases.
If you don’t want to edit the source code of RAFCON, it can be installed directly from source:
pip install /install/directory/rafcon/ --user
If you want to be able to change the source code, you can install RAFCON in editable mode.
pip install --editable /install/directory/rafcon/ --user
Any changes in /install/directory/rafcon/source
will take effect when launching RAFCON.
12.3. Running and writing tests¶
Running tests with tox
¶
The simplest and most reliable way of running the tests is using tox. If you have not installed tox, do so using
$ pip install tox
Then, in the simplest case you just call tox in the root directory of the RAFCON repository:
$ tox
This will run the following environments:
py27
,py3[4-7]
: Runs the test using the according Python interpretercoverage
: Runs the tests using Python 2.7 with a coverage reportdocs
: Builds the documentation and verifies all linkscheck
: Verifies the sdist and wheel file
Specific environments can be run with the -e
option:
$ tox -e 2.7,3.4
$ tox -e docs
When running the tests (py27
, py3[4-7]
or coverage
), you can pass custom options to pytest by listing
them after tox [tox options] --
. The default pytest options are -vx -m "(core or gui or share_elements) and not
unstable"
.
$ tox -e 2.7 -- -x -m "core"
$ tox -- -s -k "test__destruct"
Tox creates a virtualenv for each environment, based on the dependencies defined in setup.py
and tox.ini
.
These are only generated on the first run of an environment. If the dependencies change, you need to tell tox to
recreate the environments using the -r/--recreate
option:
$ tox -re py2.7
The RAFCON tests are annotated with a number of markers, allowing you to select specific tests:
core
,gui
,share_elements
,network
: Tests located in a folder with that nameunstable
: GUI tests that do not run very reliable (e.g. because of the window manager)
Pytest allows you to select tests based on markers using the -m
option. Markers can be combined using
not
, and
, or
and paranthesis:
$ tox -e 2.7 -- -x -m "gui and not unstable"
Writing tests¶
RAFCON provides a lot of tests in the tests/
folder. Many of these tests are integration tests, unit tests are
unfortunately often missing. If a test only uses imports from rafcon.core
, it is to be placed in tests/core/
,
otherwise in tests/gui/
.
RAFCON uses pytest
as testing framework. It e.g. auto detects your test files starting with test_*
. Please have
a look at the documentation before writing tests: https://pytest.org/
GUI tests¶
When you want to write an integration test using the GUI, a custom fixture named gui
is provided
(tests/gui/conftest.py
). Simply add gui
as parameter to your test (no import is required for tests residing
beneath test/gui/
). The fixture automatically starts the GUI before the test and closes it thereafter.
Important: Do not import any module from rafcon.gui
outside of a function! Otherwise, models might be created
withing the wrong thread, which leads to gtkmvc
forwarding observer notifications asynchronously (via idle_add
)
instead of synchronously.
When calling an operation that causes changes (in the core, models, GUI), you need to add the operation to the GTK queue
and wait until the operation has finished. This is simply done by calling gui(function_reference, *args, **kwargs)
instead of function_reference(*args, **kwargs)
.
If your test commands causes any logger.warning
or logger.error
, you need to specify the expected numbers. Do so
by calling gui.expected_warnings += 1
, respectively gui.expected_errors += 1
, directly after the command that
causes the warning/error.
The fixture will load the default core and gui config options and the libraries generic
and
unit_test_state_machines
. If you want to override certain settings or add more libraries, use the following
decorator:
@pytest.mark.parametrize('gui', [{
"gui_config": {
'AUTO_BACKUP_ENABLED': True,
'HISTORY_ENABLED': True
},
"libraries": {
"ros": os.path.join(testing_utils.EXAMPLES_PATH, "libraries", "ros_libraries"),
"turtle_libraries": os.path.join(testing_utils.EXAMPLES_PATH, "libraries", "turtle_libraries")
}
}], indirect=True, ids=["with history, auto backup, ros and turtle libraries"])
def test_name(gui):
pass # test code
Using the ids
argument, you can specify a label for your configuration. Other possible keys are core_config
(dict
), runtime_config
(dict
) and with_gui
(bool
, for tests that operate on models but do not
require the controllers and views). It is also possible to combine this with parameter sets:
config_options = {
"gui_config": {
'HISTORY_ENABLED': True
}
}
@pytest.mark.parametrize("gui,state_path,recursive,rel_size", [
(config_options, state_path_root, False, (40, 40)),
(config_options, state_path_root, True, (40, 40)),
(config_options, state_path_P, False, (20, 20)),
(config_options, state_path_P, True, (20, 20)),
], indirect=["gui"])
def test_name(gui, state_path, recursive, rel_size, monkeypatch):
pass # test code
Note that in this case, you need to set the indirect
parameter to ["gui"]
.
The gui
fixture offers some features:
if you want to restart the GUI within a test, call
gui.restart()
the fixture provides shorthand access the gui singletons via
gui.singletons
and core singletons viagui.core_singletons
, without requiring any further imports.if you want to run a test after the GUI was closed, you can set the function to be run via
gui.post_test = functools.partial(function_reference, *args, **kwargs)
12.4. Code of Conduct¶
Our Pledge¶
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
Our Standards¶
Examples of behavior that contributes to creating a positive environment include:
Using welcoming and inclusive language
Being respectful of differing viewpoints and experiences
Gracefully accepting constructive criticism
Focusing on what is best for the community
Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
The use of sexualized language or imagery and unwelcome sexual attention or advances
Trolling, insulting/derogatory comments, and personal or political attacks
Public or private harassment
Publishing others’ private information, such as a physical or electronic address, without explicit permission
Other conduct which could reasonably be considered inappropriate in a professional setting
Our Responsibilities¶
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
Scope¶
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Enforcement¶
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at rafcon@dlr.de. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.
Attribution¶
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/ <https://www.contributor-covenant.org/version/1/4/>`__
12.5. Bugs & Feature request¶
Please use GitHub Issues to report bugs. This page can also be used for feature requests.
12.6. Code style¶
Style guides¶
We follow the rules PEP 8 (Style Guide for Python Code) and the DLR Coding Conventions
with some minor exceptions:
Line length is limited to 120 characters (instead of 79)
no
__version__
in header (exceptrafcon/__init__.py
)
For docstrings (and generally for the documentation), we format using reStructuredText for Sphinx. More information on this is available in another PEP 287.
PyCharm Coding Style/Conventions¶
Our recommended tool for development is PyCharm. This IDE makes it easy for you to follow our style guides. We prepared a settings file for “Code styles” and “Inspection profiles”, which you just need to import: File > Import Settings > [project root/.idea/recommended_code_style_and_inspections.jar.
12.7. Git commit guidelines¶
General Git commit conventions¶
Git is used as our version control system. Please keep the following points in mind when committing to the repository:
if you are not a core developer, all changes must be bundled in pull requests
therefore, a new branch is required for new features and bug fixes
try to keep pull requests small, this eases the review and will speed up the merge of the branch
before submitting a pull request, make sure that all tests are passed
new features require new unit tests
each functional/logical change gets its own commit (try to keep them small)
no excessive use of
logger.debug
outputs (in commits) and never commitprint
commands
Git commit messages¶
When looking at the Git commit history, it should be easy to see what changes have been performed. This is why it is important to have good commit messages.
What to do:
Use imperative (Add file …)
First line is the caption of the commit (should be less than 72 chars)
summarizes the commit
mentions which code is affected (e.g. by stating the changes module/class)
Second line is blank
Following lines give a longer description and list changes in detail (use “- ” or “* ” as bullet points)
Why is the change necessary
How does it address the issue
Possible side effects
Give Issue/Feature-ID of GitHub Issues
Clos[e[s]]|Fix[e[s]]|Resolve[e[s]] #1234567
– use one of the keywords to automatically close an issueRelates to issue #1234567
– state issue id also when the issue is not supposed to be closed
What not to do:
Try to avoid using the
-m <msg>
/--message=<msg>
flag togit commit
Do not assume that the reader knows the details about bugs, features or previous commits
Neither assume that the reader looks at the commited code
Setting up your system
Add this line to your ~/.vimrc
to add spell checking and automatic wrapping to your commit messages.
autocmd Filetype gitcommit setlocal spell textwidth=72
Explaining examples
This is a good example for a commit message:
Example: Capitalized, short (<72 chars) summary
More detailed explanatory text. Wrap it to about 72 characters. Think
of first line as the subject of an email and the rest of the text as
the body. The blank line separating the summary from the body is
critical (unless you omit the body entirely); tools like rebase can get
confused if you run the two together.
Further paragraphs come after blank lines.
- Dash '-' and asterisk '*' are both fine, followed by a space
- Use a hanging indent
Th following link shows some bad examples: https://wiki.openstack.org/wiki/GitCommitMessages#Some_examples_of_bad_practice
Sources
12.8. Steps for releasing¶
Steps to perform, when releasing a new version of RAFCON (this section is only for releasing the tool inside our institute):
Decide about the new version number
Should the release be a patch or minor release? Or even a major release? Check the latest version number and adjust it appropriately.
Create a release Branch
Create a new branch from the latest commit you want to include into the new release. Optionally, if there are new untested feature, which you don’t want to include into the release, first go back to a previous commit:
$ git checkout [hash]Then, create the new branch and check it out:
$ git checkout -b release-[new version number]
Fix relevant issues (optional)
Within your new branch, you shouldn’t integrate any new features, but only solve issues. The
develop
andfeature-xy
branches should be used for new features. Take your time to ensure that the most critical issues are fixed and RAFCON is running stable.
Create a test state machine for the new version (only minor/major releases)
For each minor release, a state machine must be created to ensure backwards compatibility using a special test.
Therefore, open the state machine in
[project directory]/tests/assets/unit_test_state_machines/backward_compatibility/[latest version number]
and save it to the same folder with the correct version as library name. Commit your changes.
Check tests
Run all tests and verify that they do all pass. If not, fix them! Also check the BuildBot. Commit your changes.
Check the changelog
Open
[project directory]/doc/Changelog.rst
and verify that all changes are included within the correct version number. Compare withgit log
and the latest closed issues on GitHub. Commit your changes.
Build style files
Build
*.css
files from*.scss
files.$ ./compile_scss.sh $ git add share/themes/RAFCON/gtk-3.0/*.css --force
Apply the version number
Update the version number in
[project directory]/VERSION
.Update the
version
in[project directory]/CITATION.cff
.Update the
date-released
in[project directory]/CITATION.cff
.Run
cffconvert --ignore-suspect-keys --outputformat zenodo --outfile .zenodo.json
(see “Making software citable”, requires Python 3)Commit and push your changes.
Merge to master
When everything is prepared, you can merge the release branch into the master branch:
$ git push release-[new version number] $ git checkout master $ git pull master $ git merge release-[new version number] $ git push
Merge to develop
Merge all changes back into the develop branch:
$ git checkout develop $ git pull $ git merge release-[new version number]] $ git push
Publish new release to PyPi
Create a new distribution file and publish it on PyPi:
$ rm dist/* $ python3 setup.py sdist bdist_wheel $ twine upload dist/*
Publish to GitHub
Publish the changes to GitHub and GitHub Enterprise (assuming
github
is your GitHub remote name):$ git push github $ git checkout master $ git push githubMake a release on GitHub by navigating to https://github.com/DLR-RM/RAFCON/releases/new. Enter the new version number in the “Tag version” field. Optioanlly add a release title and decription. Click “Publish release”.
Force build of GitHub pages
Push an empty commit to the
gh-pages
branch:$ git checkout gh-pages $ git commit -m 'rebuild pages' --allow-empty $ git push $ git push github
12.9. Translating RAFCON¶
RAFCON is prepared for internationalization (i18n) and in fact ships with a basic German translation. This translation can be extended and new translations can be added. We are happy about your support here!
Add translatable strings¶
In order to allow for translating strings in RAFCON, they need to be marked in the source code.
All you need to do for this is wrapping it in the gettext
function _()
, e.g. _("Hello world")
.
Strings in glade files do not need to be marked, but the label
property needs an attribute translatable="yes"
:
<object class="GtkButton">
<property name="label" translatable="yes">Translatable button label</property>
...
</object>
Generating a translation template¶
If new translatable strings have been added or you want to create translations for a new language, a POT file (Portable Object Template) has to be created:
cd /dir/to/rafcon/repository
./bin/i18n-gen-msg
This will create a rafcon.pot file in the source/rafcon/locale folder.
Updating an existing translation¶
If you want to update a translation, open the according PO file, located in source/rafcon/locale (e.g. de.po) with Poedit. Then go to Catalog => Update from POT file and select the generated rafcon.pot file. This step is optional, if no new translatable strings have been added.
With Poedit, you can add new translations for strings comfortably. Alternatively, you can directly edit the PO files with the text editor of your choice.
Creating a new translation¶
Open Poedit. Go to File => New From POT/PO File… and enter the name of the new language. Start adding your translations. When you are done, store the file in source/rafcon/locale with the language’ locale as name, e.g. de.po for the German translation.
12.10. HowTo on creating a splashscreen¶
This is a guide on creating a splash screen picture using the GIMP template.
Location The template file is located in source/rafcon/gui/assets/themes/templates. The standard place for images to be loaded into the startup splashscreen is source/rafcon/gui/assets/splashscreens.
File The template is a .xcf which is the native project format of GIMP. This guide is also using GIMP.
Process
Open the template with GIMP.
First select single window mode:
Windows -> Single Window Mode
. This will save you a lot of time and struggle.Check if the Layer widget is present. It should contain five items, one layer
Background
and fourRAFCON_logo
layers. If not present, pressCtrl + L
.Now go to
File -> Open as Layers
and select a picture of your choice.Check if the image is placed between
Background
andRAFCON_Logo
in the Layer widget. If not, drag it in the correct position.Now select the layer with your picture in the Layer widget.
Go to the Tools widget and select the
Scale
tool.Click on your picture with the
Scale
tool and fit it in the 570x320px field.If your not satisfied with your result, try the “Move” tool and move the layer around.
Notice the small eye in the Layer widget next to a layer? Activate the visibility of a logo that serves your needs the best.
If everything is done, ignore the invisible layers and click on
File -> Export As
. Export the picture as whatever python is able to load into a pixelbuffer, .jpg and .png work fine.Voila, there is your normed splash screen!