Skip to content

Instantly share code, notes, and snippets.

@joerick
Last active March 19, 2023 11:33
Show Gist options
  • Save joerick/016153b6861f582523ab12aa3d751ec4 to your computer and use it in GitHub Desktop.
Save joerick/016153b6861f582523ab12aa3d751ec4 to your computer and use it in GitHub Desktop.
cibuildwheel high-level arch design attempt
from __future__ import annotations
import abc
from typing import ContextManager, Protocol
from cibuildwheel.logger import log
from cibuildwheel.options import Options
class PythonConfiguration(Protocol):
identifier: str
class Platform(abc.ABC):
def get_build_steps(self, build_identifiers: list[str], options: Options) -> list[BuildStep]:
...
class BuildStep(abc.ABC):
'''
A group of builds that take place in the same environment. This
abstraction is useful for the linux builds, where builds need to be
grouped in order to share the same container, and for the appropriate
setup and teardown to be performed around that container.
'''
identifiers: list[str]
def context(self) -> ContextManager[BuildStepEnv]:
...
class BuildStepEnv(abc.ABC):
'''
A build environment. This is the context in which a build takes place.
Might be on the same machine, or in a container.
'''
def python_env(self, identifier: str) -> ContextManager[PythonEnvironment]:
...
class PythonEnvironment(abc.ABC):
def __init__(self, identifier: str):
...
def call(self, command: list[str], capture_output: bool = False, add_env: dict[str, str] = None):
...
def build(platform: Platform, options: Options):
build_identifiers = [b for b in platform.build_identifiers if options.globals.build_selector(b)]
build_steps = platform.get_build_steps(build_identifiers)
for build_step in build_steps:
with build_step.context() as step_env:
step_env.copy_project_into_if_required()
with step_env.before_all_environment() as before_all_env:
before_all_env.call(options.globals.before_all)
for identifier in build_step.identifiers:
build_options = options.build_options(identifier)
with log.build(identifier), step_env.python_env(identifier) as env:
if (build_options.before_build):
log.step('Running before_build...')
env.call(build_options.before_build)
env.build_project()

This is an attempt to architect a high-level build.py script in cibuildwheel that all the platforms follow - with then a set of abstract classes/protocols that serve as the platform abstraction layer.

This is clearly an incomplete thought, but there's enough here that persuades me that this isn't a great approach.

  • The 'step' abstraction isn't needed on mac/windows, but should we include logging steps for that? We should on linux, but that means that the linux platform is using logger.log as well as the build script. seems like it's getting messy. (Though, that might not be so bad...)
  • my head starts to swirl when considering who's in control of the environment on each of these. It feels like I have to split my attention between 3 places - the step adds stuff to the env, the python environment, the build script... that's true in reality currently, but maybe it seems clearer there.
  • there's gonna be some messy stuff around the project copy in/wheels copy out steps. the lazy thing to do here would be to copy in to a temp env on mac/windows anyway to match what linux is doing. but that means another copy, and people with big projects don't like that much! (and people that use SCM-based versioning)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment