Skip to content

fix: declare the three dependencies the code imports, and drop the async_generator shim - #16

Merged
tannevaled merged 1 commit into
masterfrom
ci-and-declared-deps
Sep 6, 2026
Merged

tannevaled merged 1 commit into
masterfrom
ci-and-declared-deps

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

This package installs and then cannot be imported.

>>> import libcloudspawner.spawner
ModuleNotFoundError: No module named 'async_generator'

and once that is dealt with, again: ModuleNotFoundError: No module named 'requests'.

Comparing every third-party import in the package against pyproject.toml:

imported jinja2, jupyterhub, libcloud, requests, tornado, traitlets
declared jinja2, jupyterhub, apache-libcloud
missing requests, tornado, traitlets

tornado and traitlets happen to be installed today because jupyterhub pulls them in, so only requests breaks visibly — but the code imports all three directly, and a transitive that a dependency later drops is a break waiting for someone else's release.

async_generator is deleted rather than declared. It is a compatibility shim for Python 3.5; this project requires ≥3.7, where async def + yield is the language:

@async_generator
async def progress(self):
    while True:
        await yield_(self.nodemanager.spawner_state_event)

becomes a plain async generator. Declaring a shim as a dependency to keep a 2019 workaround alive would have been the wrong repair.

Also the first CI lane this repository has ever had. The only workflow was python-publish.yml, which releases and does not run on a pull request — so nothing checked the four dependency pull requests open here, and none of them could have caught the missing imports either.

There are no tests in the tree, so the lane does not pretend to run any. It proves install, import, packaging, and one behaviour: that LibcloudSpawner.progress is a real async generator, the contract JupyterHub calls it under. That is a behaviour check rather than a grep because a plain async def that never yields would still import cleanly while silently no longer being a generator.

Verified in a fresh virtualenv outside the source tree: pip install -e ., import, the async-generator assertion and python -m build all pass on Python 3.14. actionlint clean.

🤖 Generated with Claude Code

…ync_generator shim

This package installs and then cannot be imported:

    >>> import libcloudspawner.spawner
    ModuleNotFoundError: No module named 'async_generator'

and once that is dealt with, again:

    ModuleNotFoundError: No module named 'requests'

Comparing every third-party import in the package against pyproject.toml:

    imported : jinja2, jupyterhub, libcloud, requests, tornado, traitlets
    declared : jinja2, jupyterhub, apache-libcloud
    missing  : requests, tornado, traitlets

tornado and traitlets happen to be installed today because jupyterhub pulls
them in, so the break only shows for requests -- but the code imports all three
directly, and a transitive that a dependency later drops is a break waiting for
someone else's release. All three are declared now.

async_generator is handled differently, by deletion rather than declaration.
It is a compatibility shim for Python 3.5, and this project requires >=3.7,
where `async def` + `yield` is the language. So:

    @async_generator
    async def progress(self):
        while True:
            await yield_(self.nodemanager.spawner_state_event)

becomes a plain async generator with `yield`. Declaring a shim as a dependency
to keep a 2019 workaround alive would have been the wrong repair.

Also adds the first CI lane this repository has ever had. The only workflow
here was python-publish.yml, which releases and does not run on a pull request,
so nothing checked the four dependency pull requests open against this
repository -- and none of them could have caught the missing imports either.

There are no tests in the tree, so the lane does not pretend to run any. It
proves installation, import, packaging, and one behaviour: that
LibcloudSpawner.progress is a real async generator, which is the contract
JupyterHub calls it under. That last one is a behaviour check rather than a
grep because replacing the decorated coroutine with a plain `async def` that
never yields would still import cleanly while silently no longer being a
generator.

Verified in a FRESH virtualenv, outside the source tree: pip install -e .,
import, the async-generator assertion, and python -m build all pass on
Python 3.14. actionlint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 8484e39 into master Sep 6, 2026
2 checks passed
@tannevaled
tannevaled deleted the ci-and-declared-deps branch September 6, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant