= rdiff-backup: {page-name}
:page-name: Coding Conventions
:sectnums:
:toc: macro

link:.[Home,role="button round"] // *{page-name}*

'''''

toc::[]

We follow the latest  https://www.python.org/dev/peps/pep-0008/[PEP 8 coding standard] as enforced by flake8 within our CI pipeline, but certain aspects of coding can't be enforced through automated checks, hence to be followed by humans and reviewers.
This document lists such coding conventions.

== QUOTING

* the https://www.python.org/dev/peps/pep-0008/#string-quotes[style guide for Python code (PEP 8)] doesn't recommend single vs.
double quotes but states that triple-quoted strings should use double quotes "to be consistent with the docstring convention in https://www.python.org/dev/peps/pep-0257[PEP 257]".
* for this reason, we use double quotes everywhere, unless the text contains itself a double quote, in which case single quotes are obviously preferred.

== PRIVATE AND PUBLIC ITEMS

* we call items any definition of class, function, member, variable, etc...
* private items are pre-fixed with an underscore
* public items are to be defined before private ones.
Pre-defined functions (generally pre- and post-fixed with two underscores, like `__init__`) are defined before all others.
* the order should be logical (e.g.
called functions after calling ones) rather than lexical.
* in class definitions, variables are defined before class methods, before instance methods (each in the order defined above).

== ASSERT STATEMENTS

* assert statements should be used only sparingly, and only for validating internal function calls.
Code must continue to work with asserts disabled.
* assert statements shall not be used to validate user inputs or system consistency (e.g.
if a backup repository is corrupt or not).
* all used assert statements must have an explanatory text.
It should explain _why_ it went wrong rather than the test result itself.
* longer assert statements use parentheses and not backslashes to go over multiple lines, e.g.
`+assert (condition), ("text {hold}".format(hold=var))+`.
* assert messages should only appear during coding, assert messages visible to users are candidate for replacement through a proper fatal error handling.
* assert messages might not need translation in the future, as they shouldn't be seen by users, but still follow the "TEXT OUTPUT" conventions.

== TEXT OUTPUT

* any text output starts with a capital letter and does _not_ end with a dot.
* text output with variables is formatted with named placeholders using the form `"Some message regarding broken object '{bo}'".format(bo=actual_var)`.
  This simple example highlights a few important rules:
** complex placeholders are kept between single quotes in the string.
** the placeholder is characterized by a specific description "broken object", so that the user has an idea about the context even if the variable `actual_var` happens to be empty.
** name of placeholders are two letters for the initials of this context (`bo` for "**b**roken **o**bject") and _not_ the variable name (`av` for "**a**ctual **v**ar").
   This will help translators to better understand how variables fit in the message without having to look at the code itself.
   If the context consists only of one word, use the first two letters (e.g. `ob` for "object"), but consider that one word might not be quite enough as context!
   Avoid two letter combinations with a meaning in Python (e.g. if, in, or).
* each text string is a one-liner _not_ starting with the severity level (error, warning, etc), leaving the formatting to the `log.Log` class.
* the explanation is meant first for the user, and explains ways to solve the potential issue, then for the developer to troubleshoot.

NOTE: one of the main driver for the above recommendations is the possibility to translate those strings in the future.
      For this reason, exception and especially assert messages can be handled less strictly.

== BYTES VS. STR

* Paths and commands are expressed in bytes to avoid issues with cross-platform encoding conversion, any function manipulating such things must return bytes.
* Only methods creating a Path/Command object (`__init__` and similar) might accept strings as input and convert them internally to bytes.
Other functions must make sure that they don't accept strings as input (this is one of the few reasons to use asserts).
* Any conversion between strings and bytes happens with `os.fsencode/fsdecode`.
* Messages are to be expressed in strings (log, errors, etc).

== DOCSTRINGS

* docstrings are quoted with three double quotes (see above).
* each triple quotes are alone on their line (this is a deviation from some recommendations) so that it is readable.
* each docstring starts with a single line summarizing the purpose of the object.
* further descriptions are written after a blank line.

== IMPORT

* import in such a way that a single module name remains but not the packages from its hierarchy.
This means to avoid using imported functions, classes and variables without module's name in front.
For example `from package import module` or `import module`, but _neither_ `import package.module` _nor_ `from module import func_or_var_or_class`.
* acceptable exceptions are standard modules (e.g.
`os.path`) or where the purpose of the import is to wrap the variable/function/class and do as if it would belong to the importing module.
