• 
      

    Recurse into nested .local-packages when setting up build environments.

    Review Request #15103 — Created June 7, 2026 and updated

    Information

    buildthings
    master

    Reviewers

    When setting up the isolated build environment, buildthings would look
    up the .local-packages and replace any mentions of dependencies with
    those paths.

    The problem came from when one of those dependencies had its own
    .local-packages. Those would also be found at that package's own build
    time, but the resulting wheel used for the parent's isolated build
    environment wouldn't contain those replaced dependencies. This would
    cause the main build environment to fail, since it had no knowledge of
    the paths.

    Now, buildthings will recurse into .local-packages from any package
    mentioned in a .local-packages and mark them as explicit build-time
    dependencies. This differs from the normal approach of applying those to
    a list of dependencies, since we don't have the ability to intercept and
    normalize any dependencies' dependencies. So we add them to the
    build-time dependencies to ensure they get installed and satisfy the
    packages.

    Package-time dependencies are not affected.

    Unit tests pass.

    Successfully installed Review Board 9, which had a local-packages
    dependency chain looking like:

    • reviewboard -> Djblets, django_evolution
    • Djblets -> django_evolution, django-assert-queries

    This was failing to install django-assert-queries prior to this
    change. With the fix, it now finds and installs it in the build
    environment successfully.

    Summary ID
    Recurse into nested .local-packages when setting up build environments.
    When setting up the isolated build environment, buildthings would look up the `.local-packages` and replace any mentions of dependencies with those paths. The problem came from when one of those dependencies had its own `.local-packages`. Those would also be found at that package's own build time, but the resulting wheel used for the parent's isolated build environment wouldn't contain those replaced dependencies. This would cause the main build environment to fail, since it had no knowledge of the paths. Now, buildthings will recurse into `.local-packages` from any package mentioned in a `.local-packages` and mark them as explicit build-time dependencies. This differs from the normal approach of applying those to a list of dependencies, since we don't have the ability to intercept and normalize any dependencies' dependencies. So we add them to the build-time dependencies to ensure they get installed and satisfy the packages. Package-time dependencies are not affected.
    29d3de55ad492e09382d98405e02183e07f0ec64
    Description From Last Updated

    While local packages are an editable thing, just for consistency I feel like this should pass the build type in …

    david david

    Can we dedupe by (canonicalized) package name?

    david david

    Even though this is "internal", given that it's not unused, we should call it seen instead of _seen

    david david

    dict -> set

    david david

    Can we add a type annotation here?

    david david

    This method has some pretty intense side effects, prepending a bunch of stuff to sys.path and resolving dynamic keys (importing …

    david david
    Checks run (2 succeeded)
    flake8 passed.
    JSHint passed.
    david
    1. 
        
    2. buildthings/backend.py (Diff revision 1)
       
       
       
      Show all issues

      While local packages are an editable thing, just for consistency I feel like this should pass the build type in (since we have it).

      1. That'd be the wrong value, because build types don't match up in this way. The isolated build environment is installing local dependencies explicitly as editable wheels, no matter the parent package's build type, and we need to be following that.

    3. buildthings/backend.py (Diff revision 1)
       
       
       
      Show all issues

      Can we dedupe by (canonicalized) package name?

      1. I was going to work on that but it ultimately doesn't matter. It's a lot of work for no gain. The package manager will sort it out at isolated build env install time.

    4. buildthings/local_paths.py (Diff revision 1)
       
       
      Show all issues

      Even though this is "internal", given that it's not unused, we should call it seen instead of _seen

      1. It's only used for recursion into itself. I don't want it to be seen as part of the caller's responsibility in any way. Ideally it wouldn't even be in the function signature, but I didn't feel it was worth adding a nested function inside (which this started out as, and I collapsed it down).

    5. buildthings/local_paths.py (Diff revision 1)
       
       
      Show all issues

      dict -> set

    6. buildthings/local_paths.py (Diff revision 1)
       
       
      Show all issues

      Can we add a type annotation here?

    7. buildthings/local_paths.py (Diff revision 1)
       
       
      Show all issues

      This method has some pretty intense side effects, prepending a bunch of stuff to sys.path and resolving dynamic keys (importing those modules).

      Since we just care about the local_packages_path key, can we load the toml file directly and read that key?

      1. No because of the way that this computes defaults, but I'll follow up this change with one that creates a more side-effect-free loading process. That'll be a big enough change where I don't want to fold it into this.

    8.