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.environ
doesn'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 theiter
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, usesix.iter*
.B305
:.next()
is not a thing on Python 3. Use thenext()
builtin. For Python 2 compatibility, usesix.next()
.B306
:BaseException.message
has been deprecated as of Python 2.6 and is removed in Python 3. Usestr(e)
to access the user-readable message. Usee.args
to access arguments passed to the exception.B902
: Invalid first argument used for method. Useself
for instance methods, andcls
for 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
(usewarning
instead)G100
: Logging statements should not useextra
arguments unless whitelistedG201
: Logging statements should not useerror(..., exc_info=True)
(useexception(...)
instead)G202
: Logging statements should not use redundantexc_info=True
inexception
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.