2014-06-18 20:35:08 +02:00
qutebrowser HACKING
===================
2014-06-20 07:52:56 +02:00
The Compiler <mail@qutebrowser.org>
:icons:
:data-uri:
:toc:
2014-06-18 20:35:08 +02:00
2014-06-20 07:52:56 +02:00
I `<3` footnote:[Of course, that says `<3` in HTML.] contributors!
2014-06-20 11:07:03 +02:00
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 only meant to make life easier
for me, but if you don't follow anything in here, I won't be mad at you. I will
2014-06-20 07:52:56 +02:00
probably change it for you then, though.
2014-06-20 11:07:03 +02:00
If you have any problems, I'm more than happy to help! You can get help in
several ways:
2014-06-20 07:52:56 +02:00
2014-07-23 20:17:08 +02:00
* Send a mail to the mailing list at mailto:qutebrowser@lists.qutebrowser.org[]
2014-06-20 07:52:56 +02:00
(optionally
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe]
first).
2014-07-23 20:17:08 +02:00
* Join the IRC channel irc://irc.freenode.org/#qutebrowser[`#qutebrowser`] on
http://freenode.net/[Freenode]
(https://webchat.freenode.net/?channels=#qutebrowser[webchat]).
2014-06-20 07:52:56 +02:00
Finding something to work on
----------------------------
Chances are you already know something to improve or add when you're reading
2014-06-20 11:07:03 +02:00
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.
2014-06-20 07:52:56 +02:00
2014-10-02 07:05:56 +02:00
If you want to find something useful to do, check the
https://github.com/The-Compiler/qutebrowser/issues[issue tracker]. Some
pointers:
* https://github.com/The-Compiler/qutebrowser/milestones/v0.1[Open issues for
the v0.1 release]
* https://github.com/The-Compiler/qutebrowser/labels/easy[Issues which should
be easy to solve]
* https://github.com/The-Compiler/qutebrowser/labels/not%20code[Issues which
require little/no coding]
2014-06-20 07:52:56 +02:00
There are also some things to do if you don't want to write code:
2014-06-20 11:07:03 +02:00
* Help the community, e.g. on the mailinglist and the IRC channel.
2014-06-20 07:52:56 +02:00
* Improve the documentation.
2014-06-20 11:07:03 +02:00
* Help on the website and graphics (logo, etc.).
2014-06-20 07:52:56 +02:00
Using git
---------
qutebrowser uses http://git-scm.com/[git] for its development. You can clone
the repo like this:
----
git clone git://the-compiler.org/qutebrowser
----
If you don't know git, a http://git-scm.com/[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`.
Finding the correct branch
~~~~~~~~~~~~~~~~~~~~~~~~~~
Currently, qutebrowser is developed in the `master` branch. Feature branches
are used by me occasionally and pushed as a backup, but frequently
force-pushed. Do *not* base your work on any of the feature branches, use
`master` instead.
2014-06-20 11:07:03 +02:00
After v0.1 is released, an additional `development` branch will be added. Then,
base new features on the `development` branch, and bugfixes on the `master`
branch.
2014-06-20 07:52:56 +02:00
You can then checkout the correct branch via:
----
git checkout base-branch <1>
----
2014-06-20 11:07:03 +02:00
<1> Of course replace `base-branch` by `development` or `master`.
2014-06-20 07:52:56 +02:00
Getting patches
~~~~~~~~~~~~~~~
2014-06-20 11:07:03 +02:00
After you finished your work and did `git commit`, you can get patches of your
changes like this:
2014-06-20 07:52:56 +02:00
----
git format-patch origin/master <1>
----
<1> Replace `master` by the branch your work was based on, e.g.
`origin/develop`.
Useful utilities
----------------
Checkers
~~~~~~~~
2014-06-18 20:35:08 +02:00
2014-06-20 11:07:03 +02:00
In the _scripts/_ subfolder, there is a `run_checks.py` script.
2014-06-18 20:35:08 +02:00
2014-06-20 11:07:03 +02:00
It runs a bunch of static checks on all source files, using the following
checkers:
2014-06-18 20:35:08 +02:00
2014-06-20 11:07:03 +02:00
* Unit tests using the Python
2014-06-18 20:35:08 +02:00
https://docs.python.org/3.4/library/unittest.html[unittest] framework
* https://pypi.python.org/pypi/flake8/1.3.1[flake8]
* https://github.com/GreenSteam/pep257/[pep257]
* http://pylint.org/[pylint]
2014-06-20 11:07:03 +02:00
* A custom checker for the following things:
- untracked git files
2014-07-23 20:17:08 +02:00
- VCS conflict markers
2014-06-18 20:35:08 +02:00
2014-06-20 11:07:03 +02:00
If you changed `setup.py` or `MANIFEST.in`, add the `--setup` argument to run
the following additional checkers:
2014-06-18 20:35:08 +02:00
* https://pypi.python.org/pypi/pyroma/0.9.3[pyroma]
* https://github.com/mgedmin/check-manifest[check-manifest]
2014-08-14 21:01:39 +02:00
It needs all the checkers to be installed and also needs
https://pypi.python.org/pypi/colorama/[colorama].
2014-06-18 20:35:08 +02:00
Please make sure this script runs without any warnings on your new
contributions. There's of course the possibility of false-positives, and 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, 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.
Profiling
2014-06-20 07:52:56 +02:00
~~~~~~~~~
2014-06-18 20:35:08 +02:00
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 needs https://pypi.python.org/pypi/pyprof2calltree/[pyprof2calltree] and
http://kcachegrind.sourceforge.net/html/Home.html[KCacheGrind]. It uses the
built-in Python https://docs.python.org/3.4/library/profile.html[cProfile]
module.
2014-06-20 07:52:56 +02:00
Debugging
~~~~~~~~~
2014-06-18 20:35:08 +02:00
2014-07-23 20:17:08 +02:00
In the `qutebrowser.utils.debug` module there are some useful functions for
2014-09-03 13:31:01 +02:00
debugging.
2014-06-18 20:35:08 +02:00
When starting qutebrowser with the `--debug` flag you also get useful debug
2014-06-20 11:07:03 +02:00
logs. You can add +--logfilter _category[,category,...]_+ to restrict logging
to the given categories.
2014-06-18 20:35:08 +02:00
2014-06-20 11:07:03 +02:00
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
2014-06-18 20:35:08 +02:00
memory leaks.
2014-06-20 07:52:56 +02:00
Useful websites
~~~~~~~~~~~~~~~
Some resources which might be handy:
* http://qt-project.org/doc/qt-5/classes.html[The Qt5 reference]
* https://docs.python.org/3/library/index.html[The Python reference]
* http://httpbin.org/[httpbin, a test service for HTTP requests/responses]
* http://requestb.in/[RequestBin, a service to inspect HTTP requests]
2014-09-15 18:30:44 +02:00
Documentation of used Python libraries:
* http://jinja.pocoo.org/docs/dev/[jinja2]
* http://pygments.org/docs/[pygments]
* http://fdik.org/pyPEG/index.html[pyPEG2]
* http://pythonhosted.org/setuptools/[setuptools]
* http://cx-freeze.readthedocs.org/en/latest/overview.html[cx_Freeze]
* https://pypi.python.org/pypi/colorama[colorama]
* https://pypi.python.org/pypi/colorlog[colorlog]
2014-08-05 23:48:16 +02:00
Related RFCs and standards:
2014-09-02 09:04:47 +02:00
HTTP
^^^^
2014-08-12 06:38:30 +02:00
* https://tools.ietf.org/html/rfc2616[RFC 2616 - Hypertext Transfer Protocol
-- HTTP/1.1]
(http://www.rfc-editor.org/errata_search.php?rfc=2616[Errata])
* https://tools.ietf.org/html/rfc7230[RFC 7230 - Hypertext Transfer Protocol
(HTTP/1.1): Message Syntax and Routing]
(http://www.rfc-editor.org/errata_search.php?rfc=7230[Errata])
* https://tools.ietf.org/html/rfc7231[RFC 7231 - Hypertext Transfer Protocol
(HTTP/1.1): Semantics and Content]
(http://www.rfc-editor.org/errata_search.php?rfc=7231[Errata])
* https://tools.ietf.org/html/rfc7232[RFC 7232 - Hypertext Transfer Protocol
(HTTP/1.1): Conditional Requests]
(http://www.rfc-editor.org/errata_search.php?rfc=7232[Errata])
* https://tools.ietf.org/html/rfc7233[RFC 7233 - Hypertext Transfer Protocol
(HTTP/1.1): Range Requests]
(http://www.rfc-editor.org/errata_search.php?rfc=7233[Errata])
* https://tools.ietf.org/html/rfc7234[RFC 7234 - Hypertext Transfer Protocol
(HTTP/1.1): Caching]
(http://www.rfc-editor.org/errata_search.php?rfc=7234[Errata])
* https://tools.ietf.org/html/rfc7235[RFC 7235 - Hypertext Transfer Protocol
(HTTP/1.1): Authentication]
(http://www.rfc-editor.org/errata_search.php?rfc=7235[Errata])
2014-08-14 13:49:51 +02:00
* https://tools.ietf.org/html/rfc5987[RFC 5987 - Character Set and Language
Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters]
(http://www.rfc-editor.org/errata_search.php?rfc=5987[Errata])
2014-08-12 06:38:30 +02:00
* https://tools.ietf.org/html/rfc6266[RFC 6266 - Use of the
2014-08-05 23:48:16 +02:00
Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)]
2014-08-12 06:38:30 +02:00
(http://www.rfc-editor.org/errata_search.php?rfc=6266[Errata])
* http://tools.ietf.org/html/rfc6265[RFC 6265 - HTTP State Management Mechanism
(Cookies)] (http://www.rfc-editor.org/errata_search.php?rfc=6265[Errata])
2014-08-05 23:48:16 +02:00
* http://www.cookiecentral.com/faq/#3.5[Netscape Cookie Format]
2014-09-02 09:04:47 +02:00
Other
^^^^^
* https://tools.ietf.org/html/rfc5646[RFC 5646 - Tags for Identifying
Languages] (http://www.rfc-editor.org/errata_search.php?rfc=5646[Errata])
2014-08-05 23:48:16 +02:00
* http://www.w3.org/TR/CSS2/[Cascading Style Sheets Level 2 Revision 1 (CSS
2.1) Specification]
* http://qt-project.org/doc/qt-4.8/stylesheet-reference.html[Qt Style Sheets
Reference]
2014-08-12 10:54:36 +02:00
* http://mimesniff.spec.whatwg.org/[MIME Sniffing Standard]
* http://spec.whatwg.org/[WHATWG specifications]
2014-09-02 09:04:47 +02:00
* http://www.w3.org/html/wg/drafts/html/master/Overview.html[HTML 5.1 Nightly]
* http://www.w3.org/TR/webstorage/[Web Storage]
2014-09-26 07:06:39 +02:00
* http://www.brynosaurus.com/cachedir/spec.html[Cache directory tagging
standard]
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html[XDG
basedir specification]
2014-08-05 23:48:16 +02:00
2014-06-20 11:49:33 +02:00
Hints
-----
2014-06-23 15:17:36 +02:00
Python and Qt objects
~~~~~~~~~~~~~~~~~~~~~
For many tasks, there are solutions in both Qt and the Python standard libary
available.
In qutebrowser, the policy is usually using the Python libraries, as they
provide exceptions and other benefits.
2014-07-23 20:17:08 +02:00
There are some exceptions to that:
2014-06-23 15:17:36 +02:00
* `QThread` is used instead of Python threads because it provides signals and
slots.
* `QProcess` is used instead of Python's `subprocess` if certain actions (e.g.
cleanup) when the process finished are desired, as it provides signals for
that.
* `QUrl` is used instead of storing URLs as string, see the
<<handling-urls,handling URLs>> section for details.
When using Qt objects, two issues must be taken care of:
* Methods of Qt objects report their status by using 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
2014-08-26 19:23:06 +02:00
function `ensure_valid` in `qutebrowser.utils.qt` which should get called on
2014-06-23 15:17:36 +02:00
all such objects. It will raise `qutebrowser.utils.qt.QtValueError` if the
value is not valid.
+
If a function returns something else on error, the return value should
carefully be checked.
2014-06-23 15:19:21 +02:00
2014-06-23 15:17:36 +02:00
* Methods of Qt objects have certain maximum values, based on their underlying
C++ types.
+
When passing a numeric parameter to a Qt function, all numbers should be
range-checked using `qutebrowser.utils.check_overflow`, or passing a value
which is too large should be avoided by other means (e.g. by setting a maximum
value for a config object).
2014-09-25 08:28:27 +02:00
[[object-registry]]
The object registry
~~~~~~~~~~~~~~~~~~~
The object registry in `qutebrowser.utils.objreg` is a collection of
dictionaries which map object names to the actual long-living objects.
There are currently these object registries, also called 'scopes':
* The `global` scope, with objects which are used globally (`config`,
`cookie-jar`, etc.)
* The `tab` scope with objects which are per-tab (`hintmanager`, `webview`,
etc.). Passing this scope to `objreg.get()` always selects the object in the
currently focused tab.
* The `meta` scope which is an object registry of all other object registries,
mainly intended for debugging.
A new object can be registered by using
+objreg.register(_name_, _object_[, scope=_scope_])+. An object should not be
2014-09-25 08:34:20 +02:00
registered twice. To update it, `update=True` has to be given.
2014-09-25 08:28:27 +02:00
An object can be retrieved by using +objreg.get(_name_[, scope=_scope_])+. The
default scope is `global`.
All objects can be printed by starting with the `--debug` flag and using the
`:debug-all-objects` command.
The registry is mainly used for <<commands,command handlers>> but also can be
useful in places where using Qt's
http://qt-project.org/doc/qt-5/signalsandslots.html[signals and slots]
mechanism would be difficult.
2014-06-20 11:49:33 +02:00
Logging
~~~~~~~
Logging is used at various places throughout the qutebrowser code. If you add a
new feature, you should also add some strategic debug logging.
Unless other Python projects, qutebrowser doesn't use a logger per file,
instead it uses custom-named loggers.
The existing loggers are defined in `qutebrowser.utils.log`. If your feature
doesn't fit in any of the logging categories, simply add a new line like this:
[source,python]
----
foo = getLogger('foo')
----
2014-08-26 20:15:41 +02:00
Then in your source files, do this:
2014-06-20 11:49:33 +02:00
[source,python]
----
2014-08-26 19:10:14 +02:00
from qutebrowser.utils import log
2014-06-20 11:49:33 +02:00
...
log.foo.debug("Hello World")
----
The following logging levels are available for every logger:
2014-06-20 11:53:54 +02:00
[width="75%",cols="25%,75%"]
2014-06-20 11:49:33 +02:00
|=======================================================================
|criticial |Critical issue, qutebrowser can't continue to run.
|error |There was an issue and some kind of operation was abandoned.
|warning |There was an issue but the operation can continue running.
|info |General informational messages.
|debug |Verbose debugging informations.
|=======================================================================
2014-09-25 08:28:27 +02:00
[[commands]]
2014-06-20 11:49:33 +02:00
Commands
~~~~~~~~
qutebrowser has the concept of functions which are exposed to the user as
commands.
Creating a new command is straightforward:
[source,python]
----
2014-08-26 20:48:39 +02:00
import qutebrowser.commands.cmdutils
2014-06-20 11:49:33 +02:00
...
@cmdutils.register(...)
def foo():
...
----
The commands arguments are automatically deduced by inspecting your function.
If your function has a `count` argument with a default, the command will
support a count which will be passed in the argument.
If the function is a method of a class, the `@cmdutils.register` decorator
needs to have an `instance=...` parameter which points to the (single/main)
2014-09-25 08:34:20 +02:00
instance of the class.
2014-06-20 11:49:33 +02:00
2014-09-25 08:28:27 +02:00
The `instance` parameter is the name of an object in the object registry, which
then gets passed as the `self` parameter to the handler. The `scope` argument
selects which object registry (global, per-tab, etc.) to use. See the
2014-09-25 08:34:20 +02:00
<<object-registry,object registry>> section for details.
2014-06-20 11:49:33 +02:00
There are also other arguments to customize the way the command is registered,
see the class documentation for `register` in `qutebrowser.commands.utils` for
details.
2014-06-23 15:17:36 +02:00
[[handling-urls]]
2014-06-20 22:48:01 +02:00
Handling URLs
~~~~~~~~~~~~~
qutebrowser handles two different types of URLs: URLs as a string, and URLs as
the Qt `QUrl` type. As this can get confusing quickly, please follow the
following guidelines:
* Convert a string to a QUrl object as early as possible, i.e. directly after
the user did enter it.
2014-08-26 20:33:41 +02:00
- Use `utils.urlutils.fuzzy_url` if the URL is entered by the user
somewhere.
- Be sure you handle `utils.urlutils.FuzzyError` and display an error
2014-06-20 22:48:01 +02:00
message to the user.
* Convert a `QUrl` object to a string as late as possible, e.g. before
displaying it to the user.
- If you want to display the URL to the user, use `url.toDisplayString()`
so password information is removed.
- If you want to get the URL as string for some other reason, you most
likely want to add the `QUrl.EncodeFully` and `QUrl.RemovePassword`
flags.
* Name a string URL something like `urlstr`, and a `QUrl` something like `url`.
* Mention in the docstring whether your function needs a URL string or a
`QUrl`.
2014-08-26 20:38:23 +02:00
* Call `ensure_valid` from `utils.qtutils` whenever getting or creating a
`QUrl` and take appropriate action if not.
2014-06-20 22:48:01 +02:00
2014-06-20 11:49:33 +02:00
2014-06-18 20:35:08 +02:00
Style conventions
-----------------
2014-04-25 17:00:40 +02:00
2014-06-18 20:35:08 +02:00
qutebrowser's coding conventions are based on
http://legacy.python.org/dev/peps/pep-0008/[PEP8] and the https://google-styleguide.googlecode.com/svn/trunk/pyguide.html[Google Python style guidelines] with some additions:
* Methods overriding Qt methods (obviously!) don't follow the naming schemes.
* Everything else does though, even slots.
2014-06-20 11:07:03 +02:00
* Docstrings should look like described in
2014-06-18 20:35:08 +02:00
http://legacy.python.org/dev/peps/pep-0257/[PEP257] and the google guidelines.
2014-06-20 11:07:03 +02:00
* Class docstrings have additional _Attributes:_, _Class attributes:_ and
_Signals:_ sections, method/function docstrings have an _Emit:_ section.
2014-07-16 20:09:41 +02:00
* In docstrings of command handlers (registered via `@cmdutils.register`), the
description should be split into two parts by using `//` - the first part is
the description of the command like it will appear in the documentation, the
second part is "internal" documentation only relevant to people reading the
sourcecode.
2014-06-18 20:35:08 +02:00
+
Example for a class docstring:
+
[source,python]
----
"""Some object.
Attributes:
2014-06-20 11:07:03 +02:00
blub: The current thing to handle.
2014-06-18 20:35:08 +02:00
Signals:
2014-06-20 11:07:03 +02:00
valueChanged: Emitted when a value changed.
arg: The new value
2014-06-18 20:35:08 +02:00
"""
----
+
Example for a method/function docstring:
+
[source,python]
----
"""Do something special.
2014-07-16 20:09:41 +02:00
This will do something.
//
It is based on http://example.com/.
2014-06-18 20:35:08 +02:00
Args:
2014-06-20 11:07:03 +02:00
foo: ...
2014-06-18 20:35:08 +02:00
Return:
2014-06-20 11:07:03 +02:00
True if something, False if something else.
2014-06-18 20:35:08 +02:00
Raise:
2014-06-20 11:07:03 +02:00
ValueError if foo is None
2014-06-18 20:35:08 +02:00
Emit:
2014-06-20 11:07:03 +02:00
value_changed
2014-06-18 20:35:08 +02:00
"""
----
+
* The layout of a module should be roughly like this:
2014-06-19 09:04:37 +02:00
- Shebang (`#!/usr/bin/python`, if needed)
- vim-modeline (`# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et`)
2014-04-25 17:46:01 +02:00
- Copyright
- GPL boilerplate
- Module docstring
- Python standard library imports
- PyQt imports
- qutebrowser imports
- functions
- classes
2014-06-18 20:35:08 +02:00
* The layout of a class should be like this:
2014-04-25 17:46:01 +02:00
- docstring
2014-06-18 20:35:08 +02:00
- `__magic__` methods
2014-04-25 17:46:01 +02:00
- properties
- _private methods
- public methods
2014-06-18 20:35:08 +02:00
- `on_*` methods
2014-04-25 17:46:01 +02:00
- overrides of Qt methods