451 lines
15 KiB
Plaintext
451 lines
15 KiB
Plaintext
qutebrowser HACKING
|
|
===================
|
|
The Compiler <mail@qutebrowser.org>
|
|
:icons:
|
|
:data-uri:
|
|
:toc:
|
|
|
|
I `<3` footnote:[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 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
|
|
probably change it for you then, though.
|
|
|
|
If you have any problems, I'm more than happy to help! You can get help in
|
|
several ways:
|
|
|
|
* Send a mail to the mailing list at mailto:qutebrowser@lists.qutebrowser.org[]
|
|
(optionally
|
|
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe]
|
|
first).
|
|
* Join the IRC channel irc://irc.freenode.org/#qutebrowser[`#qutebrowser`] on
|
|
http://freenode.net/[Freenode]
|
|
(https://webchat.freenode.net/?channels=#qutebrowser[webchat]).
|
|
|
|
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 `BUGS` and `TODO` files.
|
|
|
|
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 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.
|
|
|
|
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.
|
|
|
|
You can then checkout the correct branch via:
|
|
|
|
----
|
|
git checkout base-branch <1>
|
|
----
|
|
<1> Of course replace `base-branch` by `development` or `master`.
|
|
|
|
|
|
Getting patches
|
|
~~~~~~~~~~~~~~~
|
|
|
|
After you finished your work and did `git commit`, you can get patches of 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`.
|
|
|
|
Useful utilities
|
|
----------------
|
|
|
|
Checkers
|
|
~~~~~~~~
|
|
|
|
In the _scripts/_ subfolder, there is a `run_checks.py` script.
|
|
|
|
It runs a bunch of static checks on all source files, using the following
|
|
checkers:
|
|
|
|
* Unit tests using the Python
|
|
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]
|
|
* A custom checker for the following things:
|
|
- untracked git files
|
|
- whitespace/CRLF problems
|
|
- `set_trace` invocations
|
|
- VCS conflict markers
|
|
|
|
If you changed `setup.py` or `MANIFEST.in`, add the `--setup` argument to run
|
|
the following additional checkers:
|
|
|
|
* https://pypi.python.org/pypi/pyroma/0.9.3[pyroma]
|
|
* https://github.com/mgedmin/check-manifest[check-manifest]
|
|
|
|
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
|
|
~~~~~~~~~
|
|
|
|
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.
|
|
|
|
Debugging
|
|
~~~~~~~~~
|
|
|
|
In the `qutebrowser.utils.debug` module there are some useful functions for
|
|
debugging. In particular you should use `set_trace` from
|
|
`qutebrowser.utils.debug` instead of `pdb` to set breakpoints or you'll get
|
|
annoying Qt error messages.
|
|
|
|
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.
|
|
|
|
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]
|
|
|
|
Related RFCs and standards:
|
|
|
|
* 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])
|
|
* https://tools.ietf.org/html/rfc6266[RFC 6266 - Use of the
|
|
Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)]
|
|
(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])
|
|
* http://www.cookiecentral.com/faq/#3.5[Netscape Cookie Format]
|
|
* 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]
|
|
* http://mimesniff.spec.whatwg.org/[MIME Sniffing Standard]
|
|
* http://spec.whatwg.org/[WHATWG specifications]
|
|
|
|
Hints
|
|
-----
|
|
|
|
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.
|
|
|
|
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` 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
|
|
function `qt_ensure_valid` in `qutebrowser.utils.qt` which should get called on
|
|
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.
|
|
|
|
* 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).
|
|
|
|
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')
|
|
----
|
|
|
|
Then in your source files, you have two different possibilities:
|
|
|
|
[source,python]
|
|
----
|
|
from qutebrowser.utils.log import foo as logger
|
|
...
|
|
logger.debug("Hello World")
|
|
----
|
|
|
|
or
|
|
|
|
[source,python]
|
|
----
|
|
import qutebrowser.utils.log as log
|
|
...
|
|
log.foo.debug("Hello World")
|
|
----
|
|
|
|
Use the first approach if you only need to use a single logging category. If
|
|
you need more than one category, use the second approach instead.
|
|
|
|
The following logging levels are available for every logger:
|
|
|
|
[width="75%",cols="25%,75%"]
|
|
|=======================================================================
|
|
|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.
|
|
|=======================================================================
|
|
|
|
Commands
|
|
~~~~~~~~
|
|
|
|
qutebrowser has the concept of functions which are exposed to the user as
|
|
commands.
|
|
|
|
Creating a new command is straightforward:
|
|
|
|
[source,python]
|
|
----
|
|
import qutebrowser.commands.utils as cmdutils
|
|
|
|
...
|
|
|
|
@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)
|
|
instance of the class. Registering commands with multiple instances is not
|
|
supported. For an example on how to solve that problem, see
|
|
`qutebrowser.browser.command` which dispatches commands to the currently active
|
|
tab.
|
|
|
|
The `instance` parameter is a dotted attribute-path, leading from the main
|
|
Application instance to your object instance.
|
|
For example, if the Application object has an attribute `status`, which then
|
|
has an attribute `prompt` and commands are registered there, the parameter
|
|
would be `status.prompt`.
|
|
|
|
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.
|
|
|
|
[[handling-urls]]
|
|
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.
|
|
- Use `utils.url.fuzzy_url` if the URL is entered by the user somewhere.
|
|
- Be sure you handle `utils.url.FuzzyError` and display an error
|
|
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`.
|
|
* Call `qt_ensure_valid` from `utils.qt` whenever getting or creating a
|
|
`QUrl` and take appropriate action if not.
|
|
|
|
|
|
Style conventions
|
|
-----------------
|
|
|
|
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.
|
|
* Docstrings should look like described in
|
|
http://legacy.python.org/dev/peps/pep-0257/[PEP257] and the google guidelines.
|
|
* Class docstrings have additional _Attributes:_, _Class attributes:_ and
|
|
_Signals:_ sections, method/function docstrings have an _Emit:_ section.
|
|
* 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.
|
|
+
|
|
Example for a class docstring:
|
|
+
|
|
[source,python]
|
|
----
|
|
"""Some object.
|
|
|
|
Attributes:
|
|
blub: The current thing to handle.
|
|
|
|
Signals:
|
|
valueChanged: Emitted when a value changed.
|
|
arg: The new value
|
|
"""
|
|
----
|
|
+
|
|
Example for a method/function docstring:
|
|
+
|
|
[source,python]
|
|
----
|
|
"""Do something special.
|
|
|
|
This will do something.
|
|
|
|
//
|
|
|
|
It is based on http://example.com/.
|
|
|
|
Args:
|
|
foo: ...
|
|
|
|
Return:
|
|
True if something, False if something else.
|
|
|
|
Raise:
|
|
ValueError if foo is None
|
|
|
|
Emit:
|
|
value_changed
|
|
"""
|
|
----
|
|
+
|
|
* The layout of a module should be roughly like this:
|
|
- Shebang (`#!/usr/bin/python`, if needed)
|
|
- vim-modeline (`# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et`)
|
|
- Copyright
|
|
- GPL boilerplate
|
|
- Module docstring
|
|
- Python standard library imports
|
|
- PyQt imports
|
|
- qutebrowser imports
|
|
- functions
|
|
- classes
|
|
* The layout of a class should be like this:
|
|
- docstring
|
|
- `__magic__` methods
|
|
- properties
|
|
- _private methods
|
|
- public methods
|
|
- `on_*` methods
|
|
- overrides of Qt methods
|