Important Currently, bigger changes are going on in qutebrowser, as part of a student research project about adding a plugin API to qutebrowser and moving a lot of code from the code into plugins. Due to that, bandwidth for pull request review is currently very limited, and contributions might lead to merge conflicts due to ongoing refactorings.

I <3
[Of course, that says <3 in HTML.]
contributors!

This document contains guidelines for contributing to qutebrowser, as well as useful hints when doing so.

If anything mentioned here would prevent you from contributing, please let me know, and contribute anyways! The guidelines are meant to make life easier for me, but if you don’t follow everything in here, I won’t be mad at you. In fact, I will probably change it for you.

If you have any problems, I’m more than happy to help! You can get help in several ways:

Finding something to work on

Chances are you already know something to improve or add when you’re reading this. It might be a good idea to ask on the mailing list or IRC channel to make sure nobody else started working on the same thing already.

If you want to find something useful to do, check the issue tracker. Some pointers:

If you prefer C++ or Javascript to Python, see the relevant issues which involve work in those languages:

  • C++ (mostly work on Qt, the library behind qutebrowser)

  • JavaScript

There are also some things to do if you don’t want to write code:

  • Help the community, e.g., on the mailinglist and the IRC channel.

  • Improve the documentation.

  • Help on the website and graphics (logo, etc.).

Using git

qutebrowser uses git for its development. You can clone the repo like this:

git clone https://github.com/qutebrowser/qutebrowser.git

If you don’t know git, a git cheatsheet might come in handy. Of course, if using git is the issue which prevents you from contributing, feel free to send normal patches instead, e.g., generated via diff -Nur.

Getting patches

The preferred way of submitting changes is to fork the repository and to submit a pull request.

If you prefer to send a patch to the mailinglist, you can generate a patch based on your changes like this:

git format-patch origin/master 1
1 Replace master by the branch your work was based on, e.g., origin/develop.

Running qutebrowser

After installing qutebrowser via tox, you can run .venv/bin/qutebrowser --debug --temp-basedir to test your changes with debug logging enabled and without affecting existing running instances.

Alternatively, you can install qutebrowser’s dependencies system-wide and run python3 -m qutebrowser --debug --temp-basedir.

Useful utilities

Checkers

qutebrowser uses tox to run its unittests and several linters/checkers.

Currently, the following tox environments are available:

  • Tests using pytest:

    • py35, py36: Run pytest for python 3.5/3.6 with the system-wide PyQt.

    • py36-pyqt57, …, py36-pyqt59: Run pytest with the given PyQt version (py35-* also works).

    • py36-pyqt59-cov: Run with coverage support (other Python/PyQt versions work too).

  • flake8: Run various linting checks via flake8.

  • vulture: Run vulture to find unused code portions.

  • pylint: Run pylint static code analysis.

  • pyroma: Check packaging practices with pyroma.

  • eslint: Run ESLint javascript checker.

  • check-manifest: Check MANIFEST.in completeness with check-manifest.

  • mkvenv: Bootstrap a virtualenv for testing.

  • misc: Run scripts/misc_checks.py to check for:

    • untracked git files

    • VCS conflict markers

    • common spelling mistakes

The default test suite is run with tox; the list of default environments is obtained with tox -l.

Please make sure the checks run without any warnings on your new contributions.

There’s always the possibility of false positives; the following techniques are useful to handle these:

  • Use _foo for unused parameters, with foo being a descriptive name. Using _ is discouraged.

  • If you think you have a good reason to suppress a message, then add the following comment:

    # pylint: disable=message-name

    Note you can add this per line, per function/class, or per file. Please use the smallest scope which makes sense. Most of the time, this will be line scope.

  • If you really think a check shouldn’t be done globally as it yields a lot of false-positives, let me know! I’m still tweaking the parameters.

Running Specific Tests

While you are developing you often don’t want to run the full test suite each time.

Specific test environments can be run with tox -e <envlist>.

Additional parameters can be passed to the test scripts by separating them from tox arguments with --.

Examples:

# run only pytest tests which failed in last run:
tox -e py35 -- --lf

# run only the end2end feature tests:
tox -e py35 -- tests/end2end/features

# run everything with undo in the generated name, based on the scenario text
tox -e py35 -- tests/end2end/features/test_tabs_bdd.py -k undo

# run coverage test for specific file (updates htmlcov/index.html)
tox -e py35-cov -- tests/unit/browser/test_webelem.py

Profiling

In the scripts/ subfolder there’s a run_profile.py which profiles the code and shows a graphical representation of what takes how much time.

It uses the built-in Python cProfile module and can show the output in four different ways:

Debugging

There are some useful functions for debugging in the qutebrowser.utils.debug module.

When starting qutebrowser with the --debug flag, you also get useful debug logs. You can add --logfilter [!]category[,category,…] to restrict logging to the given categories.

With --debug there are also some additional debug-* commands available, for example :debug-all-objects and :debug-all-widgets which print a list of all Qt objects/widgets to the debug log — this is very useful for finding memory leaks.

Hints

Python and Qt objects

For many tasks, there are solutions available in both Qt and the Python standard library.

In qutebrowser, the policy is usually to use the Python libraries, as they provide exceptions and other benefits.

There are some exceptions to that:

  • QThread is used instead of Python threads because it provides signals and slots.

  • QProcess is used instead of Python’s subprocess.

  • QUrl is used instead of storing URLs as string, see the handling URLs section for details.

When using Qt objects, two issues must be taken care of:

  • Methods of Qt objects report their status with their return values, instead of using exceptions.

    If a function gets or returns a Qt object which has an .isValid() method such as QUrl or QModelIndex, there’s a helper function ensure_valid in qutebrowser.utils.qtutils which should get called on all such objects. It will raise qutebrowser.utils.qtutils.QtValueError if the value is not valid.

    If a function returns something else on error, the return value should carefully be checked.

  • Methods of Qt objects have certain maximum values based on their underlying C++ types.

    To avoid passing too large of a numeric parameter to a