Cirq has a modular architecture and is organized in a monorepo. All of the modules follow the same folder structure.
Each module is structured as follows. Let's take as example a module named cirq-example:
cirq-example
├── cirq_example
│ ├── __init__.py
│ ├── _version.py
│ ├── json_resolver_cache.py
│ └── json_test_data
│ ├── __init__.py
│ └── spec.py
├── LICENSE
├── README.md
├── requirements.txt
├── setup.cfg
└── setup.py
Note that typically there is only a single top level package, cirq_example - but there might be exceptions.
Additionally, there is a metapackage named cirq that's a completely different beast and just depends on the modules.
This enables pip install cirq to have all the included modules to be installed for our users.
All modules should depend on cirq-core, which is the central, core library for Cirq.
Packaging
Each package gets published to PyPI as a separate package. To build all the wheel files locally, use
dev_tools/packaging/produce-package.sh ./dist "$(./dev_tools/packaging/generate-dev-version-id.sh)"
Packages are versioned together, share the same version number, and are released together.
Setting up a new module
To set up a new module follow these steps:
- Create the folder structure above, copy the files based on an existing module
- The
LICENSEfile should be the same - The
README.mdfile will be the documentation that appears in PyPI - The
setup.pyfile should specify aninstall_requiresconfiguration that hascirq-core=={module.version}at the minimum
- The
- Set up JSON serialization for each top level Python package
Setting up JSON serialization
Add the
<top_level_package>/json_resolver_cache.pyfile@functools.lru_cache() # pragma: no cover def _class_resolver_dictionary() -> dict[str, ObjectFactory]: # pragma: no cover return {}Register the resolver cache at the end of the
<top_level_package>/__init__.py:# Registers cirq_example's public classes for JSON serialization. from cirq.protocols.json_serialization import _register_resolver from cirq_example.json_resolver_cache import _class_resolver_dictionary _register_resolver(_class_resolver_dictionary)Add the
<top_level_package>/json_test_datafolder with the following content:spec.pycontains the core test specification for JSON testing, that plugs into the central framework:import pathlib import cirq_example from cirq_example.json_resolver_cache import _class_resolver_dictionary from cirq.testing.json import ModuleJsonTestSpec TestSpec = ModuleJsonTestSpec( name="cirq_example", packages=[cirq_example], test_data_path=pathlib.Path(__file__).parent, not_yet_serializable=[], should_not_be_serialized=[], resolver_cache=_class_resolver_dictionary(), deprecated={}, )__init__.pyshould importTestSpecfromspec.pyin
cirq/protocols/json_serialization_test.pyadd'cirq_example':Noneto theTESTED_MODULESvariable.TESTED_MODULESis also used to prepare the test framework for deprecation warnings. With new modules, we useNoneas there is no deprecation setup.
You can run check/pytest-changed-files and that should execute the script json_serialization_test.py as well.
That's it! Now, you can follow the Serialization guide for adding and removing serializable objects.
Utilities
List modules
To iterate through modules, you can list them by invoking dev_tools/modules.py.
python dev_tools/modules.py list
There are different modes of listing (e.g., the folder, package-path, top level package),
you can refer to python dev_tools/modules.py list --help for the most up to date features.