Skip to main content
NVDA follows specific coding standards to ensure consistency and maintainability across the codebase. Authors should do their best to adhere to these standards to have the best chance of their contribution being accepted.
In limited circumstances, NV Access may accept contributions that do not follow these coding standards. If you’re unable to follow these standards, please make note of this when opening your PR.

General Guidelines

In general, Python contributions to NVDA should follow the PEP 8 style guide, except where it contradicts the specific guidance below. Python code style is enforced with the Ruff linter. See linting your changes for more information.

Encoding

  • Python files should be encoded in UTF-8
  • Text files should be committed with LF line endings
  • Files can be checked out locally using CRLF if needed for Windows development using git’s core.autocrlf setting

Indentation

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 (lining up with the bracket on the previous line)
  • This requires a new-line after an opening parenthesis/bracket/brace if you intend to split the statement over multiple lines

Identifier Names

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

Use mixed case to separate words, starting with a lower case letter:
speakText
brailleHandler
currentFocusObject

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
shouldSpeak
isEnabled
hasChildren

Classes

Use mixed case to separate words, starting with an upper case letter:
BrailleHandler
TextInfo
NVDAObject

Constants

All upper case, separating words with underscores:
LANGS_WITH_CONJUNCT_CHARS
MAX_BUFFER_SIZE
DEFAULT_TIMEOUT

Scripts

Scripts (the targets of gestures) are prefixed with script_, with subsequent words in camel case:
script_cycleAudioDuckingMode
script_reportCurrentLine

Event Handlers

Prefixed with event_, with subsequent words in camel case. Note that object and action are separated by underscores:
event_gainFocus
event_caret
event_appModule_gainFocus
Where object refers to the class type that the action refers to.

Extension Points

Action:
  • Prefixed with pre_ or post_ to specify that handlers are notified before/after the action
Decider:
  • Prefixed with should_ to turn them into a question
  • Example: should_doSomething
Filter:
  • Prefixed with filter_
  • Should describe the filtering action and the data being returned
  • Should communicate if filtering happens before or after some action
  • Example: filter_displaySize_preRefresh

Enums

Formatted using the expected mix of the above:
class ExampleGroupOfData(Enum):
    CONSTANT_VALUE_MEMBER = auto()
    
    @property
    def _formatMember(self):
        pass

Translatable Strings

All strings that could be presented to the user should be marked as translatable using the _() function:
# Translators: The name of a category of NVDA commands.
SCRCAT_TEXTREVIEW = _("Text review")
All translatable strings must have a preceding translators comment describing the purpose of the string.

Multi-line Translatable Strings

Lengthy strings can be split across multiple lines using Python’s implicit line joining inside parentheses:
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)"
    )
)

Imports

  • Unused imports should be removed where possible
  • Anything imported into a (sub)module can also be imported from that submodule
  • Removing unused imports may break compatibility and should be done in compatibility breaking releases

Handling Unused Import Warnings

Unused imports will give a lint warning. Handle them in these ways:
  1. If imports are intended for re-export: Include them in __all__ definition
    __all__ = ["SomeClass", "someFunction"]
    
  2. Otherwise: Add a comment like # noqa: <explanation>

Considering Future Backwards Compatibility

When writing new code, consider how it can be moved in future while retaining backwards compatibility. See deprecations documentation for limitations.
Summary:
  • Avoid module level global variables
  • Any module level variables should be prefixed with underscore and encapsulated via getters/setters
  • Avoid code which executes at import time - use initializer functions instead

Docstrings

Docstrings should use Sphinx format without types and follow PEP 257 conventions.

When to Use Docstrings

  • All public functions, classes, and methods should have docstrings
  • Most internal functions, classes and methods should have docstrings, except where their purpose is clear from their name or code
  • A function of more than a few lines of code is most likely not self-explanatory

Guidelines

  • Providing type information in docstrings is discouraged - use type annotations instead
  • Class-level and module-level docstrings should contain:
    • High-level overview
    • Optionally: usage examples and references to commonly used methods/functions and attributes
  • Document class constructors in __init__, not at the top of the class
  • Document class attributes and non-obvious public variables in a docstring immediately below the attribute
NVDA formerly used epytext syntax for docstrings, which means there is inconsistent syntax in the codebase. Issue #12971 tracks converting epytext docstrings to Sphinx.

Learning Resources

Type Hints

All new code contributions to NVDA should use PEP 484-style type hints.
Type hints make reasoning about code much easier and allow static analysis tools to catch common errors.

Requirements

  • All variables, attributes, properties, and function/method arguments and returns should have type hints
  • No need to provide type hints for self or cls arguments to object/class methods
  • Prefer union shorthand (X | Y) over explicitly using typing.Union
  • Corollary: prefer T | None over typing.Optional[T]

Example

def speakText(
    text: str,
    priority: SpeechPriority = SpeechPriority.NORMAL,
    symbolLevel: int | None = None
) -> None:
    """Speak the given text with specified priority."""
    pass

Calling Non-Python Code

When using parts of the Windows API or parts of NVDA implemented in C++, use the ctypes library.

Naming Conventions

When providing ctypes type information for foreign functions, structures and data types, prefer to use the same name as used in the external library.
  • Example: GetModuleFileName not getModuleFileName
  • Pythonic names should be reserved for wrappers that provide more pythonic access to functions

Organization

  • Windows API functions: Define in the winBindings package in modules named according to the DLL
    • Example: winBindings.kernel32
  • nvdaHelper ctypes code: Define in the NVDAHelper.localLib module

Language Choices

The NVDA community is large and diverse. We have a responsibility to make everyone feel welcome. As our contributor code of conduct states:
Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
The wording choices we make have power. Avoid language that negatively impacts others.

Guidelines

  • Avoid metaphors, euphemisms or other language with layers of meaning or negative history
  • Avoid generalisations about people, cultures or countries
  • Avoid ableist language
  • Use gender-inclusive terminology

Examples of Preferred Terms

Instead ofUse
master/slaveleader/follower, primary/replica
blacklist/whitelistblocklist/allowlist
sanity checkconfidence check
dummy (value)placeholder
he/him/his (unknown gender)they/them/theirs

Running Code Style Checks

To check your code against these standards:
# Run linting checks
runlint.bat

# Check translatable strings
runcheckpot.bat

# Check licenses
runlicensecheck.bat
Integrate Ruff and pyright with your IDE to catch issues faster during development.

Build docs developers (and LLMs) love