Jelajahi Sumber

Declare cryptography as a dependency, and guard bare installs

cloudbridge.base.helpers imports cryptography at module scope, and almost
everything imports that module, so it is required for the library to
import at all - only generate_key_pair actually calls it, but the import
runs regardless. It appeared nowhere in pyproject.toml.

A plain `pip install cloudbridge` therefore produced an installation that
could not import cloudbridge.base.resources, and so could not do anything.
`cloudbridge[aws]` failed the same way, since boto3 does not depend on
cryptography either. Installations that worked did so because something
else in the environment happened to provide it - Galaxy, or an
azure/openstack install, which do. Confirmed against 4.3.1 and 4.4.0 from
clean virtualenvs; the import has been there since 952e1a2 in 2019.

Nothing in the suite could have caught this: every test environment
installs the [dev] extra, which pulls in the provider SDKs and their
transitive dependencies, so the tests pass against a package that cannot
be installed and used. Add a job that builds the wheel, installs it with
no extras, and imports the modules a user reaches for first. It runs from
outside the repository, because the working directory would otherwise
satisfy the import from the source tree and hide the very failure it is
meant to catch - which is how the first attempt at reproducing this
misled me.

Verified the guard fails against the published 4.4.0 wheel and passes
against this one.
Nuwan Goonasekera 1 hari lalu
induk
melakukan
64e42a5cd6
3 mengubah file dengan 77 tambahan dan 0 penghapusan
  1. 48 0
      .github/workflows/integration.yaml
  2. 24 0
      CHANGELOG.rst
  3. 5 0
      pyproject.toml

+ 48 - 0
.github/workflows/integration.yaml

@@ -63,6 +63,54 @@ jobs:
       - name: Run mypy
         run: tox -e mypy
 
+  bare-install:
+    name: Bare install imports
+    runs-on: ubuntu-latest
+    # Every test environment installs the [dev] extra, which drags in the
+    # provider SDKs and their transitive dependencies, so the suite passes
+    # happily against a package whose declared dependencies are incomplete.
+    # cloudbridge 4.3.1 and 4.4.0 both shipped unable to import
+    # cloudbridge.base.resources from a plain `pip install cloudbridge`,
+    # because base.helpers imports cryptography at module scope and nothing
+    # declared it. Install the built wheel on its own and import the modules
+    # a user reaches for first.
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v6
+        with:
+          persist-credentials: false
+
+      - name: Setup Python
+        uses: actions/setup-python@v6
+        with:
+          # The floor, since a missing dependency is likelier to be satisfied
+          # by chance on a newer interpreter's richer wheel set.
+          python-version: '3.10'
+
+      - name: Build the distributions
+        run: |
+          pip install build
+          python -m build
+
+      - name: Install the wheel with no extras
+        run: pip install dist/*.whl
+
+      - name: Import without the repo on sys.path
+        # Run from elsewhere: the working directory is the repo root, and
+        # Python would import the source tree in preference to the wheel,
+        # which hides exactly the failure this job exists to catch.
+        working-directory: /tmp
+        run: |
+          python -c "
+          import cloudbridge
+          from cloudbridge.base.resources import BaseBucketObject
+          from cloudbridge.base.helpers import generate_key_pair
+          from cloudbridge.factory import CloudProviderFactory
+          public, _ = generate_key_pair()
+          assert public.startswith('ssh-rsa'), public[:32]
+          print('cloudbridge', cloudbridge.get_version(), 'imports from a bare install')
+          "
+
   mock:
     name: Mock-provider tests
     runs-on: ubuntu-latest

+ 24 - 0
CHANGELOG.rst

@@ -1,3 +1,27 @@
+4.4.1 - unreleased
+------------------
+
+## Fixes
+* **``cryptography`` is now declared as a dependency.**
+  ``cloudbridge.base.helpers`` imports it at module scope, and almost
+  everything imports that module, so it was required for the library to
+  import at all - but it appeared nowhere in ``pyproject.toml``. A plain
+  ``pip install cloudbridge`` therefore produced an installation that raised
+  ``ModuleNotFoundError: No module named 'cryptography'`` on
+  ``import cloudbridge.base.resources``, as did ``cloudbridge[aws]``, since
+  boto3 does not depend on it either. Installations that worked did so
+  because something else in the environment happened to provide it. This
+  affected 4.4.0 and earlier; the import has been there since 2019.
+
+## Build and CI
+* **A new ``Bare install imports`` job builds the wheel, installs it with no
+  extras, and imports the modules a user reaches for first.** Every test
+  environment installs the ``[dev]`` extra, which pulls in the provider SDKs
+  and their transitive dependencies, so the suite passed against a package
+  whose declared dependencies were incomplete. The job runs from outside the
+  repository, so the source tree cannot satisfy the import in place of the
+  installed wheel.
+
 4.4.0 - August 21, 2026 (sha 55d925d56eaad247b960ad00e636253637192549)
 ----------------------------------------------------------------------
 

+ 5 - 0
pyproject.toml

@@ -31,6 +31,11 @@ classifiers = [
 dependencies = [
     "tenacity>=6.0",
     "pyeventsystem<2",
+    # Imported at module scope by cloudbridge.base.helpers, which almost
+    # everything else imports, so it is required for the library to import at
+    # all - not just for generate_key_pair, which is what actually uses it.
+    # Kept unpinned above the floor, per the general-purpose library policy.
+    "cryptography>=3.4",
 ]
 dynamic = ["version"]