Style guidelines

As mentioned in CONTRIBUTING.md we use use pylint to check for style violations. Pylint attempts to enforce styles in PEP 8. To see which lint checks we enforce, see the dev_tools/conf/.pylintrc file.

Here we include some extra style guidelines.

Import statements

We follow the import standards of PEP 8, with the following guidance.

In Cirq's main implementation code (not testing code), we prefer importing the full module. This aids in mocking during tests. Thus we prefer

from cirq import ops
qubit = ops.NamedQubit('a')

in contrast to

from cirq.ops import NamedQubit
qubit = NamedQubit('a')

or (the one we would prefer, but doing this causes cyclic dependencies)

import cirq
qubit = cirq.NamedQubit('a')

The one exception to this is for the typing code, where we prefer the direct import

from typing import List

This exception allows typing hints to be more compact.

In tests, however, we prefer that we use Cirq as you would use cirq externally. For code that is in the Cirq core framework this is

import cirq
qubit = cirq.NamedQubit('a')

For Cirq code that is outside of the core and does not appear at the cirq module level, for example work in contrib, one should use the highest level possible for test code

import cirq
from cirq import contrib
contrib.circuit_to_latex_using_qcircuit(cirq.Circuit())

Of course, if this import style fundamentally cannot be used, do not let this block submitting a pull request for the code as we will definitely grant exceptions.

Type annotations

Cirq makes extensive use of type annotations as defined by PEP 484. All new code should use type annotations where possible, especially on public classes and functions to serve as documentation, but also on internal code so that the mypy typechecker can help catch coding errors.

For documentation purposes in particular, type annotations should match the way classes and functions are accessed by cirq users, which is typically on the top-level cirq namespace (for example, users use cirq.Sampler even though the sampler class is defined in cirq.work.sampler.Sampler). Code in cirq-core typically cannot import and use cirq.Sampler directly because this could create an import cycle where modules import each other (perhaps indirectly). Instead, the import of the top-level cirq library can be guarded by the TYPE_CHECKING constant provided by typing, and the type annotation can be quoted so that it is not evaluated when the module is imported but rather during type-checking:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import cirq

def accepts_sampler(sampler: 'cirq.Sampler') -> None:
    ...

Use top-level cirq.* annotations like this when annotating new public types and classes. Older code may not adhere to this style and should be updated.

Note that type annotations may need to be quoted like this in other situations as well, such as when an annotation is a "forward reference" to a class defined later in the same file.

Nomenclature

Using consistent wording across Cirq is important for lowering users cognitive load. For rule governing naming, see the nomenclature guidelines.

Datetimes

Prefer using timezone-aware datetime objects.

import datetime
dt = datetime.datetime.now(tz=datetime.timezone.utc)

Public components of Protobuf APIs will return "aware" datetime objects. JSON de-serialization will promote values to "aware" datetime objects upon deserialization.

Comparing (or testing equality) between "naive" and "aware" datetime objects throws an exception. If you are implementing a class that has datetime member variables, delegate equality and comparison operators to the built-in datetime equality and comparison operators. If you're writing a function that compares datetime objects, you can defensively promote them to "aware" objects or use their .timestamp() properties for a comparison that will never throw an exception.

Absolutely do not use datetime.utcnow() as explained in the warnings in the Python documentation.