Building binary wheels

Several packages in the Zope Foundation repositories contain C code, which means they should be packaged and published as binary wheels. Building such binary wheels has its pitfalls, though. If not done right they can cause application instability and crashes.


Publishing binary wheels built in a local build environment is highly discouraged! Every build environment is different, binary wheels made for publication on PyPI should only be built in standardized and predictable environments.

To minimize problems we have automated binary wheel building using CI providers like AppVeyor and GitHub Actions. Wheels are built and uploaded using a special PyPI account named zope.wheelbuilder. These configurations and the PyPI account are currently maintained by Marius Gedminas, Michael Howitz and Jens Vagelpohl. You can contact them by posting your question or bug report in the meta repository issue tracker. Here is a short description of the process, please note that some steps can only be performed by the configuration maintainers:


  • Every project publishing binary wheels must add the PyPI account zope.wheelbuilder to the list of project maintainers on PyPI.

  • Log into the zope.wheelbuilder account and create an API token for the project on the “Account settings” page with upload permissions and the project name as scope. Copy the token value - this is the only time you can.

Using Appveyor

  • Log into Appveyor at, click on “Account” at the top and then “Encrypt YAML” on the left-hand menu. Paste the copied token into the input field and then copy the encrypted value.

  • If the project already uses the standardized parameterized project configuration add the following to the .meta.toml file in the [appveyor] section, replacing <USER> with the Appveyor account used and <ENCRYPTED TOKEN> with the encrypted value from the previous step. Then rebuild the configuration with so the actual appveyor.yml file is updated:

global-env-vars = [
    "# Currently the builds use @<USER>'s Appveyor account.  The PyPI token",
    "# belongs to zope.wheelbuilder, managed by @mgedmin and @dataflake.",
    "  TWINE_USERNAME: __token__",
    "    secure: <ENCRYPTED TOKEN>",
  • If you are not using the standardized project config you can edit appveyor.yml directly:


    # Currently the builds use @<USER>'s Appveyor account.  The PyPI token
    # belongs to zope.wheelbuilder, managed by @mgedmin and @dataflake.

    TWINE_USERNAME: __token__
      secure: <ENCRYPTED TOKEN>


  - ps: if ($env:APPVEYOR_REPO_TAG -eq $TRUE) { pip install twine; twine upload --skip-existing dist\*.whl }

deploy: on

Using GitHub Actions