Skip to content

Carry the OWID source inside the published package - #65

Merged
jwrosewell merged 1 commit into
mainfrom
fix/publish-owid-inside-package
Aug 29, 2026
Merged

Carry the OWID source inside the published package#65
jwrosewell merged 1 commit into
mainfrom
fix/publish-owid-inside-package

Conversation

@jwrosewell

Copy link
Copy Markdown
Contributor

The live breakage

The published Python package cannot be imported at all. Installing
fiftyone-pipeline-did 4.5.12
from PyPI into a clean virtual environment and running
import fiftyone_pipeline_did gives:

File ".../site-packages/fiftyone_pipeline_did/did_client.py", line 57, in <module>
    from owid import Version
ModuleNotFoundError: No module named 'owid'

importlib.metadata.requires("fiftyone_pipeline_did") returns None, so the
published metadata declares no dependencies, and the only files in the
installed package are __init__.py, did_client.py, fod_id.py and
id_type.py. Version 4.5.11 of 8 August fails the same way, so the fault has
been live since then. Version 4.5.12 was published automatically at 01:29 on
27 August 2026 carrying the new DidClient from
#64, so the new client
is live and unusable.

The dependency cannot come from a package registry. The 51Degrees fork,
51Degrees/owid-python, is not
published to PyPI, and the name owid on PyPI
belongs to an unrelated COVID data project, so declaring a dependency on that
name would install the wrong thing.

The fix

The OWID source is copied into the package as the private module
fiftyone_pipeline_did._owid before the distributions are built.

  • ci/copy-owid-source.ps1 (new) copies owid-python/owid/*.py into
    fiftyone_pipeline_did/src/fiftyone_pipeline_did/_owid/, copies the
    Apache-2.0 LICENSE beside it and writes a NOTICE naming the upstream
    repository and the exact submodule commit the copy came from. It throws with
    a clear message if the submodule is not checked out. Nothing is fetched over
    the network, because CI clones with --recurse-submodules, so owid-python
    is already on disk.
  • ci/build-package.ps1 calls it before the distributions are built, and
    ci/build-project.ps1 calls it before the project is built, which is the
    step that precedes the unit tests in the pull request and nightly flows.
    setup.ps1 calls it for a developer checkout, and also installs the
    owid-python fork editable, which is what the tests and examples import
    under the plain name owid to build signed envelopes.
  • setup.py adds fiftyone_pipeline_did._owid to packages, declares
    cryptography>=41, which is the only third party requirement the copied
    source has, and ships the licence and notice through package_data.
    MANIFEST.in carries the same two files into the source distribution, which
    it did not do before, so the Apache-2.0 attribution now travels with the
    code.
  • fod_id.py and did_client.py import from ._owid rather than owid.
  • fiftyone_pipeline_did now exports Owid and OwidError. The public API
    refers to both of them (FodId.from_owid takes an envelope, and the parse
    methods raise the error), and after this change a caller who has only the
    published package has no other supported name for them. The docstrings that
    named owid.OwidError now name the exported one.

The leading underscore is deliberate. Shipping a top level owid package would
claim that name on the consumer's machine and collide with the unrelated PyPI
project.

The OWID source is not committed. The submodule stays the only copy of it in
the repository, and the copy the build makes is added to .gitignore.

Evidence

Every check below was run on this branch.

The tests. python -m tox -e py in fiftyone_pipeline_did, which is how
ci/run-unit-tests.ps1 runs them. Before the change: 111 collected, 109
passed, 2 skipped. After: 111 collected, 109 passed, 2 skipped. The two skips
are the live cloud tests, which need a resource key.

One test needed changing. tests/test_fodid.py builds its fixtures with the
fork imported as owid, which still works and is untouched, but
test_invalid_base64_raises caught owid.OwidError, and the package now
raises the copy's error class, which is a different class object. The test
catches the exported fiftyone_pipeline_did.OwidError instead, which is what a
caller of the published package has to catch.

The distributions. python -m build produces both. The wheel contains
fiftyone_pipeline_did/_owid/ with the eight modules, LICENSE and NOTICE,
and the source distribution contains the same under src/.

A clean install. The wheel installed into a fresh virtual environment
pulled in cryptography on its own, importlib.metadata.requires returns
['cryptography>=41'], and there is no top level owid package in that
environment. A round trip in that environment signs a real envelope with a
fresh ECDSA P-256 key pair, parses it back with FodId.from_base64 in both
base64 alphabets, checks the flags, licence id, value and identifier type,
verifies the signature against the public key, and confirms that invalid base64
raises the exported OwidError. The same round trip passes against an install
from the source distribution.

Python 3.9, the declared floor. The package byte-compiles under 3.9, and
the same round trip passes in a 3.9 virtual environment with
cryptography==41.0.7, which is the lower bound the new requirement allows.
The 3.9 build on this machine is 3.9.1, whose Rust extension loader cannot load
current cryptography releases, and that failure is in cryptography itself
rather than in this package.

A developer checkout. The submodule is untouched, git ls-files shows no
OWID source in the tree, and the copy is ignored. pwsh ./ci/copy-owid-source.ps1
with no arguments works from the repository root, and with -RepoRoot <name>
from the workspace above the clone, which is the shape CI uses. Pointed at a
directory with no submodule it exits 1 with a message naming the missing path
and the command to fix it.

What is not covered

The other five packages in this repository were not rebuilt or retested, as
nothing in this change touches them beyond the one added line in
ci/build-project.ps1, which calls the new script. The nightly pull request
run will cover them.

This change was produced with AI assistance under James Rosewell's direction
and needs human review before merge.

The published package could not be imported at all. Installing
fiftyone-pipeline-did 4.5.12 from PyPI into a clean environment and
running "import fiftyone_pipeline_did" raised ModuleNotFoundError for
"owid", because the package imported the OWID library and nothing
installed it. The published metadata declared no dependencies at all.
Version 4.5.11 of 8 August fails the same way, and 4.5.12 carries the
new cloud client, so the new client is live and unusable.

The dependency cannot come from a package registry. The 51Degrees fork
of OWID is not published to PyPI, and the name "owid" there belongs to
an unrelated project, so declaring a dependency on that name would
install the wrong thing.

The build now copies the OWID source out of the owid-python submodule
into the package as the private module fiftyone_pipeline_did._owid
before the source distribution and the wheel are built. The leading
underscore keeps the top level name "owid" free on the consumer's
machine. The Apache-2.0 licence, and a notice naming the upstream
repository and the commit the copy was taken from, travel with the
source into both distributions. The one third party requirement the
copied source has, cryptography, is now declared.

The envelope and error types the public API refers to are exported
from the package itself, because a caller who has only the published
package has no other supported name for them.

Nothing changes in the repository, where the submodule stays the only
copy of the OWID code and the copy the build makes is ignored. The
same copy step runs before the project is built, so a developer
checkout and the unit test job produce the package the same way the
publish does.
@jwrosewell
jwrosewell merged commit 2976188 into main Aug 29, 2026
1 check passed
@jwrosewell
jwrosewell deleted the fix/publish-owid-inside-package branch August 29, 2026 12:18
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