Python code style is enforced with the Ruff linter, see linting for more information.
-
Where Python files contain non-ASCII characters, they should be encoded in UTF-8.
- There should be no Unicode BOM at the start of the file, as this unfortunately breaks one of the translation tools we use (
xgettext
). Instead, include this as the first line of the file, only if the file contains non-ASCII characters:
# -*- coding: UTF-8 -*-
- This coding comment must also be included if strings in the code (even strings that aren't translatable) contain escape sequences that produce non-ASCII characters; e.g.
"\xff"
. This is particularly relevant for braille display drivers. This is due to agettext
bug; see comment on #2592.
- There should be no Unicode BOM at the start of the file, as this unfortunately breaks one of the translation tools we use (
-
Text files should be committed with
LF
line endings. Files can be checked out locally using CRLF if needed for Windows development using git.
- Indentation must be done with tabs (one per level), not spaces.
- When splitting a single statement over multiple lines, just indent one or more additional levels.
Don't use vertical alignment; i.e. lining up with the bracket on the previous line.
- Be aware that this requires a new-line after an opening parenthesis/bracket/brace if you intend to split the statement over multiple lines.
- Use descriptive names
- name constants to avoid "magic numbers" and hint at intent or origin of the value. Consider, what does this represent?
- Functions, variables, properties, etc. should use mixed case to separate words, starting with a lower case letter;
- E.g.
speakText
.
- E.g.
- Boolean functions or variables
- Use the positive form of the language.
Avoid double negatives like
shouldNotDoSomething = False
- Start with a "question word" to hint at their boolean nature.
- E.g.
shouldX
,isX
,hasX
- Use the positive form of the language.
Avoid double negatives like
- Classes should use mixed case to separate words, starting with an upper case letter;
- E.g.
BrailleHandler
.
- E.g.
- Constants should be all upper case, separating words with underscores;
- E.g.
LANGS_WITH_CONJUNCT_CHARS
.
- E.g.
- Scripts (the targets of gestures) are prefixed with "script_", with subsequent words in camel case.
- E.g.
script_cycleAudioDuckingMode
.
- E.g.
- Event handlers are prefixed with "event_", with subsequent words in camel case.
Note,
object
andaction
are separated by underscores.- E.g.:
event_action
orevent_object_action
. object
refers to the class type that theaction
refers to.- Examples:
event_caret
,event_appModule_gainFocus
- E.g.:
- Extension points:
Action
- Prefixed with
pre_
orpost_
to specify that handlers are being notified before / after the action has taken place.
- Prefixed with
Decider
- Prefixed with
should_
to turn them into a question, e.g.should_doSomething
- Prefixed with
Filter
- Prefixed with
filter_
, e.g.filter_displaySize_preRefresh
- Should describe the filtering action and the data being returned
- Should communicate if the filtering happens before or after some action
- Prefixed with
- Enums should be formatted using the expected mix of the above, e.g.:
class ExampleGroupOfData(Enum): CONSTANT_VALUE_MEMBER = auto() @property def _formatMember(self): pass
- All strings that could be presented to the user should be marked as translatable using the
_()
function- e.g.
_("Text review")
.
- e.g.
- All translatable strings should have a preceding translators comment describing the purpose of the string for translators. For example:
# Translators: The name of a category of NVDA commands.
SCRCAT_TEXTREVIEW = _("Text review")
- Lengthy translatable strings can be split across multiple lines, taking advantage of Python's implicit line joining inside parentheses. Translators comment can span multiple lines as well. For example:
self.copySettingsButton = wx.Button(
self,
label=_(
# Translators: The label for a button in general settings to copy
# current user settings to system settings (to allow current
# settings to be used in secure screens such as User Account
# Control (UAC) dialog).
"Use currently saved settings during sign-in and on secure screens"
" (requires administrator privileges)"
)
)
- Unused imports should be removed where possible.
- Anything imported into a (sub)module can also be imported from that submodule.
- As a result, removing unused imports may break compatibility, and should be done in compatibility breaking releases (see
deprecations.md
).
- Unused imports will give a lint warning. These can be handled the following ways:
- If these imports are intended to be imported from other modules, they can be included in a definition for
__all__
. This will override and define the symbols imported when performing a star import, e.g.from module import *
. - Otherwise, with a comment like
# noqa: <explanation>
.
- If these imports are intended to be imported from other modules, they can be included in a definition for
When writing new code, consider how the code can be moved in future while retaining backwards compatibility. Refer to the limitations to retaining backwards compatibility.
In summary:
- Avoid module level global variables. Any module level variables should be prefixed with an underscore and be encapsulated, e.g. via getters and setters.
- Avoid code which executes at import time. Instead use initializer functions.
Docstrings should use Sphinx format. Providing type information in docstrings is discouraged, instead use python's type annotations.
NVDA formerly used epytext syntax for docstrings, which means there is inconsistent syntax used in the NVDA code base. #12971 exists to track converting epytext docstrings to Sphinx.