Skip to content

Style


These are all the checkers used by our style enforcement.

black

An opinionated formatter, like JavaScript's prettier and Golang's gofmt.

isort

A tool to sort imports lexicographically, by section, and by type. We use the 5 standard sections: __future__, stdlib, third party, first party, and local.

datadog_checks is configured as a first party namespace.

flake8

An easy-to-use wrapper around pycodestyle and pyflakes. We select everything it provides and only ignore a few things to give precedence to other tools.

bugbear

A flake8 plugin for finding likely bugs and design problems in programs. We enable:

  • B001: Do not use bare except:, it also catches unexpected events like memory errors, interrupts, system exit, and so on. Prefer except Exception:.
  • B003: Assigning to os.environ doesn't clear the environment. Subprocesses are going to see outdated variables, in disagreement with the current process. Use os.environ.clear() or the env= argument to Popen.
  • B006: Do not use mutable data structures for argument defaults. All calls reuse one instance of that data structure, persisting changes between them.
  • B007: Loop control variable not used within the loop body. If this is intended, start the name with an underscore.
  • B301: Python 3 does not include .iter* methods on dictionaries. The default behavior is to return iterables. Simply remove the iter prefix from the method. For Python 2 compatibility, also prefer the Python 3 equivalent if you expect that the size of the dict to be small and bounded. The performance regression on Python 2 will be negligible and the code is going to be the clearest. Alternatively, use six.iter*.
  • B305: .next() is not a thing on Python 3. Use the next() builtin. For Python 2 compatibility, use six.next().
  • B306: BaseException.message has been deprecated as of Python 2.6 and is removed in Python 3. Use str(e) to access the user-readable message. Use e.args to access arguments passed to the exception.
  • B902: Invalid first argument used for method. Use self for instance methods, and cls for class methods.

logging-format

A flake8 plugin for ensuring a consistent logging format. We enable:

  • G001: Logging statements should not use string.format() for their first argument
  • G002: Logging statements should not use % formatting for their first argument
  • G003: Logging statements should not use + concatenation for their first argument
  • G004: Logging statements should not use f"..." for their first argument (only in Python 3.6+)
  • G010: Logging statements should not use warn (use warning instead)
  • G100: Logging statements should not use extra arguments unless whitelisted
  • G201: Logging statements should not use error(..., exc_info=True) (use exception(...) instead)
  • G202: Logging statements should not use redundant exc_info=True in exception

Mypy

A comment-based type checker allowing a mix of dynamic and static typing. This is optional for now. In order to enable mypy for a specific integration, open its hatch.toml file and add the lines in the correct section:

[env.collectors.datadog-checks]
check-types: true
mypy-args = [
    "--py2",
    "--install-types",
    "--non-interactive",
    "datadog_checks/",
    "tests/",
]
mypy-deps = [
  "types-mock==0.1.5",
]
...

The mypy-args defines the mypy command line option for this specific integration. --py2 is here to make sure the integration is Python2.7 compatible. Here are some useful flags you can add:

  • --check-untyped-defs: Type-checks the interior of functions without type annotations.
  • --disallow-untyped-defs: Disallows defining functions without type annotations or with incomplete type annotations.

The datadog_checks/ tests/ arguments represent the list of files that mypy should type check. Feel free to edit them as desired, including removing tests/ (if you'd prefer to not type-check the test suite), or targeting specific files (when doing partial type checking).

Note that there is a default configuration in the mypy.ini file.

Example

Extracted from rethinkdb:

from typing import Any, Iterator # Contains the different types used

import rethinkdb

from .document_db.types import Metric

class RethinkDBCheck(AgentCheck):
    def __init__(self, *args, **kwargs):
        # type: (*Any, **Any) -> None
        super(RethinkDBCheck, self).__init__(*args, **kwargs)

    def collect_metrics(self, conn):
        # type: (rethinkdb.net.Connection) -> Iterator[Metric]
        """
        Collect metrics from the RethinkDB cluster we are connected to.
        """
        for query in self.queries:
            for metric in query.run(logger=self.log, conn=conn, config=self._config):
                yield metric

Take a look at vsphere or ibm_mq integrations for more examples.


Last update: March 7, 2024