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 bareexcept:, it also catches unexpected events like memory errors, interrupts, system exit, and so on. Preferexcept Exception:.B003: Assigning toos.environdoesn't clear the environment. Subprocesses are going to see outdated variables, in disagreement with the current process. Useos.environ.clear()or theenv=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 theiterprefix 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, usesix.iter*.B305:.next()is not a thing on Python 3. Use thenext()builtin. For Python 2 compatibility, usesix.next().B306:BaseException.messagehas been deprecated as of Python 2.6 and is removed in Python 3. Usestr(e)to access the user-readable message. Usee.argsto access arguments passed to the exception.B902: Invalid first argument used for method. Useselffor instance methods, andclsfor class methods.
logging-format¶
A flake8 plugin for ensuring a consistent logging format. We enable:
G001: Logging statements should not usestring.format()for their first argumentG002: Logging statements should not use%formatting for their first argumentG003: Logging statements should not use+concatenation for their first argumentG004: Logging statements should not usef"..."for their first argument (only in Python 3.6+)G010: Logging statements should not usewarn(usewarninginstead)G100: Logging statements should not useextraarguments unless whitelistedG201: Logging statements should not useerror(..., exc_info=True)(useexception(...)instead)G202: Logging statements should not use redundantexc_info=Trueinexception
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.