From 5360642d5f043bacea299dfdd6af4be2a3d8f6e0 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 01:15:34 +0900 Subject: [PATCH 1/8] Add deprecation shims for arcadedb-embedded and arcadedb-embedded-native (26.11.1) Two pure-Python shim distributions under bindings/python/deprecation/ that depend on the new packages (arcadedb, arcadedb-native) at the same version, warn once with a FutureWarning at import, and re-export the new module with its submodules aliased. A build script (uv build, no upload), a test against a stub arcadedb module, the migration guide, deprecation notices in the README and the docs index, and the cutover steps. The real package in bindings/python/src is unchanged. Co-Authored-By: Claude Sonnet 5.5 --- bindings/python/AGENTS.md | 1 + bindings/python/deprecation/MIGRATION.md | 41 +++ bindings/python/deprecation/README.md | 72 +++++ .../arcadedb-embedded-native/README.md | 29 ++ .../arcadedb-embedded-native/pyproject.toml | 47 +++ .../src/arcadedb_embedded/__init__.py | 95 ++++++ .../deprecation/arcadedb-embedded/README.md | 27 ++ .../arcadedb-embedded/pyproject.toml | 47 +++ .../src/arcadedb_embedded/__init__.py | 95 ++++++ bindings/python/deprecation/build_shims.py | 101 +++++++ bindings/python/docs/development/release.md | 4 + bindings/python/docs/development/testing.md | 1 + bindings/python/tests/conftest.py | 5 + .../python/tests/test_deprecation_shims.py | 281 ++++++++++++++++++ 14 files changed, 846 insertions(+) create mode 100644 bindings/python/deprecation/MIGRATION.md create mode 100644 bindings/python/deprecation/README.md create mode 100644 bindings/python/deprecation/arcadedb-embedded-native/README.md create mode 100644 bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml create mode 100644 bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py create mode 100644 bindings/python/deprecation/arcadedb-embedded/README.md create mode 100644 bindings/python/deprecation/arcadedb-embedded/pyproject.toml create mode 100644 bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py create mode 100644 bindings/python/deprecation/build_shims.py create mode 100644 bindings/python/tests/test_deprecation_shims.py diff --git a/bindings/python/AGENTS.md b/bindings/python/AGENTS.md index 35f595d7804..ee88e89f493 100644 --- a/bindings/python/AGENTS.md +++ b/bindings/python/AGENTS.md @@ -17,6 +17,7 @@ Paths in this file are relative to the repository root (the directory that conta | reporting an engine bug upstream, sending a low-hanging engine fix, or checking an upstream fix to one of our issues | `bindings/python/docs/development/upstream-issues.md` | | the periodic `bindings/python` update to ArcadeData/arcadedb (retired 2026-10-25; the bindings continue in ArcadeData/arcadedb-python) | `bindings/python/docs/development/upstream-bindings-pr.md` | | releasing | `bindings/python/docs/development/release.md` | +| publishing the last `arcadedb-embedded` and `arcadedb-embedded-native` releases (the deprecation shims), or telling users how to move | `bindings/python/deprecation/README.md`, `bindings/python/deprecation/MIGRATION.md` | | debugging a build, JVM or test failure | `bindings/python/docs/development/troubleshooting.md` | | making a performance claim, or working around an engine bug | `bindings/python/docs/guide/performance.md`, `bindings/python/docs/guide/known-issues.md`, `bindings/python/docs/benchmarks/protocol.md` | diff --git a/bindings/python/deprecation/MIGRATION.md b/bindings/python/deprecation/MIGRATION.md new file mode 100644 index 00000000000..58e1ae3df61 --- /dev/null +++ b/bindings/python/deprecation/MIGRATION.md @@ -0,0 +1,41 @@ +# Migrating from arcadedb-embedded to arcadedb + +The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python), under new PyPI names. This repository is archived after the move. Documentation: [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/). + +## Install and import + +| Before | After | +|---|---| +| `pip install arcadedb-embedded` | `pip install arcadedb` | +| `pip install arcadedb-embedded-native` (preview) | `pip install arcadedb-native` | +| `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb[arrow,pandas]'` | +| `uv add arcadedb-embedded` | `uv add arcadedb` | +| `import arcadedb_embedded` | `import arcadedb` | +| `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | +| `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... | `arcadedb.core`, `arcadedb.jvm`, ... | + +Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-native`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded`. + +## Version mapping + +From 26.11.1 the package version is the ArcadeDB engine version, as before. + +| Old | Last release | New | +|---|---|---| +| `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb` 26.11.1 and later | +| `arcadedb-embedded-native` (preview) | 26.10.2.dev1, then the shim 26.11.1 | `arcadedb-native` 26.11.1 and later | + +The 26.11.1 releases of the old names are shims and the last ones. Each installs the new package at exactly 26.11.1; importing `arcadedb_embedded` then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. A shim never upgrades: for newer versions install `arcadedb` or `arcadedb-native`. Older releases of the old names stay on PyPI for pinned installs. + +## What stays the same + +- The Python API: the module is renamed, its classes and functions are the ones of the 26.11.1 bindings. +- Python 3.11 to 3.14, and the Apache-2.0 license. +- The extras `vector`, `arrow`, `pandas` and `polars`, under the same names. +- Where to look: the user guide and API reference move with the code to the new docs site, and the old `docs.humem.ai/arcadedb` pages redirect there. + +## What changes + +- The distribution and import names, as above. +- Issues and pull requests go to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python/issues). Benchmarks stay with HumemAI, in [humemai/dbbench](https://github.com/humemai/dbbench). +- Engine changes between 26.10.2 and 26.11.1: read the 26.11.1 release notes, as for any upgrade. diff --git a/bindings/python/deprecation/README.md b/bindings/python/deprecation/README.md new file mode 100644 index 00000000000..49769b13033 --- /dev/null +++ b/bindings/python/deprecation/README.md @@ -0,0 +1,72 @@ +# Deprecation shims for the old PyPI names + +The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) as `arcadedb` and `arcadedb-native` (import `arcadedb` for both). This folder holds the last release of each old name: a pure-Python shim that depends on the new package at the same version, warns once, and re-exports it. Users: see [MIGRATION.md](MIGRATION.md). + +| Folder | PyPI project | Depends on | Version | +|---|---|---|---| +| `arcadedb-embedded/` | `arcadedb-embedded` | `arcadedb==26.11.1` | 26.11.1 | +| `arcadedb-embedded-native/` | `arcadedb-embedded-native` | `arcadedb-native==26.11.1` | 26.11.1 | + +The two `arcadedb_embedded/__init__.py` files differ only in their two constants; `tests/test_deprecation_shims.py` checks that the rest is identical. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. + +## Build and test (never uploads) + +```bash +python3 bindings/python/deprecation/build_shims.py --out /tmp/shims # uv build, wheel + sdist of both +taskset -c 0-3 uv run pytest bindings/python/tests/test_deprecation_shims.py # needs the real wheel installed, like the rest of the suite +``` + +`build_shims.py` checks each version with `scripts/check_version.py` first; add `--check-pypi` on cutover day to check it is the next version on PyPI (it fails once that version is published). + +## Cutover day + +1. Release `arcadedb` 26.11.1 (and `arcadedb-native`) from ArcadeData/arcadedb-python first. Check `pip download --no-deps arcadedb==26.11.1` and `arcadedb-native==26.11.1` work: the shims pin these exact versions. +2. Never push a `26.11.1` tag in this repository: `release-python-packages.yml` would build and publish the real wheels under `arcadedb-embedded`. +3. Build and check: `python3 bindings/python/deprecation/build_shims.py --out dist --check-pypi && uvx twine check dist/*`. +4. Publish both shims. Trusted publishing works only from a workflow, so either (a) register a trusted publisher on both PyPI projects (repository `humemai/arcadedb-embedded-python`, workflow `publish-deprecation-shims.yml`, environment `pypi`), add the workflow below to `.github/workflows/` (and to `FORK_GITHUB_ALLOWLIST` in `sync-upstream.sh` if another sync will still run), and dispatch it with `publish` on; or (b) upload from a laptop with `uvx twine upload dist/*` and a project-scoped API token. +5. In a clean environment: `pip install arcadedb-embedded==26.11.1`, `python -W default -c "import arcadedb_embedded"` shows one FutureWarning; the same for `arcadedb-embedded-native`. + +```yaml +name: Publish deprecation shims +on: + workflow_dispatch: + inputs: + publish: + description: "Upload to PyPI (off: build and check only)" + type: boolean + default: false +permissions: + contents: read +jobs: + build: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Setup UV package manager + run: | + installer="$(mktemp)" + curl -LsSf --retry 5 --retry-all-errors --retry-delay 5 -o "$installer" https://astral.sh/uv/0.12.0/install.sh + sh "$installer" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + - run: python3 bindings/python/deprecation/build_shims.py --out dist --check-pypi + - run: uvx twine check dist/*.whl dist/*.tar.gz + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: shims + path: dist/ + publish: + needs: build + if: inputs.publish + runs-on: ubuntu-24.04 + environment: pypi + permissions: + id-token: write + steps: + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: shims + path: dist/ + - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 + with: + packages-dir: dist/ +``` diff --git a/bindings/python/deprecation/arcadedb-embedded-native/README.md b/bindings/python/deprecation/arcadedb-embedded-native/README.md new file mode 100644 index 00000000000..dcb19fedcc6 --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded-native/README.md @@ -0,0 +1,29 @@ +# arcadedb-embedded-native has moved to arcadedb-native + +**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name: + +| | Old | New | +|---|---|---| +| PyPI package | `arcadedb-embedded-native` | [`arcadedb-native`](https://pypi.org/project/arcadedb-native/) | +| Import | `import arcadedb_embedded` | `import arcadedb` | +| Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | +| Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | + +## What to do + +```bash +pip uninstall arcadedb-embedded-native +pip install arcadedb-native +``` + +Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). The new package is the same native-image bindings, published under the new name. `arcadedb-native` 26.11.1 has the same version number as this release. + +`arcadedb-native` and `arcadedb` both provide the module `arcadedb`: install one of them in an environment, not both. + +## What this release does + +It is a shim. It installs `arcadedb-native==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb-native==26.11.1` and gets no updates: to receive newer versions, switch to `arcadedb-native`. + +The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb-native`. + +Older `arcadedb-embedded-native` releases (the previews 26.10.2.dev0 and 26.10.2.dev1) stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml new file mode 100644 index 00000000000..f5a8885d1a1 --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml @@ -0,0 +1,47 @@ +# Deprecation shim for arcadedb-embedded-native (the last release). Built by ../build_shims.py; see ../README.md. +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "arcadedb-embedded-native" +version = "26.11.1" +description = "Deprecated: arcadedb-embedded-native has moved to arcadedb-native (import arcadedb). Last release, a thin shim." +readme = "README.md" +requires-python = ">=3.11" +license = "Apache-2.0" +license-files = ["LICENSE"] +authors = [{ name = "Taewoon Kim", email = "taewoon@humem.ai" }] +keywords = ["arcadedb", "deprecated", "moved", "database"] +classifiers = [ + "Development Status :: 7 - Inactive", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Database", +] +# The shim installs exactly the package it points to, at the same version. +dependencies = ["arcadedb-native==26.11.1"] + +# The extras people installed from the old name, forwarded to the new one. +[project.optional-dependencies] +vector = ["arcadedb-native[vector]==26.11.1"] +arrow = ["arcadedb-native[arrow]==26.11.1"] +pandas = ["arcadedb-native[pandas]==26.11.1"] +polars = ["arcadedb-native[polars]==26.11.1"] + +[project.urls] +Homepage = "https://github.com/ArcadeData/arcadedb-python" +Documentation = "https://docs.arcadedb.com/arcadedb-python/" +Source = "https://github.com/ArcadeData/arcadedb-python" +Issues = "https://github.com/ArcadeData/arcadedb-python/issues" +"Replaced by" = "https://pypi.org/project/arcadedb-native/" + +[tool.hatch.build.targets.wheel] +packages = ["src/arcadedb_embedded"] + +[tool.hatch.build.targets.sdist] +include = ["src", "README.md", "LICENSE", "pyproject.toml"] diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py new file mode 100644 index 00000000000..b4446b64b08 --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py @@ -0,0 +1,95 @@ +# Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) +# SPDX-License-Identifier: Apache-2.0 +"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb-native``. + +This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at +import, then hands every name and submodule over to the ``arcadedb`` module. +""" + +# ---- per-shim constants: the two shims differ only in this block ---- +_OLD_DIST = "arcadedb-embedded-native" +_NEW_DIST = "arcadedb-native" +# ---- shared body: identical in both shims (tests/test_deprecation_shims.py checks it) ---- + +import importlib +import importlib.abc +import importlib.machinery +import importlib.util +import sys +import warnings + +_MESSAGE = ( + f"The {_OLD_DIST!r} package has moved and this is its last release. " + f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " + "`import arcadedb` where you wrote `import arcadedb_embedded`. " + "New home: https://github.com/ArcadeData/arcadedb-python, " + "docs: https://docs.arcadedb.com/arcadedb-python/" +) +# FutureWarning, not DeprecationWarning: Python shows it to end users by default. +warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) + +import arcadedb as _new # noqa: E402 +from arcadedb import * # noqa: E402,F401,F403 + +_OLD = __name__ +_NEW = _new.__name__ +__all__ = list( + getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] +) + + +class _AliasLoader(importlib.abc.Loader): + """Hand out the real ``arcadedb`` submodule under the old name, not a second copy.""" + + def __init__(self, target): + self._target = target + self._spec = None + + def create_module(self, spec): + module = importlib.import_module(self._target) + self._spec = module.__spec__ + return module + + def exec_module(self, module): + # the import machinery stamped the alias spec on the shared module: put its own back + module.__spec__ = self._spec + + +class _AliasFinder(importlib.abc.MetaPathFinder): + """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" + + alias_of = _NEW + + def find_spec(self, fullname, path=None, target=None): + if not fullname.startswith(_OLD + "."): + return None + real = _NEW + fullname[len(_OLD) :] + try: + found = importlib.util.find_spec(real) + except (ImportError, ValueError): + return None + if found is None: + return None + return importlib.machinery.ModuleSpec(fullname, _AliasLoader(real)) + + +# first in line, so a package alias is not also found on disk and loaded a second time +if not any(getattr(f, "alias_of", None) == _NEW for f in sys.meta_path): + sys.meta_path.insert(0, _AliasFinder()) + +# submodules the new package has already imported answer to the old name right away +for _name, _module in list(sys.modules.items()): + if _name.startswith(_NEW + ".") and _module is not None: + sys.modules.setdefault(_OLD + _name[len(_NEW) :], _module) + + +def __getattr__(name): + """Anything not star-imported (a private name, a submodule) comes from ``arcadedb``.""" + try: + return getattr(_new, name) + except AttributeError: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None + + +def __dir__(): + return sorted(set(globals()) | set(dir(_new))) diff --git a/bindings/python/deprecation/arcadedb-embedded/README.md b/bindings/python/deprecation/arcadedb-embedded/README.md new file mode 100644 index 00000000000..c4c68a2e26c --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded/README.md @@ -0,0 +1,27 @@ +# arcadedb-embedded has moved to arcadedb + +**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name: + +| | Old | New | +|---|---|---| +| PyPI package | `arcadedb-embedded` | [`arcadedb`](https://pypi.org/project/arcadedb/) | +| Import | `import arcadedb_embedded` | `import arcadedb` | +| Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | +| Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | + +## What to do + +```bash +pip uninstall arcadedb-embedded +pip install arcadedb +``` + +Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). The new package is the same bindings, published under the new name. `arcadedb` 26.11.1 has the same version number as this release. + +## What this release does + +It is a shim. It installs `arcadedb==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb==26.11.1` and gets no updates: to receive newer ArcadeDB versions, switch to `arcadedb`. + +The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb`. + +Older `arcadedb-embedded` releases stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml new file mode 100644 index 00000000000..3277d9e63ea --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml @@ -0,0 +1,47 @@ +# Deprecation shim for arcadedb-embedded (the last release). Built by ../build_shims.py; see ../README.md. +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "arcadedb-embedded" +version = "26.11.1" +description = "Deprecated: arcadedb-embedded has moved to arcadedb (import arcadedb). Last release, a thin shim." +readme = "README.md" +requires-python = ">=3.11" +license = "Apache-2.0" +license-files = ["LICENSE"] +authors = [{ name = "Taewoon Kim", email = "taewoon@humem.ai" }] +keywords = ["arcadedb", "deprecated", "moved", "database"] +classifiers = [ + "Development Status :: 7 - Inactive", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Topic :: Database", +] +# The shim installs exactly the package it points to, at the same version. +dependencies = ["arcadedb==26.11.1"] + +# The extras people installed from the old name, forwarded to the new one. +[project.optional-dependencies] +vector = ["arcadedb[vector]==26.11.1"] +arrow = ["arcadedb[arrow]==26.11.1"] +pandas = ["arcadedb[pandas]==26.11.1"] +polars = ["arcadedb[polars]==26.11.1"] + +[project.urls] +Homepage = "https://github.com/ArcadeData/arcadedb-python" +Documentation = "https://docs.arcadedb.com/arcadedb-python/" +Source = "https://github.com/ArcadeData/arcadedb-python" +Issues = "https://github.com/ArcadeData/arcadedb-python/issues" +"Replaced by" = "https://pypi.org/project/arcadedb/" + +[tool.hatch.build.targets.wheel] +packages = ["src/arcadedb_embedded"] + +[tool.hatch.build.targets.sdist] +include = ["src", "README.md", "LICENSE", "pyproject.toml"] diff --git a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py new file mode 100644 index 00000000000..cd0adc4eeb5 --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py @@ -0,0 +1,95 @@ +# Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) +# SPDX-License-Identifier: Apache-2.0 +"""Deprecated: ``arcadedb-embedded`` has moved to ``arcadedb``. + +This is the last release of ``arcadedb-embedded``. It is a thin shim: it warns once at +import, then hands every name and submodule over to the ``arcadedb`` module. +""" + +# ---- per-shim constants: the two shims differ only in this block ---- +_OLD_DIST = "arcadedb-embedded" +_NEW_DIST = "arcadedb" +# ---- shared body: identical in both shims (tests/test_deprecation_shims.py checks it) ---- + +import importlib +import importlib.abc +import importlib.machinery +import importlib.util +import sys +import warnings + +_MESSAGE = ( + f"The {_OLD_DIST!r} package has moved and this is its last release. " + f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " + "`import arcadedb` where you wrote `import arcadedb_embedded`. " + "New home: https://github.com/ArcadeData/arcadedb-python, " + "docs: https://docs.arcadedb.com/arcadedb-python/" +) +# FutureWarning, not DeprecationWarning: Python shows it to end users by default. +warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) + +import arcadedb as _new # noqa: E402 +from arcadedb import * # noqa: E402,F401,F403 + +_OLD = __name__ +_NEW = _new.__name__ +__all__ = list( + getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] +) + + +class _AliasLoader(importlib.abc.Loader): + """Hand out the real ``arcadedb`` submodule under the old name, not a second copy.""" + + def __init__(self, target): + self._target = target + self._spec = None + + def create_module(self, spec): + module = importlib.import_module(self._target) + self._spec = module.__spec__ + return module + + def exec_module(self, module): + # the import machinery stamped the alias spec on the shared module: put its own back + module.__spec__ = self._spec + + +class _AliasFinder(importlib.abc.MetaPathFinder): + """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" + + alias_of = _NEW + + def find_spec(self, fullname, path=None, target=None): + if not fullname.startswith(_OLD + "."): + return None + real = _NEW + fullname[len(_OLD) :] + try: + found = importlib.util.find_spec(real) + except (ImportError, ValueError): + return None + if found is None: + return None + return importlib.machinery.ModuleSpec(fullname, _AliasLoader(real)) + + +# first in line, so a package alias is not also found on disk and loaded a second time +if not any(getattr(f, "alias_of", None) == _NEW for f in sys.meta_path): + sys.meta_path.insert(0, _AliasFinder()) + +# submodules the new package has already imported answer to the old name right away +for _name, _module in list(sys.modules.items()): + if _name.startswith(_NEW + ".") and _module is not None: + sys.modules.setdefault(_OLD + _name[len(_NEW) :], _module) + + +def __getattr__(name): + """Anything not star-imported (a private name, a submodule) comes from ``arcadedb``.""" + try: + return getattr(_new, name) + except AttributeError: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None + + +def __dir__(): + return sorted(set(globals()) | set(dir(_new))) diff --git a/bindings/python/deprecation/build_shims.py b/bindings/python/deprecation/build_shims.py new file mode 100644 index 00000000000..b49e7fbd5cc --- /dev/null +++ b/bindings/python/deprecation/build_shims.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) +# SPDX-License-Identifier: Apache-2.0 +"""Build the two deprecation shims (``arcadedb-embedded``, ``arcadedb-embedded-native``) with ``uv build``. + +This builds only. It never uploads: publishing is a separate, deliberate step (README.md here). + +Each shim is copied to a scratch directory first, together with the repository's LICENSE, so the +license file ships in the wheel and the sdist and the source tree stays clean. The version in the +shim's pyproject.toml must pass ``scripts/check_version.py`` (syntax and counting rule). + +usage: + build_shims.py [--out DIR] [--shim NAME ...] [--check-pypi] + + --out DIR where the wheels and sdists go (default: dist/ next to this script, git-ignored) + --shim NAME build only this shim (repeatable; default: both) + --check-pypi also run check_version.py against the live PyPI project (needs the network, and + fails once that version has been published, as the counting rule says) +""" + +from __future__ import annotations + +import argparse +import importlib.util +import shutil +import subprocess # nosec B404 - runs `uv build` with a fixed argument list +import sys +import tempfile +import tomllib +from pathlib import Path + +HERE = Path(__file__).resolve().parent +REPO_ROOT = HERE.parents[2] +SHIMS = ("arcadedb-embedded", "arcadedb-embedded-native") + + +def _load_check_version(): + path = HERE.parent / "scripts" / "check_version.py" + spec = importlib.util.spec_from_file_location("check_version", path) + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + sys.modules["check_version"] = module + spec.loader.exec_module(module) + return module + + +def shim_version(shim: str) -> str: + """The version in the shim's pyproject.toml.""" + with open(HERE / shim / "pyproject.toml", "rb") as handle: + return tomllib.load(handle)["project"]["version"] + + +def check_version(shim: str, check_pypi: bool) -> None: + """Fail unless the shim's version follows the counting rule (and is the next one on PyPI).""" + cv = _load_check_version() + version = shim_version(shim) + argv = [version] + if check_pypi: + argv += ["--pypi", shim] + if cv.main(argv) != 0: + raise SystemExit(f"{shim} {version} does not pass check_version.py") + + +def build(shim: str, out_dir: Path) -> list[Path]: + """Build one shim into out_dir and return the files it produced.""" + uv = shutil.which("uv") + if uv is None: + raise SystemExit("uv is required (https://docs.astral.sh/uv/)") + out_dir.mkdir(parents=True, exist_ok=True) + before = set(out_dir.iterdir()) + with tempfile.TemporaryDirectory(prefix=f"{shim}-") as scratch: + stage = Path(scratch) / shim + shutil.copytree( + HERE / shim, stage, ignore=shutil.ignore_patterns("__pycache__", "dist") + ) + shutil.copyfile(REPO_ROOT / "LICENSE", stage / "LICENSE") + subprocess.run( # nosec B603 - fixed argument list, no shell + [uv, "build", "--out-dir", str(out_dir), str(stage)], check=True + ) + made = set(out_dir.iterdir()) - before + return sorted(p for p in made if p.suffix == ".whl" or p.name.endswith(".tar.gz")) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) + parser.add_argument("--out", type=Path, default=HERE / "dist") + parser.add_argument("--shim", action="append", choices=SHIMS) + parser.add_argument("--check-pypi", action="store_true") + args = parser.parse_args(argv) + + shims = args.shim or list(SHIMS) + for shim in shims: + check_version(shim, args.check_pypi) + for shim in shims: + for artifact in build(shim, args.out): + print(f"built {artifact}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bindings/python/docs/development/release.md b/bindings/python/docs/development/release.md index 2453830074a..12970aba6b4 100644 --- a/bindings/python/docs/development/release.md +++ b/bindings/python/docs/development/release.md @@ -381,6 +381,10 @@ uv run --project .. --group docs mike alias --update-aliases \ 3. Check "Set as a pre-release" 4. Or delete the release entirely +## The Last Releases: Deprecation Shims + +The bindings move to ArcadeData/arcadedb-python as `arcadedb` and `arcadedb-native`. The old names end with one shim release each, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that depends on the new package at the same version and warns once). They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. + ## Troubleshooting ### PyPI upload fails diff --git a/bindings/python/docs/development/testing.md b/bindings/python/docs/development/testing.md index f81b5cc1e8b..c90e0672671 100644 --- a/bindings/python/docs/development/testing.md +++ b/bindings/python/docs/development/testing.md @@ -143,6 +143,7 @@ asserts, so the list below stops at the file. | `test_wheel_platform_tag.py` | The wheel's manylinux platform tag, and `__version__` equals the installed distribution version | | `test_wheel_size.py` | The wheel size gate: a wheel at or over 100 decimal megabytes fails the build | | `test_check_test_skips.py` | The skip gate described below | +| `test_deprecation_shims.py` | The shims for the old PyPI names (`deprecation/`): both build with `uv build` as pure `py3-none-any` wheels, each pins the new package at the same version, the version follows the counting rule, and against a stub `arcadedb` the import warns once with a `FutureWarning` and names, submodules (also not-yet-imported and nested ones) and private attributes resolve to the new module. Not collected where `deprecation/` is absent (the upstream pull request branch) | ### Docs and open engine findings diff --git a/bindings/python/tests/conftest.py b/bindings/python/tests/conftest.py index 8198f3305be..0f19dbe88ba 100644 --- a/bindings/python/tests/conftest.py +++ b/bindings/python/tests/conftest.py @@ -28,6 +28,11 @@ if not os.path.isdir(os.path.join(os.path.dirname(os.path.dirname(__file__)), "docs")): # the upstream pull request branch has no docs/ directory collect_ignore.append("test_docs_examples.py") +if not os.path.isdir( + os.path.join(os.path.dirname(os.path.dirname(__file__)), "deprecation") +): + # the upstream pull request branch has no deprecation/ directory (the shims for the old PyPI names) + collect_ignore.append("test_deprecation_shims.py") @pytest.hookimpl(trylast=True) diff --git a/bindings/python/tests/test_deprecation_shims.py b/bindings/python/tests/test_deprecation_shims.py new file mode 100644 index 00000000000..e44b7a0e66b --- /dev/null +++ b/bindings/python/tests/test_deprecation_shims.py @@ -0,0 +1,281 @@ +"""The deprecation shims for the old PyPI names (deprecation/): built here, checked against a stub. + +`arcadedb-embedded` and `arcadedb-embedded-native` get one last release, 26.11.1: a pure-Python shim that +installs the new package at the same version, warns once at import, and re-exports it. The tests build both +shims with `uv build` (no upload), and check the wheel, the dependency pin, the warning, and attribute and +submodule access against a stub `arcadedb` module: the real package is not needed. +""" + +from __future__ import annotations + +import importlib.util +import json +import re +import subprocess # nosec B404 - test-controlled child processes, no untrusted input +import sys +import textwrap +import zipfile +from pathlib import Path + +import pytest + +BINDINGS = Path(__file__).resolve().parents[1] +DEPRECATION = BINDINGS / "deprecation" +VERSION = "26.11.1" +# old distribution -> the package it points to +SHIMS = {"arcadedb-embedded": "arcadedb", "arcadedb-embedded-native": "arcadedb-native"} +# What PyPI listed before the cutover (checked 2026-10-25): the counting rule is applied against this +# snapshot, because once the shims are published the live list contains 26.11.1 itself. +PUBLISHED_BEFORE_CUTOVER = { + "arcadedb-embedded": ["26.9.1", "26.10.1", "26.10.2", "26.10.2.post1"], + "arcadedb-embedded-native": ["26.10.2.dev0", "26.10.2.dev1"], +} +BODY_MARKER = "# ---- shared body" + +STUB_FILES = { + "arcadedb/__init__.py": """ + __version__ = "26.11.1" + __all__ = ["Database", "open_database"] + + class Database: + pass + + def open_database(path): + return Database() + + _private = "p" + from . import core # the real package imports its submodules like this + """, + "arcadedb/core.py": "class Core:\n pass\n\nVALUE = 1\n", + # not imported by __init__: only a lazy import reaches it + "arcadedb/extra.py": "VALUE = 42\n", + "arcadedb/sub/__init__.py": "", + "arcadedb/sub/deep.py": "NAME = 'deep'\n", +} + +PROBE = """ +import json, sys, warnings +sys.path.insert(0, sys.argv[1]) +with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + import arcadedb_embedded + import arcadedb_embedded.core + import arcadedb_embedded.extra + import arcadedb_embedded.sub.deep + from arcadedb_embedded.sub import deep + from arcadedb_embedded.extra import VALUE +import arcadedb +out = { + "warnings": [[w.category.__name__, str(w.message), w.filename] for w in caught], + "same_class": arcadedb_embedded.Database is arcadedb.Database, + "same_function": arcadedb_embedded.open_database is arcadedb.open_database, + "version": arcadedb_embedded.__version__, + "all": sorted(arcadedb_embedded.__all__), + "private": arcadedb_embedded._private, + "core_alias": arcadedb_embedded.core is arcadedb.core, + "core_in_modules": sys.modules["arcadedb_embedded.core"] is arcadedb.core, + "lazy_alias": arcadedb_embedded.extra is arcadedb.extra, + "lazy_value": VALUE, + "nested_alias": arcadedb_embedded.sub.deep is arcadedb.sub.deep and deep is arcadedb.sub.deep, + "no_second_copy": sorted(n for n in sys.modules if n.startswith("arcadedb_embedded.") + and sys.modules[n].__name__.startswith("arcadedb_embedded.")), + "spec_kept": arcadedb.extra.__spec__.name, + "in_dir": all(n in dir(arcadedb_embedded) for n in ("Database", "core", "_private")), +} +try: + arcadedb_embedded.missing_name +except AttributeError as err: + out["missing_attr"] = str(err) +try: + import arcadedb_embedded.no_such_module +except ModuleNotFoundError: + out["missing_module"] = True +print(json.dumps(out)) +""" + + +def _load_build_shims(): + spec = importlib.util.spec_from_file_location( + "build_shims", DEPRECATION / "build_shims.py" + ) + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + sys.modules["build_shims"] = module + spec.loader.exec_module(module) + return module + + +def _load_check_version(): + spec = importlib.util.spec_from_file_location( + "check_version", BINDINGS / "scripts" / "check_version.py" + ) + module = importlib.util.module_from_spec(spec) + assert spec.loader is not None + sys.modules["check_version"] = module + spec.loader.exec_module(module) + return module + + +@pytest.fixture(scope="module") +def wheels(tmp_path_factory): + """Build both shims once (`uv build`, no upload) and return {old distribution: wheel path}.""" + out = tmp_path_factory.mktemp("shim-dist") + assert _load_build_shims().main(["--out", str(out)]) == 0 + built = {} + for dist in SHIMS: + found = list(out.glob(f"{dist.replace('-', '_')}-{VERSION}-*.whl")) + assert len(found) == 1, f"{dist}: expected one wheel in {out}, found {found}" + built[dist] = found[0] + return built + + +@pytest.fixture(scope="module") +def sites(wheels, tmp_path_factory): + """Per shim, a site directory holding the unpacked wheel next to the stub `arcadedb`.""" + result = {} + for dist, wheel in wheels.items(): + site = tmp_path_factory.mktemp(f"site-{dist}") + with zipfile.ZipFile(wheel) as archive: + archive.extractall(site) + for name, body in STUB_FILES.items(): + target = site / name + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(textwrap.dedent(body).lstrip("\n"), encoding="utf-8") + result[dist] = site + return result + + +def _metadata(wheel: Path) -> str: + with zipfile.ZipFile(wheel) as archive: + name = next(n for n in archive.namelist() if n.endswith(".dist-info/METADATA")) + return archive.read(name).decode("utf-8") + + +def _probe(site: Path) -> dict: + done = subprocess.run( # nosec B603 - fixed argument list, no shell + [sys.executable, "-I", "-c", PROBE, str(site)], + capture_output=True, + text=True, + timeout=120, + check=False, + ) + assert done.returncode == 0, done.stderr + return json.loads(done.stdout.strip().splitlines()[-1]) + + +@pytest.mark.parametrize("dist", SHIMS) +def test_wheel_is_pure_python(dist, wheels): + wheel = wheels[dist] + assert wheel.name == f"{dist.replace('-', '_')}-{VERSION}-py3-none-any.whl" + with zipfile.ZipFile(wheel) as archive: + names = archive.namelist() + wheel_file = archive.read( + next(n for n in names if n.endswith(".dist-info/WHEEL")) + ).decode("utf-8") + assert "Root-Is-Purelib: true" in wheel_file + assert "Tag: py3-none-any" in wheel_file + code = [ + n + for n in names + if not n.startswith(f"{dist.replace('-', '_')}-{VERSION}.dist-info/") + ] + assert code == ["arcadedb_embedded/__init__.py"], code + assert any(n.endswith("licenses/LICENSE") for n in names) + + +@pytest.mark.parametrize("dist", SHIMS) +def test_dependency_pins_the_new_package_at_the_same_version(dist, wheels): + metadata = _metadata(wheels[dist]) + new = SHIMS[dist] + assert f"Name: {dist}\n" in metadata + assert f"Version: {VERSION}\n" in metadata + requires = re.findall(r"^Requires-Dist: (.+)$", metadata, flags=re.MULTILINE) + assert f"{new}=={VERSION}" in requires + # every requirement is an exact pin on the new package, extras forwarded, nothing else + assert all( + re.fullmatch( + rf"{re.escape(new)}(\[\w+\])?=={re.escape(VERSION)}(; extra == '\w+')?", r + ) + for r in requires + ), requires + + +@pytest.mark.parametrize("dist", SHIMS) +def test_version_follows_the_counting_rule(dist): + cv = _load_check_version() + assert str(cv.check_next(VERSION, PUBLISHED_BEFORE_CUTOVER[dist])) == VERSION + with pytest.raises(cv.VersionError): + cv.check_next(VERSION, PUBLISHED_BEFORE_CUTOVER[dist] + [VERSION]) + # the build script reads the same version from pyproject.toml, and the rule's script accepts it + build_shims = _load_build_shims() + assert build_shims.shim_version(dist) == VERSION + build_shims.check_version(dist, check_pypi=False) + + +def test_both_shims_share_one_body(): + bodies = [] + for dist in SHIMS: + text = ( + DEPRECATION / dist / "src" / "arcadedb_embedded" / "__init__.py" + ).read_text(encoding="utf-8") + assert BODY_MARKER in text + bodies.append(text[text.index(BODY_MARKER) :]) + assert bodies[0] == bodies[1] + + +@pytest.mark.parametrize("dist", SHIMS) +def test_warning_fires_once_and_is_visible_by_default(dist, sites): + result = _probe(sites[dist]) + assert len(result["warnings"]) == 1, result["warnings"] + category, message, filename = result["warnings"][0] + assert category == "FutureWarning" + assert filename == "" # attributed to the importing code, not to the shim + new = SHIMS[dist] + assert f"pip install {new}" in message + assert "import arcadedb" in message + assert "https://github.com/ArcadeData/arcadedb-python" in message + assert "https://docs.arcadedb.com/arcadedb-python/" in message + + # with Python's default filters, with no -W option: shown once on stderr + shown = subprocess.run( # nosec B603 - fixed argument list, no shell + [ + sys.executable, + "-I", + "-c", + "import sys; sys.path.insert(0, sys.argv[1]); import arcadedb_embedded; import arcadedb_embedded.core", + str(sites[dist]), + ], + capture_output=True, + text=True, + timeout=120, + check=False, + ) + assert shown.returncode == 0, shown.stderr + assert shown.stderr.count("FutureWarning") == 1, shown.stderr + + +@pytest.mark.parametrize("dist", SHIMS) +def test_attributes_and_submodules_come_from_the_new_module(dist, sites): + result = _probe(sites[dist]) + assert result["same_class"] and result["same_function"] + assert result["version"] == VERSION + assert result["all"] == ["Database", "open_database"] + assert result["private"] == "p" + assert result["in_dir"] + # a submodule the new package already imported, one it has not, and a nested one: the same + # module objects as under `arcadedb`, never a second copy loaded from disk + assert result["core_alias"] and result["core_in_modules"] + assert result["lazy_alias"] and result["lazy_value"] == 42 + assert result["nested_alias"] + assert result["no_second_copy"] == [] + assert result["spec_kept"] == "arcadedb.extra" + assert "no attribute 'missing_name'" in result["missing_attr"] + assert result["missing_module"] is True + + +def test_the_real_package_has_no_warning(): + """The benchmark harness imports `arcadedb_embedded` from the real package until the split: it must stay quiet.""" + source = (BINDINGS / "src" / "arcadedb_embedded" / "__init__.py").read_text( + encoding="utf-8" + ) + assert "warnings" not in source and "FutureWarning" not in source From 49ac34840a8b3d6f1947a09c46da640e12b92d69 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 01:47:16 +0900 Subject: [PATCH 2/8] CI triggers cover bindings/python/deprecation (the trigger-coverage lint) Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test-python-bindings.yml | 2 ++ .github/workflows/test-python-examples.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/test-python-bindings.yml b/.github/workflows/test-python-bindings.yml index 1fe7fc0d239..77b2e2443c7 100644 --- a/.github/workflows/test-python-bindings.yml +++ b/.github/workflows/test-python-bindings.yml @@ -13,6 +13,7 @@ on: paths: - 'bindings/python/src/**' - 'bindings/python/tests/**' + - 'bindings/python/deprecation/**' - 'bindings/python/examples/**' - 'bindings/python/scripts/**' - 'bindings/python/pyproject.toml' @@ -25,6 +26,7 @@ on: paths: - 'bindings/python/src/**' - 'bindings/python/tests/**' + - 'bindings/python/deprecation/**' - 'bindings/python/examples/**' - 'bindings/python/scripts/**' - 'bindings/python/pyproject.toml' diff --git a/.github/workflows/test-python-examples.yml b/.github/workflows/test-python-examples.yml index 9e6b7d83801..ed158051c89 100644 --- a/.github/workflows/test-python-examples.yml +++ b/.github/workflows/test-python-examples.yml @@ -12,6 +12,7 @@ on: paths: - 'bindings/python/src/**' - 'bindings/python/tests/**' + - 'bindings/python/deprecation/**' - 'bindings/python/examples/**' - 'bindings/python/scripts/**' - 'bindings/python/pyproject.toml' @@ -24,6 +25,7 @@ on: paths: - 'bindings/python/src/**' - 'bindings/python/tests/**' + - 'bindings/python/deprecation/**' - 'bindings/python/examples/**' - 'bindings/python/scripts/**' - 'bindings/python/pyproject.toml' From da2e6cfee629b8a2cf11c9b2195106867733e328 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 02:17:24 +0900 Subject: [PATCH 3/8] build_shims: find uv through UV when it is not on PATH (Windows test env) Co-Authored-By: Claude Opus 5.5 --- bindings/python/deprecation/build_shims.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bindings/python/deprecation/build_shims.py b/bindings/python/deprecation/build_shims.py index b49e7fbd5cc..78042d7eb02 100644 --- a/bindings/python/deprecation/build_shims.py +++ b/bindings/python/deprecation/build_shims.py @@ -22,6 +22,7 @@ import argparse import importlib.util +import os import shutil import subprocess # nosec B404 - runs `uv build` with a fixed argument list import sys @@ -63,8 +64,9 @@ def check_version(shim: str, check_pypi: bool) -> None: def build(shim: str, out_dir: Path) -> list[Path]: """Build one shim into out_dir and return the files it produced.""" - uv = shutil.which("uv") - if uv is None: + # `uv run` exports its own path as UV; a uv-made test environment (Windows CI) may not have uv on PATH. + uv = os.environ.get("UV") or shutil.which("uv") + if not uv: raise SystemExit("uv is required (https://docs.astral.sh/uv/)") out_dir.mkdir(parents=True, exist_ok=True) before = set(out_dir.iterdir()) From cffc522175e16f169efcc9ad0e8a667dc9eb62f7 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 16:19:13 +0900 Subject: [PATCH 4/8] Native shim: ship arcadedb_native, the import name of the old arcadedb-embedded-native package (finding F1) The old arcadedb-embedded-native wheel contains only the package arcadedb_native (checked on 26.10.2.dev1), but the shim shipped only arcadedb_embedded. Someone who upgraded and ran `import arcadedb_native` got a bare ModuleNotFoundError, with no warning and no pointer to arcadedb-native. - arcadedb-embedded-native now ships arcadedb_native next to arcadedb_embedded (kept for anyone who aliased it): the same shared body, so one FutureWarning, then the arcadedb module with submodules. - The warning names the module that was imported: "write `import arcadedb` where you wrote `import arcadedb_native`" (it was hard-coded to arcadedb_embedded). The text for arcadedb_embedded is unchanged. - The alias finder is now keyed on the pair (new module, old name), so importing both old names in one process aliases the submodules of each; before, the second name got none. - Tests: the shared-body check covers all three files; the wheel contents, warning, and attribute and submodule tests run per (shim, module); a new test imports both old names in one process. The new tests fail on the old shims with ModuleNotFoundError: No module named 'arcadedb_native'. - README, MIGRATION.md and deprecation/README.md name arcadedb_native. Co-Authored-By: Claude Sonnet 5.5 --- bindings/python/deprecation/MIGRATION.md | 5 +- bindings/python/deprecation/README.md | 4 +- .../arcadedb-embedded-native/README.md | 6 +- .../arcadedb-embedded-native/pyproject.toml | 2 +- .../src/arcadedb_embedded/__init__.py | 8 +- .../src/arcadedb_native/__init__.py | 99 +++++++++++++++++++ .../src/arcadedb_embedded/__init__.py | 8 +- .../python/tests/test_deprecation_shims.py | 97 ++++++++++++++---- 8 files changed, 199 insertions(+), 30 deletions(-) create mode 100644 bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py diff --git a/bindings/python/deprecation/MIGRATION.md b/bindings/python/deprecation/MIGRATION.md index 58e1ae3df61..7d0ea2c409c 100644 --- a/bindings/python/deprecation/MIGRATION.md +++ b/bindings/python/deprecation/MIGRATION.md @@ -11,8 +11,9 @@ The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [A | `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb[arrow,pandas]'` | | `uv add arcadedb-embedded` | `uv add arcadedb` | | `import arcadedb_embedded` | `import arcadedb` | +| `import arcadedb_native` (from `arcadedb-embedded-native`) | `import arcadedb` | | `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | -| `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... | `arcadedb.core`, `arcadedb.jvm`, ... | +| `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... (and `arcadedb_native.core`, ...) | `arcadedb.core`, `arcadedb.jvm`, ... | Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-native`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded`. @@ -25,7 +26,7 @@ From 26.11.1 the package version is the ArcadeDB engine version, as before. | `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb` 26.11.1 and later | | `arcadedb-embedded-native` (preview) | 26.10.2.dev1, then the shim 26.11.1 | `arcadedb-native` 26.11.1 and later | -The 26.11.1 releases of the old names are shims and the last ones. Each installs the new package at exactly 26.11.1; importing `arcadedb_embedded` then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. A shim never upgrades: for newer versions install `arcadedb` or `arcadedb-native`. Older releases of the old names stay on PyPI for pinned installs. +The 26.11.1 releases of the old names are shims and the last ones. Each installs the new package at exactly 26.11.1; importing `arcadedb_embedded` (or `arcadedb_native`, for the native shim) then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. A shim never upgrades: for newer versions install `arcadedb` or `arcadedb-native`. Older releases of the old names stay on PyPI for pinned installs. ## What stays the same diff --git a/bindings/python/deprecation/README.md b/bindings/python/deprecation/README.md index 49769b13033..426f58e41e8 100644 --- a/bindings/python/deprecation/README.md +++ b/bindings/python/deprecation/README.md @@ -7,7 +7,7 @@ The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/Arca | `arcadedb-embedded/` | `arcadedb-embedded` | `arcadedb==26.11.1` | 26.11.1 | | `arcadedb-embedded-native/` | `arcadedb-embedded-native` | `arcadedb-native==26.11.1` | 26.11.1 | -The two `arcadedb_embedded/__init__.py` files differ only in their two constants; `tests/test_deprecation_shims.py` checks that the rest is identical. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. +The shim modules are `arcadedb_embedded` (in both shims) and `arcadedb_native` (in `arcadedb-embedded-native` only: the old native package imported as `arcadedb_native`). The three `__init__.py` files differ only in their docstring and two constants; `tests/test_deprecation_shims.py` checks that the rest is identical. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. ## Build and test (never uploads) @@ -24,7 +24,7 @@ taskset -c 0-3 uv run pytest bindings/python/tests/test_deprecation_shims.py # 2. Never push a `26.11.1` tag in this repository: `release-python-packages.yml` would build and publish the real wheels under `arcadedb-embedded`. 3. Build and check: `python3 bindings/python/deprecation/build_shims.py --out dist --check-pypi && uvx twine check dist/*`. 4. Publish both shims. Trusted publishing works only from a workflow, so either (a) register a trusted publisher on both PyPI projects (repository `humemai/arcadedb-embedded-python`, workflow `publish-deprecation-shims.yml`, environment `pypi`), add the workflow below to `.github/workflows/` (and to `FORK_GITHUB_ALLOWLIST` in `sync-upstream.sh` if another sync will still run), and dispatch it with `publish` on; or (b) upload from a laptop with `uvx twine upload dist/*` and a project-scoped API token. -5. In a clean environment: `pip install arcadedb-embedded==26.11.1`, `python -W default -c "import arcadedb_embedded"` shows one FutureWarning; the same for `arcadedb-embedded-native`. +5. In a clean environment: `pip install arcadedb-embedded==26.11.1`, `python -W default -c "import arcadedb_embedded"` shows one FutureWarning; for `arcadedb-embedded-native`, `python -W default -c "import arcadedb_native"` shows one FutureWarning too. ```yaml name: Publish deprecation shims diff --git a/bindings/python/deprecation/arcadedb-embedded-native/README.md b/bindings/python/deprecation/arcadedb-embedded-native/README.md index dcb19fedcc6..6c3d8dad871 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/README.md +++ b/bindings/python/deprecation/arcadedb-embedded-native/README.md @@ -5,7 +5,7 @@ | | Old | New | |---|---|---| | PyPI package | `arcadedb-embedded-native` | [`arcadedb-native`](https://pypi.org/project/arcadedb-native/) | -| Import | `import arcadedb_embedded` | `import arcadedb` | +| Import | `import arcadedb_native` | `import arcadedb` | | Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | | Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | @@ -16,13 +16,13 @@ pip uninstall arcadedb-embedded-native pip install arcadedb-native ``` -Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). The new package is the same native-image bindings, published under the new name. `arcadedb-native` 26.11.1 has the same version number as this release. +Then change `import arcadedb_native` to `import arcadedb` (and `from arcadedb_native import X` to `from arcadedb import X`). The new package is the same native-image bindings, published under the new name. `arcadedb-native` 26.11.1 has the same version number as this release. `arcadedb-native` and `arcadedb` both provide the module `arcadedb`: install one of them in an environment, not both. ## What this release does -It is a shim. It installs `arcadedb-native==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb-native==26.11.1` and gets no updates: to receive newer versions, switch to `arcadedb-native`. +It is a shim. It installs `arcadedb-native==26.11.1`, and `import arcadedb_native` (the import name of the old package; `import arcadedb_embedded` works too) emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb-native==26.11.1` and gets no updates: to receive newer versions, switch to `arcadedb-native`. The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb-native`. diff --git a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml index f5a8885d1a1..0470831d457 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml +++ b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml @@ -41,7 +41,7 @@ Issues = "https://github.com/ArcadeData/arcadedb-python/issues" "Replaced by" = "https://pypi.org/project/arcadedb-native/" [tool.hatch.build.targets.wheel] -packages = ["src/arcadedb_embedded"] +packages = ["src/arcadedb_embedded", "src/arcadedb_native"] [tool.hatch.build.targets.sdist] include = ["src", "README.md", "LICENSE", "pyproject.toml"] diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py index b4446b64b08..d5f96a74031 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py @@ -21,7 +21,7 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " - "`import arcadedb` where you wrote `import arcadedb_embedded`. " + f"`import arcadedb` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" ) @@ -59,6 +59,7 @@ class _AliasFinder(importlib.abc.MetaPathFinder): """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" alias_of = _NEW + old = _OLD def find_spec(self, fullname, path=None, target=None): if not fullname.startswith(_OLD + "."): @@ -74,7 +75,10 @@ def find_spec(self, fullname, path=None, target=None): # first in line, so a package alias is not also found on disk and loaded a second time -if not any(getattr(f, "alias_of", None) == _NEW for f in sys.meta_path): +if not any( + getattr(f, "alias_of", None) == _NEW and getattr(f, "old", None) == _OLD + for f in sys.meta_path +): sys.meta_path.insert(0, _AliasFinder()) # submodules the new package has already imported answer to the old name right away diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py new file mode 100644 index 00000000000..d5f96a74031 --- /dev/null +++ b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py @@ -0,0 +1,99 @@ +# Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) +# SPDX-License-Identifier: Apache-2.0 +"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb-native``. + +This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at +import, then hands every name and submodule over to the ``arcadedb`` module. +""" + +# ---- per-shim constants: the two shims differ only in this block ---- +_OLD_DIST = "arcadedb-embedded-native" +_NEW_DIST = "arcadedb-native" +# ---- shared body: identical in both shims (tests/test_deprecation_shims.py checks it) ---- + +import importlib +import importlib.abc +import importlib.machinery +import importlib.util +import sys +import warnings + +_MESSAGE = ( + f"The {_OLD_DIST!r} package has moved and this is its last release. " + f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " + f"`import arcadedb` where you wrote `import {__name__}`. " + "New home: https://github.com/ArcadeData/arcadedb-python, " + "docs: https://docs.arcadedb.com/arcadedb-python/" +) +# FutureWarning, not DeprecationWarning: Python shows it to end users by default. +warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) + +import arcadedb as _new # noqa: E402 +from arcadedb import * # noqa: E402,F401,F403 + +_OLD = __name__ +_NEW = _new.__name__ +__all__ = list( + getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] +) + + +class _AliasLoader(importlib.abc.Loader): + """Hand out the real ``arcadedb`` submodule under the old name, not a second copy.""" + + def __init__(self, target): + self._target = target + self._spec = None + + def create_module(self, spec): + module = importlib.import_module(self._target) + self._spec = module.__spec__ + return module + + def exec_module(self, module): + # the import machinery stamped the alias spec on the shared module: put its own back + module.__spec__ = self._spec + + +class _AliasFinder(importlib.abc.MetaPathFinder): + """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" + + alias_of = _NEW + old = _OLD + + def find_spec(self, fullname, path=None, target=None): + if not fullname.startswith(_OLD + "."): + return None + real = _NEW + fullname[len(_OLD) :] + try: + found = importlib.util.find_spec(real) + except (ImportError, ValueError): + return None + if found is None: + return None + return importlib.machinery.ModuleSpec(fullname, _AliasLoader(real)) + + +# first in line, so a package alias is not also found on disk and loaded a second time +if not any( + getattr(f, "alias_of", None) == _NEW and getattr(f, "old", None) == _OLD + for f in sys.meta_path +): + sys.meta_path.insert(0, _AliasFinder()) + +# submodules the new package has already imported answer to the old name right away +for _name, _module in list(sys.modules.items()): + if _name.startswith(_NEW + ".") and _module is not None: + sys.modules.setdefault(_OLD + _name[len(_NEW) :], _module) + + +def __getattr__(name): + """Anything not star-imported (a private name, a submodule) comes from ``arcadedb``.""" + try: + return getattr(_new, name) + except AttributeError: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None + + +def __dir__(): + return sorted(set(globals()) | set(dir(_new))) diff --git a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py index cd0adc4eeb5..98502a09e34 100644 --- a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py @@ -21,7 +21,7 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " - "`import arcadedb` where you wrote `import arcadedb_embedded`. " + f"`import arcadedb` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" ) @@ -59,6 +59,7 @@ class _AliasFinder(importlib.abc.MetaPathFinder): """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" alias_of = _NEW + old = _OLD def find_spec(self, fullname, path=None, target=None): if not fullname.startswith(_OLD + "."): @@ -74,7 +75,10 @@ def find_spec(self, fullname, path=None, target=None): # first in line, so a package alias is not also found on disk and loaded a second time -if not any(getattr(f, "alias_of", None) == _NEW for f in sys.meta_path): +if not any( + getattr(f, "alias_of", None) == _NEW and getattr(f, "old", None) == _OLD + for f in sys.meta_path +): sys.meta_path.insert(0, _AliasFinder()) # submodules the new package has already imported answer to the old name right away diff --git a/bindings/python/tests/test_deprecation_shims.py b/bindings/python/tests/test_deprecation_shims.py index e44b7a0e66b..b17321ad0d0 100644 --- a/bindings/python/tests/test_deprecation_shims.py +++ b/bindings/python/tests/test_deprecation_shims.py @@ -1,7 +1,8 @@ """The deprecation shims for the old PyPI names (deprecation/): built here, checked against a stub. `arcadedb-embedded` and `arcadedb-embedded-native` get one last release, 26.11.1: a pure-Python shim that -installs the new package at the same version, warns once at import, and re-exports it. The tests build both +installs the new package at the same version, warns once at import, and re-exports it. The old native package +imported as `arcadedb_native`, so its shim ships that module as well as `arcadedb_embedded`. The tests build both shims with `uv build` (no upload), and check the wheel, the dependency pin, the warning, and attribute and submodule access against a stub `arcadedb` module: the real package is not needed. """ @@ -30,6 +31,12 @@ "arcadedb-embedded": ["26.9.1", "26.10.1", "26.10.2", "26.10.2.post1"], "arcadedb-embedded-native": ["26.10.2.dev0", "26.10.2.dev1"], } +# old distribution -> the import names its shim ships (the old native package imported as arcadedb_native) +MODULES = { + "arcadedb-embedded": ["arcadedb_embedded"], + "arcadedb-embedded-native": ["arcadedb_embedded", "arcadedb_native"], +} +CASES = [(dist, module) for dist, modules in MODULES.items() for module in modules] BODY_MARKER = "# ---- shared body" STUB_FILES = { @@ -53,6 +60,7 @@ def open_database(path): "arcadedb/sub/deep.py": "NAME = 'deep'\n", } +# written for arcadedb_embedded; _probe() swaps in the module name under test PROBE = """ import json, sys, warnings sys.path.insert(0, sys.argv[1]) @@ -151,9 +159,15 @@ def _metadata(wheel: Path) -> str: return archive.read(name).decode("utf-8") -def _probe(site: Path) -> dict: +def _probe(site: Path, module: str = "arcadedb_embedded") -> dict: done = subprocess.run( # nosec B603 - fixed argument list, no shell - [sys.executable, "-I", "-c", PROBE, str(site)], + [ + sys.executable, + "-I", + "-c", + PROBE.replace("arcadedb_embedded", module), + str(site), + ], capture_output=True, text=True, timeout=120, @@ -179,7 +193,7 @@ def test_wheel_is_pure_python(dist, wheels): for n in names if not n.startswith(f"{dist.replace('-', '_')}-{VERSION}.dist-info/") ] - assert code == ["arcadedb_embedded/__init__.py"], code + assert sorted(code) == [f"{m}/__init__.py" for m in MODULES[dist]], code assert any(n.endswith("licenses/LICENSE") for n in names) @@ -212,27 +226,38 @@ def test_version_follows_the_counting_rule(dist): build_shims.check_version(dist, check_pypi=False) -def test_both_shims_share_one_body(): +def test_all_shim_modules_share_one_body(): bodies = [] - for dist in SHIMS: - text = ( - DEPRECATION / dist / "src" / "arcadedb_embedded" / "__init__.py" - ).read_text(encoding="utf-8") + for dist, module in CASES: + text = (DEPRECATION / dist / "src" / module / "__init__.py").read_text( + encoding="utf-8" + ) assert BODY_MARKER in text bodies.append(text[text.index(BODY_MARKER) :]) - assert bodies[0] == bodies[1] + assert len(bodies) == 3 + assert all(body == bodies[0] for body in bodies) -@pytest.mark.parametrize("dist", SHIMS) -def test_warning_fires_once_and_is_visible_by_default(dist, sites): - result = _probe(sites[dist]) +def test_the_native_shim_ships_the_old_native_import_name(): + """The old arcadedb-embedded-native package imported as `arcadedb_native`; without this module an upgrader gets a bare ModuleNotFoundError.""" + assert "arcadedb_native" in MODULES["arcadedb-embedded-native"] + assert "arcadedb_native" not in MODULES["arcadedb-embedded"] + shim = DEPRECATION / "arcadedb-embedded-native" + assert (shim / "src" / "arcadedb_native" / "__init__.py").is_file() + pyproject = (shim / "pyproject.toml").read_text(encoding="utf-8") + assert '"src/arcadedb_native"' in pyproject + + +@pytest.mark.parametrize(("dist", "module"), CASES) +def test_warning_fires_once_and_is_visible_by_default(dist, module, sites): + result = _probe(sites[dist], module) assert len(result["warnings"]) == 1, result["warnings"] category, message, filename = result["warnings"][0] assert category == "FutureWarning" assert filename == "" # attributed to the importing code, not to the shim new = SHIMS[dist] assert f"pip install {new}" in message - assert "import arcadedb" in message + assert f"`import arcadedb` where you wrote `import {module}`" in message assert "https://github.com/ArcadeData/arcadedb-python" in message assert "https://docs.arcadedb.com/arcadedb-python/" in message @@ -242,7 +267,7 @@ def test_warning_fires_once_and_is_visible_by_default(dist, sites): sys.executable, "-I", "-c", - "import sys; sys.path.insert(0, sys.argv[1]); import arcadedb_embedded; import arcadedb_embedded.core", + f"import sys; sys.path.insert(0, sys.argv[1]); import {module}; import {module}.core", str(sites[dist]), ], capture_output=True, @@ -254,9 +279,9 @@ def test_warning_fires_once_and_is_visible_by_default(dist, sites): assert shown.stderr.count("FutureWarning") == 1, shown.stderr -@pytest.mark.parametrize("dist", SHIMS) -def test_attributes_and_submodules_come_from_the_new_module(dist, sites): - result = _probe(sites[dist]) +@pytest.mark.parametrize(("dist", "module"), CASES) +def test_attributes_and_submodules_come_from_the_new_module(dist, module, sites): + result = _probe(sites[dist], module) assert result["same_class"] and result["same_function"] assert result["version"] == VERSION assert result["all"] == ["Database", "open_database"] @@ -273,6 +298,42 @@ def test_attributes_and_submodules_come_from_the_new_module(dist, sites): assert result["missing_module"] is True +BOTH = """ +import sys, warnings +sys.path.insert(0, sys.argv[1]) +with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + import arcadedb_embedded + import arcadedb_native +import arcadedb +# a lazily imported submodule must resolve under each old name, not only under the first one imported +import arcadedb_embedded.extra +import arcadedb_native.extra +assert arcadedb_embedded.extra is arcadedb.extra, "arcadedb_embedded.extra" +assert arcadedb_native.extra is arcadedb.extra, "arcadedb_native.extra" +from arcadedb_native.sub import deep +assert deep is arcadedb.sub.deep +assert [w.category.__name__ for w in caught] == ["FutureWarning", "FutureWarning"], caught +assert [str(w.message).rsplit("`", 2)[-2] for w in caught] == [ + "import arcadedb_embedded", + "import arcadedb_native", +] +print("ok") +""" + + +def test_the_native_shim_serves_both_old_names_in_one_process(sites): + done = subprocess.run( # nosec B603 - fixed argument list, no shell + [sys.executable, "-I", "-c", BOTH, str(sites["arcadedb-embedded-native"])], + capture_output=True, + text=True, + timeout=120, + check=False, + ) + assert done.returncode == 0, done.stderr + assert done.stdout.strip() == "ok" + + def test_the_real_package_has_no_warning(): """The benchmark harness imports `arcadedb_embedded` from the real package until the split: it must stay quiet.""" source = (BINDINGS / "src" / "arcadedb_embedded" / "__init__.py").read_text( From 1d268b297da481e15ff5daffb4cbb57a11d360b9 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 20:27:05 +0900 Subject: [PATCH 5/8] Shims: arcadedb-embedded points to arcadedb-jvm, arcadedb-embedded-native to arcadedb (final package names) The final names are PyPI `arcadedb` = the native package and `arcadedb-jvm` = the JVM package. The shims now follow that, so nobody is moved from the JVM build to the native one by following the old name. - arcadedb-embedded (the old JVM package) depends on arcadedb-jvm==26.11.1 (extras vector, arrow, pandas, polars forwarded to arcadedb-jvm), with "Replaced by" https://pypi.org/project/arcadedb-jvm/. Its warning says `pip install arcadedb-jvm`, that it is the same JVM build (so nothing changes at run time), and that `arcadedb` is the native build. - arcadedb-embedded-native depends on arcadedb==26.11.1 (it was arcadedb-native), with "Replaced by" https://pypi.org/project/arcadedb/. Its warning says `pip install arcadedb`. - build_shims.py: TARGETS is now the one table that says where each old name points, and IMPORT_NAME is the one assumption behind it (both new packages provide the import name `arcadedb`; neither has a real release yet). check_target() runs before every build and fails when a shim's pyproject.toml (name, description, dependencies, extras, Replaced by URL) or its _NEW_DIST and _NEW_MODULE constants disagree with the table. - The shim __init__ files take the module to re-export from a new _NEW_MODULE constant and print a per-shim _NOTE; the shared body is still identical in all three and still checked by the tests. - README of both shims (the PyPI descriptions), MIGRATION.md, deprecation/README.md (cutover steps: arcadedb-jvm and arcadedb must exist at 26.11.1 first; the native shim waits for a final arcadedb), the notices in the root README.md and docs/index.md, release.md and testing.md state the new mapping. - Tests (22, were 16): the JVM shim requires arcadedb-jvm and not arcadedb, the native shim requires arcadedb and not arcadedb-jvm or arcadedb-native; extras, Replaced by URL and PyPI README install lines; the warning text per shim; TARGETS equals the decided mapping; check_target() refuses a shim pointed at the other package or another module name. Co-Authored-By: Claude Sonnet 5.5 --- bindings/python/deprecation/MIGRATION.md | 24 ++-- bindings/python/deprecation/README.md | 12 +- .../arcadedb-embedded-native/README.md | 16 +-- .../arcadedb-embedded-native/pyproject.toml | 14 +- .../src/arcadedb_embedded/__init__.py | 28 ++-- .../src/arcadedb_native/__init__.py | 28 ++-- .../deprecation/arcadedb-embedded/README.md | 16 ++- .../arcadedb-embedded/pyproject.toml | 14 +- .../src/arcadedb_embedded/__init__.py | 28 ++-- bindings/python/deprecation/build_shims.py | 62 +++++++++ bindings/python/docs/development/release.md | 2 +- bindings/python/docs/development/testing.md | 2 +- .../python/tests/test_deprecation_shims.py | 121 ++++++++++++++++-- 13 files changed, 272 insertions(+), 95 deletions(-) diff --git a/bindings/python/deprecation/MIGRATION.md b/bindings/python/deprecation/MIGRATION.md index 7d0ea2c409c..419ddaa4b34 100644 --- a/bindings/python/deprecation/MIGRATION.md +++ b/bindings/python/deprecation/MIGRATION.md @@ -1,21 +1,23 @@ -# Migrating from arcadedb-embedded to arcadedb +# Migrating from arcadedb-embedded and arcadedb-embedded-native -The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python), under new PyPI names. This repository is archived after the move. Documentation: [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/). +The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python), under new PyPI names: `arcadedb-embedded` (the JVM package) becomes `arcadedb-jvm`, and `arcadedb-embedded-native` (the native package) becomes `arcadedb`. This repository is archived after the move. Documentation: [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/). ## Install and import | Before | After | |---|---| -| `pip install arcadedb-embedded` | `pip install arcadedb` | -| `pip install arcadedb-embedded-native` (preview) | `pip install arcadedb-native` | -| `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb[arrow,pandas]'` | -| `uv add arcadedb-embedded` | `uv add arcadedb` | +| `pip install arcadedb-embedded` | `pip install arcadedb-jvm` | +| `pip install arcadedb-embedded-native` (preview) | `pip install arcadedb` | +| `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb-jvm[arrow,pandas]'` | +| `uv add arcadedb-embedded` | `uv add arcadedb-jvm` | | `import arcadedb_embedded` | `import arcadedb` | | `import arcadedb_native` (from `arcadedb-embedded-native`) | `import arcadedb` | | `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | | `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... (and `arcadedb_native.core`, ...) | `arcadedb.core`, `arcadedb.jvm`, ... | -Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-native`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded`. +`arcadedb-jvm` is the same JVM build as `arcadedb-embedded`, so moving to it changes nothing at run time. `pip install arcadedb` gives the native build instead (no JVM), a different runtime: take it on purpose, from the documentation, not because the name looks like the successor of `arcadedb-embedded`. + +Both new packages provide the module `arcadedb`, so an environment holds `arcadedb-jvm` or `arcadedb`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded` (or `arcadedb-embedded-native`). ## Version mapping @@ -23,14 +25,14 @@ From 26.11.1 the package version is the ArcadeDB engine version, as before. | Old | Last release | New | |---|---|---| -| `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb` 26.11.1 and later | -| `arcadedb-embedded-native` (preview) | 26.10.2.dev1, then the shim 26.11.1 | `arcadedb-native` 26.11.1 and later | +| `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb-jvm` 26.11.1 and later | +| `arcadedb-embedded-native` (preview) | 26.10.2.dev1, then the shim 26.11.1 | `arcadedb` 26.11.1 and later | -The 26.11.1 releases of the old names are shims and the last ones. Each installs the new package at exactly 26.11.1; importing `arcadedb_embedded` (or `arcadedb_native`, for the native shim) then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. A shim never upgrades: for newer versions install `arcadedb` or `arcadedb-native`. Older releases of the old names stay on PyPI for pinned installs. +The 26.11.1 releases of the old names are shims and the last ones. The `arcadedb-embedded` shim installs `arcadedb-jvm` at exactly 26.11.1, and the `arcadedb-embedded-native` shim installs `arcadedb` at exactly 26.11.1; importing `arcadedb_embedded` (or `arcadedb_native`, for the native shim) then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. A shim never upgrades: for newer versions install `arcadedb-jvm` or `arcadedb`. Older releases of the old names stay on PyPI for pinned installs. ## What stays the same -- The Python API: the module is renamed, its classes and functions are the ones of the 26.11.1 bindings. +- The Python API: the module is renamed, its classes and functions are the ones of the 26.11.1 bindings. The runtime of each old package stays the same when you follow the table: JVM to `arcadedb-jvm`, native to `arcadedb`. - Python 3.11 to 3.14, and the Apache-2.0 license. - The extras `vector`, `arrow`, `pandas` and `polars`, under the same names. - Where to look: the user guide and API reference move with the code to the new docs site, and the old `docs.humem.ai/arcadedb` pages redirect there. diff --git a/bindings/python/deprecation/README.md b/bindings/python/deprecation/README.md index 426f58e41e8..851bf147bc3 100644 --- a/bindings/python/deprecation/README.md +++ b/bindings/python/deprecation/README.md @@ -1,13 +1,13 @@ # Deprecation shims for the old PyPI names -The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) as `arcadedb` and `arcadedb-native` (import `arcadedb` for both). This folder holds the last release of each old name: a pure-Python shim that depends on the new package at the same version, warns once, and re-exports it. Users: see [MIGRATION.md](MIGRATION.md). +The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build), both importing as `arcadedb`. This folder holds the last release of each old name: a pure-Python shim that depends on its replacement at the same version, warns once, and re-exports it. The old JVM package `arcadedb-embedded` points to `arcadedb-jvm` (the same build, so no silent change at run time) and the old native package `arcadedb-embedded-native` points to `arcadedb`. Users: see [MIGRATION.md](MIGRATION.md). | Folder | PyPI project | Depends on | Version | |---|---|---|---| -| `arcadedb-embedded/` | `arcadedb-embedded` | `arcadedb==26.11.1` | 26.11.1 | -| `arcadedb-embedded-native/` | `arcadedb-embedded-native` | `arcadedb-native==26.11.1` | 26.11.1 | +| `arcadedb-embedded/` | `arcadedb-embedded` (JVM) | `arcadedb-jvm==26.11.1` | 26.11.1 | +| `arcadedb-embedded-native/` | `arcadedb-embedded-native` (native) | `arcadedb==26.11.1` | 26.11.1 | -The shim modules are `arcadedb_embedded` (in both shims) and `arcadedb_native` (in `arcadedb-embedded-native` only: the old native package imported as `arcadedb_native`). The three `__init__.py` files differ only in their docstring and two constants; `tests/test_deprecation_shims.py` checks that the rest is identical. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. +The shim modules are `arcadedb_embedded` (in both shims) and `arcadedb_native` (in `arcadedb-embedded-native` only: the old native package imported as `arcadedb_native`). The three `__init__.py` files differ only in their docstring and the constants `_OLD_DIST`, `_NEW_DIST`, `_NEW_MODULE` and `_NOTE`; `tests/test_deprecation_shims.py` checks that the rest is identical. The mapping above lives in one place, `TARGETS` in `build_shims.py`, together with the one assumption behind it: both `arcadedb` and `arcadedb-jvm` provide the import name `arcadedb` (the layout says so, but neither has a real release yet). `build_shims.py` refuses to build when a shim's `pyproject.toml` (name, description, dependencies, extras, `Replaced by` URL) or its `_NEW_DIST` and `_NEW_MODULE` constants disagree with `TARGETS`. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. ## Build and test (never uploads) @@ -20,11 +20,11 @@ taskset -c 0-3 uv run pytest bindings/python/tests/test_deprecation_shims.py # ## Cutover day -1. Release `arcadedb` 26.11.1 (and `arcadedb-native`) from ArcadeData/arcadedb-python first. Check `pip download --no-deps arcadedb==26.11.1` and `arcadedb-native==26.11.1` work: the shims pin these exact versions. +1. Release `arcadedb-jvm` 26.11.1 and `arcadedb` 26.11.1 (the native build) from ArcadeData/arcadedb-python first. Check `pip download --no-deps arcadedb-jvm==26.11.1` and `arcadedb==26.11.1` work: the shims pin these exact versions. If the first final `arcadedb` is not out (only `26.11.1.devN`), do not publish the native shim: `arcadedb==26.11.1` does not match a dev release, so change its pin and version rule here first. The JVM shim can go out on its own once `arcadedb-jvm==26.11.1` exists. 2. Never push a `26.11.1` tag in this repository: `release-python-packages.yml` would build and publish the real wheels under `arcadedb-embedded`. 3. Build and check: `python3 bindings/python/deprecation/build_shims.py --out dist --check-pypi && uvx twine check dist/*`. 4. Publish both shims. Trusted publishing works only from a workflow, so either (a) register a trusted publisher on both PyPI projects (repository `humemai/arcadedb-embedded-python`, workflow `publish-deprecation-shims.yml`, environment `pypi`), add the workflow below to `.github/workflows/` (and to `FORK_GITHUB_ALLOWLIST` in `sync-upstream.sh` if another sync will still run), and dispatch it with `publish` on; or (b) upload from a laptop with `uvx twine upload dist/*` and a project-scoped API token. -5. In a clean environment: `pip install arcadedb-embedded==26.11.1`, `python -W default -c "import arcadedb_embedded"` shows one FutureWarning; for `arcadedb-embedded-native`, `python -W default -c "import arcadedb_native"` shows one FutureWarning too. +5. In a clean environment: `pip install arcadedb-embedded==26.11.1` installs `arcadedb-jvm` (not `arcadedb`: check `pip list`), and `python -W default -c "import arcadedb_embedded"` shows one FutureWarning; for `arcadedb-embedded-native`, which installs `arcadedb`, `python -W default -c "import arcadedb_native"` shows one FutureWarning too. ```yaml name: Publish deprecation shims diff --git a/bindings/python/deprecation/arcadedb-embedded-native/README.md b/bindings/python/deprecation/arcadedb-embedded-native/README.md index 6c3d8dad871..7c43e91c6cd 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/README.md +++ b/bindings/python/deprecation/arcadedb-embedded-native/README.md @@ -1,10 +1,10 @@ -# arcadedb-embedded-native has moved to arcadedb-native +# arcadedb-embedded-native has moved to arcadedb -**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name: +**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. `arcadedb` is the native build, the same kind of build as `arcadedb-embedded-native` (a GraalVM native image, no JVM): | | Old | New | |---|---|---| -| PyPI package | `arcadedb-embedded-native` | [`arcadedb-native`](https://pypi.org/project/arcadedb-native/) | +| PyPI package | `arcadedb-embedded-native` | [`arcadedb`](https://pypi.org/project/arcadedb/) | | Import | `import arcadedb_native` | `import arcadedb` | | Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | | Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | @@ -13,17 +13,17 @@ ```bash pip uninstall arcadedb-embedded-native -pip install arcadedb-native +pip install arcadedb ``` -Then change `import arcadedb_native` to `import arcadedb` (and `from arcadedb_native import X` to `from arcadedb import X`). The new package is the same native-image bindings, published under the new name. `arcadedb-native` 26.11.1 has the same version number as this release. +Then change `import arcadedb_native` to `import arcadedb` (and `from arcadedb_native import X` to `from arcadedb import X`). The new package is the native-image bindings, published under the new name. `arcadedb` 26.11.1 has the same version number as this release. -`arcadedb-native` and `arcadedb` both provide the module `arcadedb`: install one of them in an environment, not both. +`arcadedb` and `arcadedb-jvm` (the JVM build) both provide the module `arcadedb`: install one of them in an environment, not both. ## What this release does -It is a shim. It installs `arcadedb-native==26.11.1`, and `import arcadedb_native` (the import name of the old package; `import arcadedb_embedded` works too) emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb-native==26.11.1` and gets no updates: to receive newer versions, switch to `arcadedb-native`. +It is a shim. It installs `arcadedb==26.11.1`, and `import arcadedb_native` (the import name of the old package; `import arcadedb_embedded` works too) emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb==26.11.1` and gets no updates: to receive newer versions, switch to `arcadedb`. -The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb-native`. +The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb`. Older `arcadedb-embedded-native` releases (the previews 26.10.2.dev0 and 26.10.2.dev1) stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml index 0470831d457..e77850ba89c 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml +++ b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "arcadedb-embedded-native" version = "26.11.1" -description = "Deprecated: arcadedb-embedded-native has moved to arcadedb-native (import arcadedb). Last release, a thin shim." +description = "Deprecated: arcadedb-embedded-native has moved to arcadedb (import arcadedb). Last release, a thin shim." readme = "README.md" requires-python = ">=3.11" license = "Apache-2.0" @@ -24,21 +24,21 @@ classifiers = [ "Topic :: Database", ] # The shim installs exactly the package it points to, at the same version. -dependencies = ["arcadedb-native==26.11.1"] +dependencies = ["arcadedb==26.11.1"] # The extras people installed from the old name, forwarded to the new one. [project.optional-dependencies] -vector = ["arcadedb-native[vector]==26.11.1"] -arrow = ["arcadedb-native[arrow]==26.11.1"] -pandas = ["arcadedb-native[pandas]==26.11.1"] -polars = ["arcadedb-native[polars]==26.11.1"] +vector = ["arcadedb[vector]==26.11.1"] +arrow = ["arcadedb[arrow]==26.11.1"] +pandas = ["arcadedb[pandas]==26.11.1"] +polars = ["arcadedb[polars]==26.11.1"] [project.urls] Homepage = "https://github.com/ArcadeData/arcadedb-python" Documentation = "https://docs.arcadedb.com/arcadedb-python/" Source = "https://github.com/ArcadeData/arcadedb-python" Issues = "https://github.com/ArcadeData/arcadedb-python/issues" -"Replaced by" = "https://pypi.org/project/arcadedb-native/" +"Replaced by" = "https://pypi.org/project/arcadedb/" [tool.hatch.build.targets.wheel] packages = ["src/arcadedb_embedded", "src/arcadedb_native"] diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py index d5f96a74031..d7b94f6927a 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py @@ -1,15 +1,16 @@ # Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) # SPDX-License-Identifier: Apache-2.0 -"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb-native``. +"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb``. -This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at -import, then hands every name and submodule over to the ``arcadedb`` module. +This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb``, which provides the module ``arcadedb``. """ -# ---- per-shim constants: the two shims differ only in this block ---- +# ---- per-shim constants: the shims differ only in this block ---- _OLD_DIST = "arcadedb-embedded-native" -_NEW_DIST = "arcadedb-native" -# ---- shared body: identical in both shims (tests/test_deprecation_shims.py checks it) ---- +_NEW_DIST = "arcadedb" +_NEW_MODULE = "arcadedb" +_NOTE = "`arcadedb` is the native build, like this package." +# ---- shared body: identical in all shim modules (tests/test_deprecation_shims.py checks it) ---- import importlib import importlib.abc @@ -20,22 +21,23 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " - f"`import arcadedb` where you wrote `import {__name__}`. " + f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`). {_NOTE} " + f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" ) # FutureWarning, not DeprecationWarning: Python shows it to end users by default. warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) -import arcadedb as _new # noqa: E402 -from arcadedb import * # noqa: E402,F401,F403 - -_OLD = __name__ -_NEW = _new.__name__ +_new = importlib.import_module(_NEW_MODULE) __all__ = list( getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] ) +# the same names `from import *` would bind +globals().update({_n: getattr(_new, _n) for _n in __all__}) + +_OLD = __name__ +_NEW = _new.__name__ class _AliasLoader(importlib.abc.Loader): diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py index d5f96a74031..d7b94f6927a 100644 --- a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py @@ -1,15 +1,16 @@ # Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) # SPDX-License-Identifier: Apache-2.0 -"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb-native``. +"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb``. -This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at -import, then hands every name and submodule over to the ``arcadedb`` module. +This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb``, which provides the module ``arcadedb``. """ -# ---- per-shim constants: the two shims differ only in this block ---- +# ---- per-shim constants: the shims differ only in this block ---- _OLD_DIST = "arcadedb-embedded-native" -_NEW_DIST = "arcadedb-native" -# ---- shared body: identical in both shims (tests/test_deprecation_shims.py checks it) ---- +_NEW_DIST = "arcadedb" +_NEW_MODULE = "arcadedb" +_NOTE = "`arcadedb` is the native build, like this package." +# ---- shared body: identical in all shim modules (tests/test_deprecation_shims.py checks it) ---- import importlib import importlib.abc @@ -20,22 +21,23 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " - f"`import arcadedb` where you wrote `import {__name__}`. " + f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`). {_NOTE} " + f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" ) # FutureWarning, not DeprecationWarning: Python shows it to end users by default. warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) -import arcadedb as _new # noqa: E402 -from arcadedb import * # noqa: E402,F401,F403 - -_OLD = __name__ -_NEW = _new.__name__ +_new = importlib.import_module(_NEW_MODULE) __all__ = list( getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] ) +# the same names `from import *` would bind +globals().update({_n: getattr(_new, _n) for _n in __all__}) + +_OLD = __name__ +_NEW = _new.__name__ class _AliasLoader(importlib.abc.Loader): diff --git a/bindings/python/deprecation/arcadedb-embedded/README.md b/bindings/python/deprecation/arcadedb-embedded/README.md index c4c68a2e26c..9f0d587bb76 100644 --- a/bindings/python/deprecation/arcadedb-embedded/README.md +++ b/bindings/python/deprecation/arcadedb-embedded/README.md @@ -1,10 +1,10 @@ -# arcadedb-embedded has moved to arcadedb +# arcadedb-embedded has moved to arcadedb-jvm -**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name: +**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. `arcadedb-jvm` is the same JVM build as `arcadedb-embedded` (JPype and a bundled JRE), so moving to it changes nothing at run time: | | Old | New | |---|---|---| -| PyPI package | `arcadedb-embedded` | [`arcadedb`](https://pypi.org/project/arcadedb/) | +| PyPI package | `arcadedb-embedded` | [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) | | Import | `import arcadedb_embedded` | `import arcadedb` | | Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | | Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | @@ -13,15 +13,17 @@ ```bash pip uninstall arcadedb-embedded -pip install arcadedb +pip install arcadedb-jvm ``` -Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). The new package is the same bindings, published under the new name. `arcadedb` 26.11.1 has the same version number as this release. +Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). The new package is the same bindings and the same JVM build, published under the new name. `arcadedb-jvm` 26.11.1 has the same version number as this release. + +Do not install `arcadedb` instead: it is the native build (no JVM), a different runtime. Choose it on purpose, from the documentation, not by following the old name. `arcadedb` and `arcadedb-jvm` both provide the module `arcadedb`: install one of them in an environment, not both. ## What this release does -It is a shim. It installs `arcadedb==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb==26.11.1` and gets no updates: to receive newer ArcadeDB versions, switch to `arcadedb`. +It is a shim. It installs `arcadedb-jvm==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module of `arcadedb-jvm`, submodules included, so existing code keeps running. It is pinned to `arcadedb-jvm==26.11.1` and gets no updates: to receive newer ArcadeDB versions, switch to `arcadedb-jvm`. -The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb`. +The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb-jvm`. Older `arcadedb-embedded` releases stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml index 3277d9e63ea..279654a2d7f 100644 --- a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml +++ b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "arcadedb-embedded" version = "26.11.1" -description = "Deprecated: arcadedb-embedded has moved to arcadedb (import arcadedb). Last release, a thin shim." +description = "Deprecated: arcadedb-embedded has moved to arcadedb-jvm (import arcadedb). Last release, a thin shim." readme = "README.md" requires-python = ">=3.11" license = "Apache-2.0" @@ -24,21 +24,21 @@ classifiers = [ "Topic :: Database", ] # The shim installs exactly the package it points to, at the same version. -dependencies = ["arcadedb==26.11.1"] +dependencies = ["arcadedb-jvm==26.11.1"] # The extras people installed from the old name, forwarded to the new one. [project.optional-dependencies] -vector = ["arcadedb[vector]==26.11.1"] -arrow = ["arcadedb[arrow]==26.11.1"] -pandas = ["arcadedb[pandas]==26.11.1"] -polars = ["arcadedb[polars]==26.11.1"] +vector = ["arcadedb-jvm[vector]==26.11.1"] +arrow = ["arcadedb-jvm[arrow]==26.11.1"] +pandas = ["arcadedb-jvm[pandas]==26.11.1"] +polars = ["arcadedb-jvm[polars]==26.11.1"] [project.urls] Homepage = "https://github.com/ArcadeData/arcadedb-python" Documentation = "https://docs.arcadedb.com/arcadedb-python/" Source = "https://github.com/ArcadeData/arcadedb-python" Issues = "https://github.com/ArcadeData/arcadedb-python/issues" -"Replaced by" = "https://pypi.org/project/arcadedb/" +"Replaced by" = "https://pypi.org/project/arcadedb-jvm/" [tool.hatch.build.targets.wheel] packages = ["src/arcadedb_embedded"] diff --git a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py index 98502a09e34..0200b6146d6 100644 --- a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py @@ -1,15 +1,16 @@ # Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) # SPDX-License-Identifier: Apache-2.0 -"""Deprecated: ``arcadedb-embedded`` has moved to ``arcadedb``. +"""Deprecated: ``arcadedb-embedded`` has moved to ``arcadedb-jvm``. -This is the last release of ``arcadedb-embedded``. It is a thin shim: it warns once at -import, then hands every name and submodule over to the ``arcadedb`` module. +This is the last release of ``arcadedb-embedded``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb-jvm``, which provides the module ``arcadedb``. """ -# ---- per-shim constants: the two shims differ only in this block ---- +# ---- per-shim constants: the shims differ only in this block ---- _OLD_DIST = "arcadedb-embedded" -_NEW_DIST = "arcadedb" -# ---- shared body: identical in both shims (tests/test_deprecation_shims.py checks it) ---- +_NEW_DIST = "arcadedb-jvm" +_NEW_MODULE = "arcadedb" +_NOTE = "`arcadedb-jvm` is the same JVM build as this package, so nothing changes at run time (`arcadedb` is the native build, a different runtime)." +# ---- shared body: identical in all shim modules (tests/test_deprecation_shims.py checks it) ---- import importlib import importlib.abc @@ -20,22 +21,23 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`) and write " - f"`import arcadedb` where you wrote `import {__name__}`. " + f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`). {_NOTE} " + f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" ) # FutureWarning, not DeprecationWarning: Python shows it to end users by default. warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) -import arcadedb as _new # noqa: E402 -from arcadedb import * # noqa: E402,F401,F403 - -_OLD = __name__ -_NEW = _new.__name__ +_new = importlib.import_module(_NEW_MODULE) __all__ = list( getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] ) +# the same names `from import *` would bind +globals().update({_n: getattr(_new, _n) for _n in __all__}) + +_OLD = __name__ +_NEW = _new.__name__ class _AliasLoader(importlib.abc.Loader): diff --git a/bindings/python/deprecation/build_shims.py b/bindings/python/deprecation/build_shims.py index 78042d7eb02..9704d96242e 100644 --- a/bindings/python/deprecation/build_shims.py +++ b/bindings/python/deprecation/build_shims.py @@ -5,6 +5,8 @@ This builds only. It never uploads: publishing is a separate, deliberate step (README.md here). +Where each old name points is set in ``TARGETS`` below, and nowhere else. Before it builds, this script checks that each shim's ``pyproject.toml`` and ``__init__.py`` files agree with ``TARGETS``, so a changed target cannot ship half-changed. + Each shim is copied to a scratch directory first, together with the repository's LICENSE, so the license file ships in the wheel and the sdist and the source tree stays clean. The version in the shim's pyproject.toml must pass ``scripts/check_version.py`` (syntax and counting rule). @@ -29,12 +31,34 @@ import tempfile import tomllib from pathlib import Path +from typing import NamedTuple HERE = Path(__file__).resolve().parent REPO_ROOT = HERE.parents[2] SHIMS = ("arcadedb-embedded", "arcadedb-embedded-native") +class Target(NamedTuple): + """The package an old name points to.""" + + dist: str # the PyPI distribution the shim depends on and tells people to install + module: str # the import name that distribution provides: what the shim re-exports + + +# Where each old name points (the packaging layout decided on 2026-10-25, "layout v2" in ArcadeData/arcadedb-python .notes/MIGRATION.md): +# arcadedb-embedded (the JVM package) -> arcadedb-jvm, the same JVM build, so there is no silent change at run time +# arcadedb-embedded-native (the native package) -> arcadedb, the native build +# ASSUMPTION: both new distributions provide the import name `arcadedb`. The layout says "import arcadedb for all" and that `arcadedb` and `arcadedb-jvm` refuse to import side by side because they share that module. Neither has a real release on PyPI yet (arcadedb 0.0.1 and arcadedb-jvm 0.0.1 are placeholders), so this cannot be checked here. +# If a package gets another import name, change it here and in the `_NEW_MODULE` constant of that shim's `__init__.py` files; check_target() fails until both agree. +IMPORT_NAME = "arcadedb" +TARGETS = { + "arcadedb-embedded": Target(dist="arcadedb-jvm", module=IMPORT_NAME), + "arcadedb-embedded-native": Target(dist="arcadedb", module=IMPORT_NAME), +} +# the extras people installed from the old names, forwarded to the same extras of the target +EXTRAS = ("vector", "arrow", "pandas", "polars") + + def _load_check_version(): path = HERE.parent / "scripts" / "check_version.py" spec = importlib.util.spec_from_file_location("check_version", path) @@ -51,6 +75,43 @@ def shim_version(shim: str) -> str: return tomllib.load(handle)["project"]["version"] +def check_target(shim: str) -> None: + """Fail unless the shim's metadata and import constants all point at TARGETS[shim].""" + target = TARGETS[shim] + version = shim_version(shim) + with open(HERE / shim / "pyproject.toml", "rb") as handle: + project = tomllib.load(handle)["project"] + expected = { + "name": shim, + "description": f"Deprecated: {shim} has moved to {target.dist} (import {target.module}). Last release, a thin shim.", + "dependencies": [f"{target.dist}=={version}"], + "optional-dependencies": { + extra: [f"{target.dist}[{extra}]=={version}"] for extra in EXTRAS + }, + } + problems = [ + f"project.{key} is {project.get(key)!r}, expected {want!r}" + for key, want in expected.items() + if project.get(key) != want + ] + replaced_by = project["urls"].get("Replaced by") + if replaced_by != f"https://pypi.org/project/{target.dist}/": + problems.append(f"project.urls['Replaced by'] is {replaced_by!r}") + for path in sorted((HERE / shim / "src").glob("*/__init__.py")): + text = path.read_text(encoding="utf-8") + for name, want in ( + ("_OLD_DIST", shim), + ("_NEW_DIST", target.dist), + ("_NEW_MODULE", target.module), + ): + if f'{name} = "{want}"\n' not in text: + problems.append(f'{path.relative_to(HERE)}: {name} is not "{want}"') + if problems: + raise SystemExit( + f"{shim} does not point at {target.dist}:\n " + "\n ".join(problems) + ) + + def check_version(shim: str, check_pypi: bool) -> None: """Fail unless the shim's version follows the counting rule (and is the next one on PyPI).""" cv = _load_check_version() @@ -92,6 +153,7 @@ def main(argv: list[str] | None = None) -> int: shims = args.shim or list(SHIMS) for shim in shims: + check_target(shim) check_version(shim, args.check_pypi) for shim in shims: for artifact in build(shim, args.out): diff --git a/bindings/python/docs/development/release.md b/bindings/python/docs/development/release.md index 12970aba6b4..1be65effdc0 100644 --- a/bindings/python/docs/development/release.md +++ b/bindings/python/docs/development/release.md @@ -383,7 +383,7 @@ uv run --project .. --group docs mike alias --update-aliases \ ## The Last Releases: Deprecation Shims -The bindings move to ArcadeData/arcadedb-python as `arcadedb` and `arcadedb-native`. The old names end with one shim release each, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that depends on the new package at the same version and warns once). They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. +The bindings move to ArcadeData/arcadedb-python as `arcadedb-jvm` (the JVM build) and `arcadedb` (the native build). The old names end with one shim release each, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that warns once and depends on its replacement at the same version: `arcadedb-embedded` on `arcadedb-jvm`, `arcadedb-embedded-native` on `arcadedb`). They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. ## Troubleshooting diff --git a/bindings/python/docs/development/testing.md b/bindings/python/docs/development/testing.md index c90e0672671..07fb1bbfc1a 100644 --- a/bindings/python/docs/development/testing.md +++ b/bindings/python/docs/development/testing.md @@ -143,7 +143,7 @@ asserts, so the list below stops at the file. | `test_wheel_platform_tag.py` | The wheel's manylinux platform tag, and `__version__` equals the installed distribution version | | `test_wheel_size.py` | The wheel size gate: a wheel at or over 100 decimal megabytes fails the build | | `test_check_test_skips.py` | The skip gate described below | -| `test_deprecation_shims.py` | The shims for the old PyPI names (`deprecation/`): both build with `uv build` as pure `py3-none-any` wheels, each pins the new package at the same version, the version follows the counting rule, and against a stub `arcadedb` the import warns once with a `FutureWarning` and names, submodules (also not-yet-imported and nested ones) and private attributes resolve to the new module. Not collected where `deprecation/` is absent (the upstream pull request branch) | +| `test_deprecation_shims.py` | The shims for the old PyPI names (`deprecation/`): both build with `uv build` as pure `py3-none-any` wheels, each pins its replacement at the same version (`arcadedb-embedded` pins `arcadedb-jvm`, `arcadedb-embedded-native` pins `arcadedb`, and `build_shims.py` refuses a shim that points elsewhere), the version follows the counting rule, and against a stub `arcadedb` the import warns once with a `FutureWarning` and names, submodules (also not-yet-imported and nested ones) and private attributes resolve to the new module. Not collected where `deprecation/` is absent (the upstream pull request branch) | ### Docs and open engine findings diff --git a/bindings/python/tests/test_deprecation_shims.py b/bindings/python/tests/test_deprecation_shims.py index b17321ad0d0..0d740dfa120 100644 --- a/bindings/python/tests/test_deprecation_shims.py +++ b/bindings/python/tests/test_deprecation_shims.py @@ -1,10 +1,12 @@ """The deprecation shims for the old PyPI names (deprecation/): built here, checked against a stub. `arcadedb-embedded` and `arcadedb-embedded-native` get one last release, 26.11.1: a pure-Python shim that -installs the new package at the same version, warns once at import, and re-exports it. The old native package -imported as `arcadedb_native`, so its shim ships that module as well as `arcadedb_embedded`. The tests build both -shims with `uv build` (no upload), and check the wheel, the dependency pin, the warning, and attribute and -submodule access against a stub `arcadedb` module: the real package is not needed. +installs its replacement at the same version, warns once at import, and re-exports it. The old JVM package +`arcadedb-embedded` points to `arcadedb-jvm` (the same JVM build), the old native package +`arcadedb-embedded-native` to `arcadedb` (the native build); both replacements import as `arcadedb`. The old +native package imported as `arcadedb_native`, so its shim ships that module as well as `arcadedb_embedded`. The +tests build both shims with `uv build` (no upload), and check the wheel, the dependency pin, the warning, and +attribute and submodule access against a stub `arcadedb` module: the real packages are not needed. """ from __future__ import annotations @@ -23,8 +25,11 @@ BINDINGS = Path(__file__).resolve().parents[1] DEPRECATION = BINDINGS / "deprecation" VERSION = "26.11.1" -# old distribution -> the package it points to -SHIMS = {"arcadedb-embedded": "arcadedb", "arcadedb-embedded-native": "arcadedb-native"} +# old distribution -> the package it points to (written out here on purpose, apart from TARGETS in build_shims.py, +# so a wrong edit of that table fails a test) +SHIMS = {"arcadedb-embedded": "arcadedb-jvm", "arcadedb-embedded-native": "arcadedb"} +# the import name of both replacements (an assumption, see build_shims.IMPORT_NAME) +IMPORT_NAME = "arcadedb" # What PyPI listed before the cutover (checked 2026-10-25): the counting rule is applied against this # snapshot, because once the shims are published the live list contains 26.11.1 itself. PUBLISHED_BEFORE_CUTOVER = { @@ -38,6 +43,7 @@ } CASES = [(dist, module) for dist, modules in MODULES.items() for module in modules] BODY_MARKER = "# ---- shared body" +EXTRAS = ["arrow", "pandas", "polars", "vector"] STUB_FILES = { "arcadedb/__init__.py": """ @@ -159,6 +165,12 @@ def _metadata(wheel: Path) -> str: return archive.read(name).decode("utf-8") +def _required_names(metadata: str) -> set[str]: + """The distribution names in the Requires-Dist lines, without extras, version or marker.""" + requires = re.findall(r"^Requires-Dist: (.+)$", metadata, flags=re.MULTILINE) + return {re.match(r"[A-Za-z0-9][A-Za-z0-9._-]*", r).group(0) for r in requires} + + def _probe(site: Path, module: str = "arcadedb_embedded") -> dict: done = subprocess.run( # nosec B603 - fixed argument list, no shell [ @@ -214,6 +226,94 @@ def test_dependency_pins_the_new_package_at_the_same_version(dist, wheels): ), requires +def test_the_jvm_shim_requires_arcadedb_jvm_and_not_arcadedb(wheels): + """arcadedb-embedded was the JVM package: it must land on the JVM build, never silently on the native `arcadedb`.""" + metadata = _metadata(wheels["arcadedb-embedded"]) + assert f"Requires-Dist: arcadedb-jvm=={VERSION}\n" in metadata + assert _required_names(metadata) == {"arcadedb-jvm"} + assert "arcadedb" not in _required_names(metadata) + assert "arcadedb-embedded-native" not in metadata + # the extras are forwarded to the same extras of arcadedb-jvm + assert ( + sorted(re.findall(r"^Provides-Extra: (.+)$", metadata, flags=re.MULTILINE)) + == EXTRAS + ) + for extra in EXTRAS: + assert ( + f"Requires-Dist: arcadedb-jvm[{extra}]=={VERSION}; extra == '{extra}'\n" + in metadata + ) + assert ( + "Project-URL: Replaced by, https://pypi.org/project/arcadedb-jvm/\n" in metadata + ) + assert "has moved to arcadedb-jvm (import arcadedb)" in metadata + + +def test_the_native_shim_requires_arcadedb_and_not_arcadedb_jvm(wheels): + """arcadedb-embedded-native was the native package: it lands on `arcadedb`, which is the native build.""" + metadata = _metadata(wheels["arcadedb-embedded-native"]) + assert f"Requires-Dist: arcadedb=={VERSION}\n" in metadata + assert _required_names(metadata) == {"arcadedb"} + assert "arcadedb-jvm" not in _required_names(metadata) + assert "arcadedb-native" not in _required_names(metadata) + assert ( + sorted(re.findall(r"^Provides-Extra: (.+)$", metadata, flags=re.MULTILINE)) + == EXTRAS + ) + for extra in EXTRAS: + assert ( + f"Requires-Dist: arcadedb[{extra}]=={VERSION}; extra == '{extra}'\n" + in metadata + ) + assert "Project-URL: Replaced by, https://pypi.org/project/arcadedb/\n" in metadata + assert "has moved to arcadedb (import arcadedb)" in metadata + + +def test_the_pypi_readme_gives_the_right_install_line(wheels): + jvm = _metadata(wheels["arcadedb-embedded"]) + assert "pip install arcadedb-jvm\n" in jvm + assert not re.search(r"pip install arcadedb(?![-\w])", jvm) + assert "pip uninstall arcadedb-embedded\n" in jvm + native = _metadata(wheels["arcadedb-embedded-native"]) + assert "pip install arcadedb\n" in native + assert "pip install arcadedb-jvm" not in native + assert "pip uninstall arcadedb-embedded-native\n" in native + + +def test_build_shims_has_one_table_with_the_decided_mapping(): + build_shims = _load_build_shims() + assert {k: v.dist for k, v in build_shims.TARGETS.items()} == SHIMS + assert {v.module for v in build_shims.TARGETS.values()} == {IMPORT_NAME} + assert sorted(build_shims.EXTRAS) == EXTRAS + for dist in SHIMS: + build_shims.check_target( + dist + ) # the files under deprecation/ agree with the table + + +@pytest.mark.parametrize("dist", SHIMS) +def test_build_shims_refuses_a_shim_that_points_elsewhere(dist, monkeypatch): + """Changing the table without the shim files (or the other way round) must stop the build.""" + build_shims = _load_build_shims() + other = next(d for d in SHIMS.values() if d != SHIMS[dist]) + monkeypatch.setitem( + build_shims.TARGETS, dist, build_shims.Target(dist=other, module=IMPORT_NAME) + ) + with pytest.raises(SystemExit) as refused: + build_shims.check_target(dist) + message = str(refused.value) + assert f"does not point at {other}" in message + assert "project.dependencies" in message and "_NEW_DIST" in message + monkeypatch.setitem( + build_shims.TARGETS, + dist, + build_shims.Target(dist=SHIMS[dist], module="arcadedb_other"), + ) + with pytest.raises(SystemExit) as refused: + build_shims.check_target(dist) + assert "_NEW_MODULE" in str(refused.value) + + @pytest.mark.parametrize("dist", SHIMS) def test_version_follows_the_counting_rule(dist): cv = _load_check_version() @@ -256,8 +356,13 @@ def test_warning_fires_once_and_is_visible_by_default(dist, module, sites): assert category == "FutureWarning" assert filename == "" # attributed to the importing code, not to the shim new = SHIMS[dist] - assert f"pip install {new}" in message - assert f"`import arcadedb` where you wrote `import {module}`" in message + assert f"Install {new!r} instead (`pip install {new}`)" in message + # `pip install arcadedb` must not appear in the JVM shim's text as the command to run (it is the native build) + assert (re.search(r"pip install arcadedb(?![-\w])", message) is not None) == ( + new == "arcadedb" + ) + assert f"`import {IMPORT_NAME}` where you wrote `import {module}`" in message + assert ("same JVM build" in message) == (new == "arcadedb-jvm") assert "https://github.com/ArcadeData/arcadedb-python" in message assert "https://docs.arcadedb.com/arcadedb-python/" in message From 0a531f6d74115511ba6c0ab4a8c080ff17b8d1d3 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 21:08:27 +0900 Subject: [PATCH 6/8] Shims: drop the arcadedb-embedded-native shim; arcadedb-embedded points to arcadedb (the native build) Two decisions of 2026-10-25 replace the mapping of the previous commit: - arcadedb-embedded-native gets no shim. Its PyPI project has no files and will be archived, so there is nothing to redirect. The shim folder, the arcadedb_native module, its tests and its documentation are gone, and build_shims.py builds one shim. - arcadedb-embedded (the old JVM package) now depends on `arcadedb==26.11.1`, the NATIVE build, re-exports it as arcadedb_embedded and warns once (FutureWarning) in plain words: the package is now `arcadedb`, which is the native build and runs without a JVM; for the JVM build install `arcadedb-jvm`; the Which package? page of the arcadedb-python documentation is linked. The README (the PyPI description) and MIGRATION.md say the same. - The extras are forwarded to `arcadedb`; the old `vector` extra (numpy) maps to the `numpy` extra of `arcadedb`, which has no `vector` (EXTRAS in build_shims.py is now an old-to-new mapping, and check_target() checks it). - deprecation/README.md and the root README state that this shim publishes only after the native `arcadedb` 26.11.1 FINAL exists (the pin `arcadedb==26.11.1` does not match a devN release). Tests (12, were 22; red before the change, green after): the shim requires `arcadedb` and not `arcadedb-jvm`, extras mapping and Replaced by URL, the PyPI README says arcadedb is the native build and names `arcadedb-jvm` with the Which package? link, the warning text, a guard that no native shim or arcadedb_native module comes back, TARGETS equals the decided mapping, check_target() refuses a shim pointed at arcadedb-jvm or another module name. Co-Authored-By: Claude Sonnet 5.5 --- bindings/python/AGENTS.md | 2 +- bindings/python/deprecation/MIGRATION.md | 39 ++-- bindings/python/deprecation/README.md | 21 +- .../arcadedb-embedded-native/README.md | 29 --- .../arcadedb-embedded-native/pyproject.toml | 47 ----- .../src/arcadedb_embedded/__init__.py | 101 --------- .../src/arcadedb_native/__init__.py | 101 --------- .../deprecation/arcadedb-embedded/README.md | 23 ++- .../arcadedb-embedded/pyproject.toml | 16 +- .../src/arcadedb_embedded/__init__.py | 18 +- bindings/python/deprecation/build_shims.py | 27 +-- bindings/python/docs/development/release.md | 2 +- bindings/python/docs/development/testing.md | 2 +- .../python/tests/test_deprecation_shims.py | 192 ++++++------------ 14 files changed, 149 insertions(+), 471 deletions(-) delete mode 100644 bindings/python/deprecation/arcadedb-embedded-native/README.md delete mode 100644 bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml delete mode 100644 bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py delete mode 100644 bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py diff --git a/bindings/python/AGENTS.md b/bindings/python/AGENTS.md index ee88e89f493..81bd8d44522 100644 --- a/bindings/python/AGENTS.md +++ b/bindings/python/AGENTS.md @@ -17,7 +17,7 @@ Paths in this file are relative to the repository root (the directory that conta | reporting an engine bug upstream, sending a low-hanging engine fix, or checking an upstream fix to one of our issues | `bindings/python/docs/development/upstream-issues.md` | | the periodic `bindings/python` update to ArcadeData/arcadedb (retired 2026-10-25; the bindings continue in ArcadeData/arcadedb-python) | `bindings/python/docs/development/upstream-bindings-pr.md` | | releasing | `bindings/python/docs/development/release.md` | -| publishing the last `arcadedb-embedded` and `arcadedb-embedded-native` releases (the deprecation shims), or telling users how to move | `bindings/python/deprecation/README.md`, `bindings/python/deprecation/MIGRATION.md` | +| publishing the last `arcadedb-embedded` release (the deprecation shim), or telling users how to move | `bindings/python/deprecation/README.md`, `bindings/python/deprecation/MIGRATION.md` | | debugging a build, JVM or test failure | `bindings/python/docs/development/troubleshooting.md` | | making a performance claim, or working around an engine bug | `bindings/python/docs/guide/performance.md`, `bindings/python/docs/guide/known-issues.md`, `bindings/python/docs/benchmarks/protocol.md` | diff --git a/bindings/python/deprecation/MIGRATION.md b/bindings/python/deprecation/MIGRATION.md index 419ddaa4b34..ca5c4a7dde9 100644 --- a/bindings/python/deprecation/MIGRATION.md +++ b/bindings/python/deprecation/MIGRATION.md @@ -1,23 +1,23 @@ -# Migrating from arcadedb-embedded and arcadedb-embedded-native +# Migrating from arcadedb-embedded -The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python), under new PyPI names: `arcadedb-embedded` (the JVM package) becomes `arcadedb-jvm`, and `arcadedb-embedded-native` (the native package) becomes `arcadedb`. This repository is archived after the move. Documentation: [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/). +The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python), under new PyPI names: `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build). `arcadedb-embedded` was the JVM package. Its last release is a shim that installs `arcadedb`, the native build. If you need the JVM build, install `arcadedb-jvm` yourself. This repository is archived after the move. Documentation: [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/). To decide between the two packages, read [Which package?](https://docs.arcadedb.com/arcadedb-python/which-package.html). + +`arcadedb-embedded-native` (the native preview package) has no shim and no new release. Its PyPI project has no files and is archived. If you used a preview of it from another source, install `arcadedb`. ## Install and import -| Before | After | -|---|---| -| `pip install arcadedb-embedded` | `pip install arcadedb-jvm` | -| `pip install arcadedb-embedded-native` (preview) | `pip install arcadedb` | -| `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb-jvm[arrow,pandas]'` | -| `uv add arcadedb-embedded` | `uv add arcadedb-jvm` | -| `import arcadedb_embedded` | `import arcadedb` | -| `import arcadedb_native` (from `arcadedb-embedded-native`) | `import arcadedb` | -| `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | -| `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... (and `arcadedb_native.core`, ...) | `arcadedb.core`, `arcadedb.jvm`, ... | +| Before | After (native build) | After (JVM build) | +|---|---|---| +| `pip install arcadedb-embedded` | `pip install arcadedb` | `pip install arcadedb-jvm` | +| `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb[arrow,pandas]'` | `pip install 'arcadedb-jvm[arrow,pandas]'` | +| `uv add arcadedb-embedded` | `uv add arcadedb` | `uv add arcadedb-jvm` | +| `import arcadedb_embedded` | `import arcadedb` | `import arcadedb` | +| `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | `from arcadedb import open_database` | +| `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... | `arcadedb.core`, ... | `arcadedb.core`, `arcadedb.jvm`, ... | -`arcadedb-jvm` is the same JVM build as `arcadedb-embedded`, so moving to it changes nothing at run time. `pip install arcadedb` gives the native build instead (no JVM), a different runtime: take it on purpose, from the documentation, not because the name looks like the successor of `arcadedb-embedded`. +The package for `arcadedb-embedded` users is now `arcadedb`, which is the native build: the engine runs as a native library, without a JVM. It is a different runtime from the one you had. If your code needs the JVM build, for example because it uses Java classes through JPype, install `arcadedb-jvm`: it is the same JVM build as `arcadedb-embedded`, so nothing changes at run time. The extra `vector` of `arcadedb-embedded` (numpy) is the extra `numpy` of `arcadedb`; `arrow`, `pandas` and `polars` keep their names. -Both new packages provide the module `arcadedb`, so an environment holds `arcadedb-jvm` or `arcadedb`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded` (or `arcadedb-embedded-native`). +Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-jvm`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded`. ## Version mapping @@ -25,20 +25,21 @@ From 26.11.1 the package version is the ArcadeDB engine version, as before. | Old | Last release | New | |---|---|---| -| `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb-jvm` 26.11.1 and later | -| `arcadedb-embedded-native` (preview) | 26.10.2.dev1, then the shim 26.11.1 | `arcadedb` 26.11.1 and later | +| `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb` 26.11.1 and later (native), or `arcadedb-jvm` 26.11.1 and later (JVM) | +| `arcadedb-embedded-native` (preview) | 26.10.2.dev1 (no files on PyPI), no shim | `arcadedb` 26.11.1 and later | -The 26.11.1 releases of the old names are shims and the last ones. The `arcadedb-embedded` shim installs `arcadedb-jvm` at exactly 26.11.1, and the `arcadedb-embedded-native` shim installs `arcadedb` at exactly 26.11.1; importing `arcadedb_embedded` (or `arcadedb_native`, for the native shim) then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. A shim never upgrades: for newer versions install `arcadedb-jvm` or `arcadedb`. Older releases of the old names stay on PyPI for pinned installs. +The 26.11.1 release of `arcadedb-embedded` is a shim and the last one. It installs `arcadedb` at exactly 26.11.1. Importing `arcadedb_embedded` then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. The warning says that the package is now `arcadedb`, that this is the native build, and that `arcadedb-jvm` is the JVM build. The shim never upgrades: for newer versions install `arcadedb` or `arcadedb-jvm`. Older releases of the old name stay on PyPI for pinned installs. The shim is published only after the native `arcadedb` 26.11.1 final release exists. ## What stays the same -- The Python API: the module is renamed, its classes and functions are the ones of the 26.11.1 bindings. The runtime of each old package stays the same when you follow the table: JVM to `arcadedb-jvm`, native to `arcadedb`. +- The Python API: the module is renamed, its classes and functions are the ones of the 26.11.1 bindings. A few features can differ between the two builds; Which package? lists them. - Python 3.11 to 3.14, and the Apache-2.0 license. -- The extras `vector`, `arrow`, `pandas` and `polars`, under the same names. +- The extras `arrow`, `pandas` and `polars`, under the same names. - Where to look: the user guide and API reference move with the code to the new docs site, and the old `docs.humem.ai/arcadedb` pages redirect there. ## What changes - The distribution and import names, as above. +- The default runtime: `pip install arcadedb` is the native build, not the JVM build. - Issues and pull requests go to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python/issues). Benchmarks stay with HumemAI, in [humemai/dbbench](https://github.com/humemai/dbbench). - Engine changes between 26.10.2 and 26.11.1: read the 26.11.1 release notes, as for any upgrade. diff --git a/bindings/python/deprecation/README.md b/bindings/python/deprecation/README.md index 851bf147bc3..d2b6b935b17 100644 --- a/bindings/python/deprecation/README.md +++ b/bindings/python/deprecation/README.md @@ -1,18 +1,21 @@ -# Deprecation shims for the old PyPI names +# Deprecation shim for the old PyPI name arcadedb-embedded -The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build), both importing as `arcadedb`. This folder holds the last release of each old name: a pure-Python shim that depends on its replacement at the same version, warns once, and re-exports it. The old JVM package `arcadedb-embedded` points to `arcadedb-jvm` (the same build, so no silent change at run time) and the old native package `arcadedb-embedded-native` points to `arcadedb`. Users: see [MIGRATION.md](MIGRATION.md). +The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build), both importing as `arcadedb`. This folder holds the last release of the old name `arcadedb-embedded`: a pure-Python shim that depends on `arcadedb` at the same version, warns once, and re-exports it. The old JVM package therefore moves to the native build, on purpose (decided 2026-10-25). The warning and the README say so in plain words, name `arcadedb-jvm` as the way to keep the JVM build, and link the Which package? page of the new documentation. Users: see [MIGRATION.md](MIGRATION.md). + +`arcadedb-embedded-native` (the native preview package) gets no shim. Its PyPI project has no files and is archived, so there is nothing to redirect. | Folder | PyPI project | Depends on | Version | |---|---|---|---| -| `arcadedb-embedded/` | `arcadedb-embedded` (JVM) | `arcadedb-jvm==26.11.1` | 26.11.1 | -| `arcadedb-embedded-native/` | `arcadedb-embedded-native` (native) | `arcadedb==26.11.1` | 26.11.1 | +| `arcadedb-embedded/` | `arcadedb-embedded` (old JVM package) | `arcadedb==26.11.1` (the native build) | 26.11.1 | + +The shim module is `arcadedb_embedded`. The mapping above lives in one place, `TARGETS` in `build_shims.py`, together with the one assumption behind it: `arcadedb` provides the import name `arcadedb` (the layout says so, but it has no real release yet). `EXTRAS` there maps the extras of the old package to the extras of `arcadedb` (`vector`, the numpy extra, becomes `numpy`; `arcadedb` has no `vector`). `build_shims.py` refuses to build when the shim's `pyproject.toml` (name, description, dependencies, extras, `Replaced by` URL) or its `_NEW_DIST` and `_NEW_MODULE` constants disagree with `TARGETS`. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. -The shim modules are `arcadedb_embedded` (in both shims) and `arcadedb_native` (in `arcadedb-embedded-native` only: the old native package imported as `arcadedb_native`). The three `__init__.py` files differ only in their docstring and the constants `_OLD_DIST`, `_NEW_DIST`, `_NEW_MODULE` and `_NOTE`; `tests/test_deprecation_shims.py` checks that the rest is identical. The mapping above lives in one place, `TARGETS` in `build_shims.py`, together with the one assumption behind it: both `arcadedb` and `arcadedb-jvm` provide the import name `arcadedb` (the layout says so, but neither has a real release yet). `build_shims.py` refuses to build when a shim's `pyproject.toml` (name, description, dependencies, extras, `Replaced by` URL) or its `_NEW_DIST` and `_NEW_MODULE` constants disagree with `TARGETS`. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. +**This shim publishes only after the native `arcadedb` 26.11.1 FINAL exists.** It pins `arcadedb==26.11.1`, and a `26.11.1.devN` release does not satisfy that pin. Until the final release is out, do not publish this shim: `pip install arcadedb-embedded` would fail to resolve. ## Build and test (never uploads) ```bash -python3 bindings/python/deprecation/build_shims.py --out /tmp/shims # uv build, wheel + sdist of both +python3 bindings/python/deprecation/build_shims.py --out /tmp/shims # uv build, wheel + sdist taskset -c 0-3 uv run pytest bindings/python/tests/test_deprecation_shims.py # needs the real wheel installed, like the rest of the suite ``` @@ -20,11 +23,11 @@ taskset -c 0-3 uv run pytest bindings/python/tests/test_deprecation_shims.py # ## Cutover day -1. Release `arcadedb-jvm` 26.11.1 and `arcadedb` 26.11.1 (the native build) from ArcadeData/arcadedb-python first. Check `pip download --no-deps arcadedb-jvm==26.11.1` and `arcadedb==26.11.1` work: the shims pin these exact versions. If the first final `arcadedb` is not out (only `26.11.1.devN`), do not publish the native shim: `arcadedb==26.11.1` does not match a dev release, so change its pin and version rule here first. The JVM shim can go out on its own once `arcadedb-jvm==26.11.1` exists. +1. Release the native `arcadedb` 26.11.1 FINAL from ArcadeData/arcadedb-python first (and `arcadedb-jvm` 26.11.1, which the warning names). Check `pip download --no-deps arcadedb==26.11.1` works: the shim pins this exact version. If the first final `arcadedb` is not out (only `26.11.1.devN`), do not publish the shim. 2. Never push a `26.11.1` tag in this repository: `release-python-packages.yml` would build and publish the real wheels under `arcadedb-embedded`. 3. Build and check: `python3 bindings/python/deprecation/build_shims.py --out dist --check-pypi && uvx twine check dist/*`. -4. Publish both shims. Trusted publishing works only from a workflow, so either (a) register a trusted publisher on both PyPI projects (repository `humemai/arcadedb-embedded-python`, workflow `publish-deprecation-shims.yml`, environment `pypi`), add the workflow below to `.github/workflows/` (and to `FORK_GITHUB_ALLOWLIST` in `sync-upstream.sh` if another sync will still run), and dispatch it with `publish` on; or (b) upload from a laptop with `uvx twine upload dist/*` and a project-scoped API token. -5. In a clean environment: `pip install arcadedb-embedded==26.11.1` installs `arcadedb-jvm` (not `arcadedb`: check `pip list`), and `python -W default -c "import arcadedb_embedded"` shows one FutureWarning; for `arcadedb-embedded-native`, which installs `arcadedb`, `python -W default -c "import arcadedb_native"` shows one FutureWarning too. +4. Publish the shim. Trusted publishing works only from a workflow, so either (a) register a trusted publisher on the PyPI project `arcadedb-embedded` (repository `humemai/arcadedb-embedded-python`, workflow `publish-deprecation-shims.yml`, environment `pypi`), add the workflow below to `.github/workflows/` (and to `FORK_GITHUB_ALLOWLIST` in `sync-upstream.sh` if another sync will still run), and dispatch it with `publish` on; or (b) upload from a laptop with `uvx twine upload dist/*` and a project-scoped API token. +5. In a clean environment: `pip install arcadedb-embedded==26.11.1` installs `arcadedb` (the native build, not `arcadedb-jvm`: check `pip list`), and `python -W default -c "import arcadedb_embedded"` shows one FutureWarning that names `arcadedb-jvm` for the JVM build. ```yaml name: Publish deprecation shims diff --git a/bindings/python/deprecation/arcadedb-embedded-native/README.md b/bindings/python/deprecation/arcadedb-embedded-native/README.md deleted file mode 100644 index 7c43e91c6cd..00000000000 --- a/bindings/python/deprecation/arcadedb-embedded-native/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# arcadedb-embedded-native has moved to arcadedb - -**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. `arcadedb` is the native build, the same kind of build as `arcadedb-embedded-native` (a GraalVM native image, no JVM): - -| | Old | New | -|---|---|---| -| PyPI package | `arcadedb-embedded-native` | [`arcadedb`](https://pypi.org/project/arcadedb/) | -| Import | `import arcadedb_native` | `import arcadedb` | -| Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | -| Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | - -## What to do - -```bash -pip uninstall arcadedb-embedded-native -pip install arcadedb -``` - -Then change `import arcadedb_native` to `import arcadedb` (and `from arcadedb_native import X` to `from arcadedb import X`). The new package is the native-image bindings, published under the new name. `arcadedb` 26.11.1 has the same version number as this release. - -`arcadedb` and `arcadedb-jvm` (the JVM build) both provide the module `arcadedb`: install one of them in an environment, not both. - -## What this release does - -It is a shim. It installs `arcadedb==26.11.1`, and `import arcadedb_native` (the import name of the old package; `import arcadedb_embedded` works too) emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb==26.11.1` and gets no updates: to receive newer versions, switch to `arcadedb`. - -The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb`. - -Older `arcadedb-embedded-native` releases (the previews 26.10.2.dev0 and 26.10.2.dev1) stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml deleted file mode 100644 index e77850ba89c..00000000000 --- a/bindings/python/deprecation/arcadedb-embedded-native/pyproject.toml +++ /dev/null @@ -1,47 +0,0 @@ -# Deprecation shim for arcadedb-embedded-native (the last release). Built by ../build_shims.py; see ../README.md. -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "arcadedb-embedded-native" -version = "26.11.1" -description = "Deprecated: arcadedb-embedded-native has moved to arcadedb (import arcadedb). Last release, a thin shim." -readme = "README.md" -requires-python = ">=3.11" -license = "Apache-2.0" -license-files = ["LICENSE"] -authors = [{ name = "Taewoon Kim", email = "taewoon@humem.ai" }] -keywords = ["arcadedb", "deprecated", "moved", "database"] -classifiers = [ - "Development Status :: 7 - Inactive", - "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "Topic :: Database", -] -# The shim installs exactly the package it points to, at the same version. -dependencies = ["arcadedb==26.11.1"] - -# The extras people installed from the old name, forwarded to the new one. -[project.optional-dependencies] -vector = ["arcadedb[vector]==26.11.1"] -arrow = ["arcadedb[arrow]==26.11.1"] -pandas = ["arcadedb[pandas]==26.11.1"] -polars = ["arcadedb[polars]==26.11.1"] - -[project.urls] -Homepage = "https://github.com/ArcadeData/arcadedb-python" -Documentation = "https://docs.arcadedb.com/arcadedb-python/" -Source = "https://github.com/ArcadeData/arcadedb-python" -Issues = "https://github.com/ArcadeData/arcadedb-python/issues" -"Replaced by" = "https://pypi.org/project/arcadedb/" - -[tool.hatch.build.targets.wheel] -packages = ["src/arcadedb_embedded", "src/arcadedb_native"] - -[tool.hatch.build.targets.sdist] -include = ["src", "README.md", "LICENSE", "pyproject.toml"] diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py deleted file mode 100644 index d7b94f6927a..00000000000 --- a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_embedded/__init__.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) -# SPDX-License-Identifier: Apache-2.0 -"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb``. - -This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb``, which provides the module ``arcadedb``. -""" - -# ---- per-shim constants: the shims differ only in this block ---- -_OLD_DIST = "arcadedb-embedded-native" -_NEW_DIST = "arcadedb" -_NEW_MODULE = "arcadedb" -_NOTE = "`arcadedb` is the native build, like this package." -# ---- shared body: identical in all shim modules (tests/test_deprecation_shims.py checks it) ---- - -import importlib -import importlib.abc -import importlib.machinery -import importlib.util -import sys -import warnings - -_MESSAGE = ( - f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`). {_NOTE} " - f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " - "New home: https://github.com/ArcadeData/arcadedb-python, " - "docs: https://docs.arcadedb.com/arcadedb-python/" -) -# FutureWarning, not DeprecationWarning: Python shows it to end users by default. -warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) - -_new = importlib.import_module(_NEW_MODULE) -__all__ = list( - getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] -) -# the same names `from import *` would bind -globals().update({_n: getattr(_new, _n) for _n in __all__}) - -_OLD = __name__ -_NEW = _new.__name__ - - -class _AliasLoader(importlib.abc.Loader): - """Hand out the real ``arcadedb`` submodule under the old name, not a second copy.""" - - def __init__(self, target): - self._target = target - self._spec = None - - def create_module(self, spec): - module = importlib.import_module(self._target) - self._spec = module.__spec__ - return module - - def exec_module(self, module): - # the import machinery stamped the alias spec on the shared module: put its own back - module.__spec__ = self._spec - - -class _AliasFinder(importlib.abc.MetaPathFinder): - """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" - - alias_of = _NEW - old = _OLD - - def find_spec(self, fullname, path=None, target=None): - if not fullname.startswith(_OLD + "."): - return None - real = _NEW + fullname[len(_OLD) :] - try: - found = importlib.util.find_spec(real) - except (ImportError, ValueError): - return None - if found is None: - return None - return importlib.machinery.ModuleSpec(fullname, _AliasLoader(real)) - - -# first in line, so a package alias is not also found on disk and loaded a second time -if not any( - getattr(f, "alias_of", None) == _NEW and getattr(f, "old", None) == _OLD - for f in sys.meta_path -): - sys.meta_path.insert(0, _AliasFinder()) - -# submodules the new package has already imported answer to the old name right away -for _name, _module in list(sys.modules.items()): - if _name.startswith(_NEW + ".") and _module is not None: - sys.modules.setdefault(_OLD + _name[len(_NEW) :], _module) - - -def __getattr__(name): - """Anything not star-imported (a private name, a submodule) comes from ``arcadedb``.""" - try: - return getattr(_new, name) - except AttributeError: - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None - - -def __dir__(): - return sorted(set(globals()) | set(dir(_new))) diff --git a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py b/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py deleted file mode 100644 index d7b94f6927a..00000000000 --- a/bindings/python/deprecation/arcadedb-embedded-native/src/arcadedb_native/__init__.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) -# SPDX-License-Identifier: Apache-2.0 -"""Deprecated: ``arcadedb-embedded-native`` has moved to ``arcadedb``. - -This is the last release of ``arcadedb-embedded-native``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb``, which provides the module ``arcadedb``. -""" - -# ---- per-shim constants: the shims differ only in this block ---- -_OLD_DIST = "arcadedb-embedded-native" -_NEW_DIST = "arcadedb" -_NEW_MODULE = "arcadedb" -_NOTE = "`arcadedb` is the native build, like this package." -# ---- shared body: identical in all shim modules (tests/test_deprecation_shims.py checks it) ---- - -import importlib -import importlib.abc -import importlib.machinery -import importlib.util -import sys -import warnings - -_MESSAGE = ( - f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`). {_NOTE} " - f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " - "New home: https://github.com/ArcadeData/arcadedb-python, " - "docs: https://docs.arcadedb.com/arcadedb-python/" -) -# FutureWarning, not DeprecationWarning: Python shows it to end users by default. -warnings.warn(_MESSAGE, FutureWarning, stacklevel=2) - -_new = importlib.import_module(_NEW_MODULE) -__all__ = list( - getattr(_new, "__all__", None) or [n for n in dir(_new) if not n.startswith("_")] -) -# the same names `from import *` would bind -globals().update({_n: getattr(_new, _n) for _n in __all__}) - -_OLD = __name__ -_NEW = _new.__name__ - - -class _AliasLoader(importlib.abc.Loader): - """Hand out the real ``arcadedb`` submodule under the old name, not a second copy.""" - - def __init__(self, target): - self._target = target - self._spec = None - - def create_module(self, spec): - module = importlib.import_module(self._target) - self._spec = module.__spec__ - return module - - def exec_module(self, module): - # the import machinery stamped the alias spec on the shared module: put its own back - module.__spec__ = self._spec - - -class _AliasFinder(importlib.abc.MetaPathFinder): - """Resolve ``arcadedb_embedded.x.y`` to ``arcadedb.x.y``, also for modules not yet imported.""" - - alias_of = _NEW - old = _OLD - - def find_spec(self, fullname, path=None, target=None): - if not fullname.startswith(_OLD + "."): - return None - real = _NEW + fullname[len(_OLD) :] - try: - found = importlib.util.find_spec(real) - except (ImportError, ValueError): - return None - if found is None: - return None - return importlib.machinery.ModuleSpec(fullname, _AliasLoader(real)) - - -# first in line, so a package alias is not also found on disk and loaded a second time -if not any( - getattr(f, "alias_of", None) == _NEW and getattr(f, "old", None) == _OLD - for f in sys.meta_path -): - sys.meta_path.insert(0, _AliasFinder()) - -# submodules the new package has already imported answer to the old name right away -for _name, _module in list(sys.modules.items()): - if _name.startswith(_NEW + ".") and _module is not None: - sys.modules.setdefault(_OLD + _name[len(_NEW) :], _module) - - -def __getattr__(name): - """Anything not star-imported (a private name, a submodule) comes from ``arcadedb``.""" - try: - return getattr(_new, name) - except AttributeError: - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None - - -def __dir__(): - return sorted(set(globals()) | set(dir(_new))) diff --git a/bindings/python/deprecation/arcadedb-embedded/README.md b/bindings/python/deprecation/arcadedb-embedded/README.md index 9f0d587bb76..e6417217563 100644 --- a/bindings/python/deprecation/arcadedb-embedded/README.md +++ b/bindings/python/deprecation/arcadedb-embedded/README.md @@ -1,10 +1,10 @@ -# arcadedb-embedded has moved to arcadedb-jvm +# arcadedb-embedded has moved to arcadedb -**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. `arcadedb-jvm` is the same JVM build as `arcadedb-embedded` (JPype and a bundled JRE), so moving to it changes nothing at run time: +**This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. The package is now [`arcadedb`](https://pypi.org/project/arcadedb/), which is the **native build**: the engine runs as a native library, without a JVM. If you need the JVM build, install [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) instead. | | Old | New | |---|---|---| -| PyPI package | `arcadedb-embedded` | [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) | +| PyPI package | `arcadedb-embedded` | [`arcadedb`](https://pypi.org/project/arcadedb/) (native build) or [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) (JVM build) | | Import | `import arcadedb_embedded` | `import arcadedb` | | Source and issues | `humemai/arcadedb-embedded-python` (archived) | [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) | | Documentation | `docs.humem.ai/arcadedb` | [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/) | @@ -13,17 +13,24 @@ ```bash pip uninstall arcadedb-embedded -pip install arcadedb-jvm +pip install arcadedb ``` -Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). The new package is the same bindings and the same JVM build, published under the new name. `arcadedb-jvm` 26.11.1 has the same version number as this release. +Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). + +`arcadedb-embedded` was the JVM build, so `arcadedb` is a different runtime. If your code needs the JVM build, for example because it uses Java classes through JPype or a long-running load that suits the JVM, install it instead: + +```bash +pip uninstall arcadedb-embedded +pip install arcadedb-jvm +``` -Do not install `arcadedb` instead: it is the native build (no JVM), a different runtime. Choose it on purpose, from the documentation, not by following the old name. `arcadedb` and `arcadedb-jvm` both provide the module `arcadedb`: install one of them in an environment, not both. +[Which package?](https://docs.arcadedb.com/arcadedb-python/which-package.html) explains the difference. `arcadedb` and `arcadedb-jvm` both provide the module `arcadedb`: install one of them in an environment, not both. ## What this release does -It is a shim. It installs `arcadedb-jvm==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module of `arcadedb-jvm`, submodules included, so existing code keeps running. It is pinned to `arcadedb-jvm==26.11.1` and gets no updates: to receive newer ArcadeDB versions, switch to `arcadedb-jvm`. +It is a shim. It installs `arcadedb==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb==26.11.1` and gets no updates: to receive newer ArcadeDB versions, switch to `arcadedb` (or `arcadedb-jvm`). -The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to the same extras of `arcadedb-jvm`. +The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to `arcadedb`. The old `vector` extra (numpy) maps to the `numpy` extra of `arcadedb`. Older `arcadedb-embedded` releases stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml index 279654a2d7f..51202b3334e 100644 --- a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml +++ b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "arcadedb-embedded" version = "26.11.1" -description = "Deprecated: arcadedb-embedded has moved to arcadedb-jvm (import arcadedb). Last release, a thin shim." +description = "Deprecated: arcadedb-embedded has moved to arcadedb (import arcadedb). Last release, a thin shim." readme = "README.md" requires-python = ">=3.11" license = "Apache-2.0" @@ -24,21 +24,21 @@ classifiers = [ "Topic :: Database", ] # The shim installs exactly the package it points to, at the same version. -dependencies = ["arcadedb-jvm==26.11.1"] +dependencies = ["arcadedb==26.11.1"] -# The extras people installed from the old name, forwarded to the new one. +# The extras people installed from the old name, forwarded to the new one (the old `vector` extra was numpy; `arcadedb` has no `vector`). [project.optional-dependencies] -vector = ["arcadedb-jvm[vector]==26.11.1"] -arrow = ["arcadedb-jvm[arrow]==26.11.1"] -pandas = ["arcadedb-jvm[pandas]==26.11.1"] -polars = ["arcadedb-jvm[polars]==26.11.1"] +vector = ["arcadedb[numpy]==26.11.1"] +arrow = ["arcadedb[arrow]==26.11.1"] +pandas = ["arcadedb[pandas]==26.11.1"] +polars = ["arcadedb[polars]==26.11.1"] [project.urls] Homepage = "https://github.com/ArcadeData/arcadedb-python" Documentation = "https://docs.arcadedb.com/arcadedb-python/" Source = "https://github.com/ArcadeData/arcadedb-python" Issues = "https://github.com/ArcadeData/arcadedb-python/issues" -"Replaced by" = "https://pypi.org/project/arcadedb-jvm/" +"Replaced by" = "https://pypi.org/project/arcadedb/" [tool.hatch.build.targets.wheel] packages = ["src/arcadedb_embedded"] diff --git a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py index 0200b6146d6..1e337866e6c 100644 --- a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py @@ -1,16 +1,20 @@ # Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) # SPDX-License-Identifier: Apache-2.0 -"""Deprecated: ``arcadedb-embedded`` has moved to ``arcadedb-jvm``. +"""Deprecated: ``arcadedb-embedded`` has moved to ``arcadedb``, the native build. -This is the last release of ``arcadedb-embedded``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb-jvm``, which provides the module ``arcadedb``. +This is the last release of ``arcadedb-embedded``. It is a thin shim: it warns once at import, then hands every name and submodule over to ``arcadedb``, which provides the module ``arcadedb``. For the JVM build, install ``arcadedb-jvm``. """ -# ---- per-shim constants: the shims differ only in this block ---- +# ---- constants of this shim ---- _OLD_DIST = "arcadedb-embedded" -_NEW_DIST = "arcadedb-jvm" +_NEW_DIST = "arcadedb" _NEW_MODULE = "arcadedb" -_NOTE = "`arcadedb-jvm` is the same JVM build as this package, so nothing changes at run time (`arcadedb` is the native build, a different runtime)." -# ---- shared body: identical in all shim modules (tests/test_deprecation_shims.py checks it) ---- +_NOTE = ( + "`arcadedb` is the native build, which runs without a JVM. " + "If you need the JVM build, install `arcadedb-jvm` (`pip install arcadedb-jvm`). " + "Which package to pick: https://docs.arcadedb.com/arcadedb-python/which-package.html." +) +# ---- body (tests/test_deprecation_shims.py checks that it is there) ---- import importlib import importlib.abc @@ -21,7 +25,7 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"Install {_NEW_DIST!r} instead (`pip install {_NEW_DIST}`). {_NOTE} " + f"The package is now {_NEW_DIST!r}: install it with `pip install {_NEW_DIST}`. {_NOTE} " f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" diff --git a/bindings/python/deprecation/build_shims.py b/bindings/python/deprecation/build_shims.py index 9704d96242e..facd51ef148 100644 --- a/bindings/python/deprecation/build_shims.py +++ b/bindings/python/deprecation/build_shims.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Copyright © 2021-present Arcade Data Ltd (info@arcadedata.com) # SPDX-License-Identifier: Apache-2.0 -"""Build the two deprecation shims (``arcadedb-embedded``, ``arcadedb-embedded-native``) with ``uv build``. +"""Build the deprecation shim of ``arcadedb-embedded`` with ``uv build``. This builds only. It never uploads: publishing is a separate, deliberate step (README.md here). @@ -15,7 +15,7 @@ build_shims.py [--out DIR] [--shim NAME ...] [--check-pypi] --out DIR where the wheels and sdists go (default: dist/ next to this script, git-ignored) - --shim NAME build only this shim (repeatable; default: both) + --shim NAME build only this shim (repeatable; default: all of them, which is one) --check-pypi also run check_version.py against the live PyPI project (needs the network, and fails once that version has been published, as the counting rule says) """ @@ -35,7 +35,8 @@ HERE = Path(__file__).resolve().parent REPO_ROOT = HERE.parents[2] -SHIMS = ("arcadedb-embedded", "arcadedb-embedded-native") +# There is one shim. `arcadedb-embedded-native` (the native preview package) gets none: its PyPI project has no files and is archived. +SHIMS = ("arcadedb-embedded",) class Target(NamedTuple): @@ -45,18 +46,18 @@ class Target(NamedTuple): module: str # the import name that distribution provides: what the shim re-exports -# Where each old name points (the packaging layout decided on 2026-10-25, "layout v2" in ArcadeData/arcadedb-python .notes/MIGRATION.md): -# arcadedb-embedded (the JVM package) -> arcadedb-jvm, the same JVM build, so there is no silent change at run time -# arcadedb-embedded-native (the native package) -> arcadedb, the native build -# ASSUMPTION: both new distributions provide the import name `arcadedb`. The layout says "import arcadedb for all" and that `arcadedb` and `arcadedb-jvm` refuse to import side by side because they share that module. Neither has a real release on PyPI yet (arcadedb 0.0.1 and arcadedb-jvm 0.0.1 are placeholders), so this cannot be checked here. -# If a package gets another import name, change it here and in the `_NEW_MODULE` constant of that shim's `__init__.py` files; check_target() fails until both agree. +# Where each old name points (the packaging layout decided on 2026-10-25, "layout v2" in ArcadeData/arcadedb-python .notes/MIGRATION.md, +# and the decision of 2026-10-25 that the old JVM package moves to the native build): +# arcadedb-embedded (the old JVM package) -> arcadedb, the NATIVE build. The warning and the README say so in plain words and name +# `arcadedb-jvm` as the way to keep the JVM build. This shim therefore publishes only after the native `arcadedb` 26.11.1 FINAL exists. +# ASSUMPTION: `arcadedb` provides the import name `arcadedb`. The layout says "import arcadedb for all". `arcadedb` has no real release on PyPI yet (0.0.1 is a placeholder), so this cannot be checked here. +# If the package gets another import name, change it here and in the `_NEW_MODULE` constant of the shim's `__init__.py`; check_target() fails until both agree. IMPORT_NAME = "arcadedb" TARGETS = { - "arcadedb-embedded": Target(dist="arcadedb-jvm", module=IMPORT_NAME), - "arcadedb-embedded-native": Target(dist="arcadedb", module=IMPORT_NAME), + "arcadedb-embedded": Target(dist="arcadedb", module=IMPORT_NAME), } -# the extras people installed from the old names, forwarded to the same extras of the target -EXTRAS = ("vector", "arrow", "pandas", "polars") +# the extras people installed from the old name -> the extra of the target they are forwarded to (the old `vector` extra was numpy; `arcadedb` has no `vector`) +EXTRAS = {"vector": "numpy", "arrow": "arrow", "pandas": "pandas", "polars": "polars"} def _load_check_version(): @@ -86,7 +87,7 @@ def check_target(shim: str) -> None: "description": f"Deprecated: {shim} has moved to {target.dist} (import {target.module}). Last release, a thin shim.", "dependencies": [f"{target.dist}=={version}"], "optional-dependencies": { - extra: [f"{target.dist}[{extra}]=={version}"] for extra in EXTRAS + old: [f"{target.dist}[{new}]=={version}"] for old, new in EXTRAS.items() }, } problems = [ diff --git a/bindings/python/docs/development/release.md b/bindings/python/docs/development/release.md index 1be65effdc0..0eda370355e 100644 --- a/bindings/python/docs/development/release.md +++ b/bindings/python/docs/development/release.md @@ -383,7 +383,7 @@ uv run --project .. --group docs mike alias --update-aliases \ ## The Last Releases: Deprecation Shims -The bindings move to ArcadeData/arcadedb-python as `arcadedb-jvm` (the JVM build) and `arcadedb` (the native build). The old names end with one shim release each, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that warns once and depends on its replacement at the same version: `arcadedb-embedded` on `arcadedb-jvm`, `arcadedb-embedded-native` on `arcadedb`). They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. +The bindings move to ArcadeData/arcadedb-python as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build). The old name `arcadedb-embedded` ends with one shim release, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that warns once and depends on `arcadedb`, the native build, at the same version; the warning names `arcadedb-jvm` for the JVM build). It publishes only after the native `arcadedb` 26.11.1 FINAL exists. `arcadedb-embedded-native` gets no shim: its PyPI project has no files and is archived. They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. ## Troubleshooting diff --git a/bindings/python/docs/development/testing.md b/bindings/python/docs/development/testing.md index 07fb1bbfc1a..aa7372658b6 100644 --- a/bindings/python/docs/development/testing.md +++ b/bindings/python/docs/development/testing.md @@ -143,7 +143,7 @@ asserts, so the list below stops at the file. | `test_wheel_platform_tag.py` | The wheel's manylinux platform tag, and `__version__` equals the installed distribution version | | `test_wheel_size.py` | The wheel size gate: a wheel at or over 100 decimal megabytes fails the build | | `test_check_test_skips.py` | The skip gate described below | -| `test_deprecation_shims.py` | The shims for the old PyPI names (`deprecation/`): both build with `uv build` as pure `py3-none-any` wheels, each pins its replacement at the same version (`arcadedb-embedded` pins `arcadedb-jvm`, `arcadedb-embedded-native` pins `arcadedb`, and `build_shims.py` refuses a shim that points elsewhere), the version follows the counting rule, and against a stub `arcadedb` the import warns once with a `FutureWarning` and names, submodules (also not-yet-imported and nested ones) and private attributes resolve to the new module. Not collected where `deprecation/` is absent (the upstream pull request branch) | +| `test_deprecation_shims.py` | The shim for the old PyPI name `arcadedb-embedded` (`deprecation/`): it builds with `uv build` as a pure `py3-none-any` wheel, pins `arcadedb` (the native build) at the same version and never `arcadedb-jvm` (`build_shims.py` refuses a shim that points elsewhere), there is no shim for `arcadedb-embedded-native`, the warning names `arcadedb-jvm` for the JVM build and links the Which package? page, the version follows the counting rule, and against a stub `arcadedb` the import warns once with a `FutureWarning` and names, submodules (also not-yet-imported and nested ones) and private attributes resolve to the new module. Not collected where `deprecation/` is absent (the upstream pull request branch) | ### Docs and open engine findings diff --git a/bindings/python/tests/test_deprecation_shims.py b/bindings/python/tests/test_deprecation_shims.py index 0d740dfa120..b2036db622a 100644 --- a/bindings/python/tests/test_deprecation_shims.py +++ b/bindings/python/tests/test_deprecation_shims.py @@ -1,12 +1,11 @@ -"""The deprecation shims for the old PyPI names (deprecation/): built here, checked against a stub. - -`arcadedb-embedded` and `arcadedb-embedded-native` get one last release, 26.11.1: a pure-Python shim that -installs its replacement at the same version, warns once at import, and re-exports it. The old JVM package -`arcadedb-embedded` points to `arcadedb-jvm` (the same JVM build), the old native package -`arcadedb-embedded-native` to `arcadedb` (the native build); both replacements import as `arcadedb`. The old -native package imported as `arcadedb_native`, so its shim ships that module as well as `arcadedb_embedded`. The -tests build both shims with `uv build` (no upload), and check the wheel, the dependency pin, the warning, and -attribute and submodule access against a stub `arcadedb` module: the real packages are not needed. +"""The deprecation shim for the old PyPI name (deprecation/): built here, checked against a stub. + +`arcadedb-embedded` gets one last release, 26.11.1: a pure-Python shim that installs `arcadedb` (the native +build, the new home of the bindings) at the same version, warns once at import, and re-exports it. The old JVM +package therefore moves to the native build; the warning says so in plain words and names `arcadedb-jvm` for +people who need the JVM build. `arcadedb-embedded-native` gets no shim: its PyPI project has no files and is +archived. The tests build the shim with `uv build` (no upload), and check the wheel, the dependency pin, the +warning, and attribute and submodule access against a stub `arcadedb` module: the real package is not needed. """ from __future__ import annotations @@ -26,24 +25,22 @@ DEPRECATION = BINDINGS / "deprecation" VERSION = "26.11.1" # old distribution -> the package it points to (written out here on purpose, apart from TARGETS in build_shims.py, -# so a wrong edit of that table fails a test) -SHIMS = {"arcadedb-embedded": "arcadedb-jvm", "arcadedb-embedded-native": "arcadedb"} -# the import name of both replacements (an assumption, see build_shims.IMPORT_NAME) +# so a wrong edit of that table fails a test). The old JVM package points to the NATIVE build. +SHIMS = {"arcadedb-embedded": "arcadedb"} +# the import name of the replacement (an assumption, see build_shims.IMPORT_NAME) IMPORT_NAME = "arcadedb" +WHICH_PACKAGE_URL = "https://docs.arcadedb.com/arcadedb-python/which-package.html" # What PyPI listed before the cutover (checked 2026-10-25): the counting rule is applied against this # snapshot, because once the shims are published the live list contains 26.11.1 itself. PUBLISHED_BEFORE_CUTOVER = { "arcadedb-embedded": ["26.9.1", "26.10.1", "26.10.2", "26.10.2.post1"], - "arcadedb-embedded-native": ["26.10.2.dev0", "26.10.2.dev1"], -} -# old distribution -> the import names its shim ships (the old native package imported as arcadedb_native) -MODULES = { - "arcadedb-embedded": ["arcadedb_embedded"], - "arcadedb-embedded-native": ["arcadedb_embedded", "arcadedb_native"], } +# old distribution -> the import names its shim ships +MODULES = {"arcadedb-embedded": ["arcadedb_embedded"]} CASES = [(dist, module) for dist, modules in MODULES.items() for module in modules] -BODY_MARKER = "# ---- shared body" -EXTRAS = ["arrow", "pandas", "polars", "vector"] +BODY_MARKER = "# ---- body" +# the extras of the old package -> the extra of `arcadedb` they are forwarded to (`vector` was numpy; `arcadedb` has no `vector`) +EXTRAS = {"arrow": "arrow", "pandas": "pandas", "polars": "polars", "vector": "numpy"} STUB_FILES = { "arcadedb/__init__.py": """ @@ -132,7 +129,7 @@ def _load_check_version(): @pytest.fixture(scope="module") def wheels(tmp_path_factory): - """Build both shims once (`uv build`, no upload) and return {old distribution: wheel path}.""" + """Build the shim once (`uv build`, no upload) and return {old distribution: wheel path}.""" out = tmp_path_factory.mktemp("shim-dist") assert _load_build_shims().main(["--out", str(out)]) == 0 built = {} @@ -226,65 +223,55 @@ def test_dependency_pins_the_new_package_at_the_same_version(dist, wheels): ), requires -def test_the_jvm_shim_requires_arcadedb_jvm_and_not_arcadedb(wheels): - """arcadedb-embedded was the JVM package: it must land on the JVM build, never silently on the native `arcadedb`.""" +def test_the_shim_requires_arcadedb_the_native_build_and_not_arcadedb_jvm(wheels): + """The decided layout: the old JVM package moves to `arcadedb`, which is the native build. It never pulls `arcadedb-jvm`.""" metadata = _metadata(wheels["arcadedb-embedded"]) - assert f"Requires-Dist: arcadedb-jvm=={VERSION}\n" in metadata - assert _required_names(metadata) == {"arcadedb-jvm"} - assert "arcadedb" not in _required_names(metadata) - assert "arcadedb-embedded-native" not in metadata - # the extras are forwarded to the same extras of arcadedb-jvm - assert ( - sorted(re.findall(r"^Provides-Extra: (.+)$", metadata, flags=re.MULTILINE)) - == EXTRAS - ) - for extra in EXTRAS: - assert ( - f"Requires-Dist: arcadedb-jvm[{extra}]=={VERSION}; extra == '{extra}'\n" - in metadata - ) - assert ( - "Project-URL: Replaced by, https://pypi.org/project/arcadedb-jvm/\n" in metadata - ) - assert "has moved to arcadedb-jvm (import arcadedb)" in metadata - - -def test_the_native_shim_requires_arcadedb_and_not_arcadedb_jvm(wheels): - """arcadedb-embedded-native was the native package: it lands on `arcadedb`, which is the native build.""" - metadata = _metadata(wheels["arcadedb-embedded-native"]) assert f"Requires-Dist: arcadedb=={VERSION}\n" in metadata assert _required_names(metadata) == {"arcadedb"} assert "arcadedb-jvm" not in _required_names(metadata) assert "arcadedb-native" not in _required_names(metadata) - assert ( - sorted(re.findall(r"^Provides-Extra: (.+)$", metadata, flags=re.MULTILINE)) - == EXTRAS - ) - for extra in EXTRAS: + assert "arcadedb-embedded-native" not in metadata + # the extras of the old package are forwarded to the matching extras of arcadedb + assert sorted( + re.findall(r"^Provides-Extra: (.+)$", metadata, flags=re.MULTILINE) + ) == sorted(EXTRAS) + for old, new in EXTRAS.items(): assert ( - f"Requires-Dist: arcadedb[{extra}]=={VERSION}; extra == '{extra}'\n" - in metadata + f"Requires-Dist: arcadedb[{new}]=={VERSION}; extra == '{old}'\n" in metadata ) assert "Project-URL: Replaced by, https://pypi.org/project/arcadedb/\n" in metadata assert "has moved to arcadedb (import arcadedb)" in metadata -def test_the_pypi_readme_gives_the_right_install_line(wheels): - jvm = _metadata(wheels["arcadedb-embedded"]) - assert "pip install arcadedb-jvm\n" in jvm - assert not re.search(r"pip install arcadedb(?![-\w])", jvm) - assert "pip uninstall arcadedb-embedded\n" in jvm - native = _metadata(wheels["arcadedb-embedded-native"]) - assert "pip install arcadedb\n" in native - assert "pip install arcadedb-jvm" not in native - assert "pip uninstall arcadedb-embedded-native\n" in native +def test_the_pypi_readme_says_arcadedb_is_the_native_build_and_names_the_jvm_package( + wheels, +): + metadata = _metadata(wheels["arcadedb-embedded"]) + assert "pip install arcadedb\n" in metadata + assert "pip uninstall arcadedb-embedded\n" in metadata + assert "native build" in metadata + # the JVM build is named as the other choice, with the install line, and the Which package? page is linked + assert "pip install arcadedb-jvm\n" in metadata + assert WHICH_PACKAGE_URL in metadata + + +def test_there_is_no_shim_for_the_native_preview_package(): + """`arcadedb-embedded-native` has no files on PyPI and is archived: nothing here builds, ships or mentions a shim for it.""" + build_shims = _load_build_shims() + assert build_shims.SHIMS == ("arcadedb-embedded",) + assert sorted(build_shims.TARGETS) == ["arcadedb-embedded"] + folders = sorted( + p.name for p in DEPRECATION.iterdir() if p.is_dir() and p.name != "__pycache__" + ) + assert folders == ["arcadedb-embedded"] + assert not list(DEPRECATION.glob("**/arcadedb_native")) def test_build_shims_has_one_table_with_the_decided_mapping(): build_shims = _load_build_shims() assert {k: v.dist for k, v in build_shims.TARGETS.items()} == SHIMS assert {v.module for v in build_shims.TARGETS.values()} == {IMPORT_NAME} - assert sorted(build_shims.EXTRAS) == EXTRAS + assert dict(build_shims.EXTRAS) == EXTRAS for dist in SHIMS: build_shims.check_target( dist @@ -295,7 +282,7 @@ def test_build_shims_has_one_table_with_the_decided_mapping(): def test_build_shims_refuses_a_shim_that_points_elsewhere(dist, monkeypatch): """Changing the table without the shim files (or the other way round) must stop the build.""" build_shims = _load_build_shims() - other = next(d for d in SHIMS.values() if d != SHIMS[dist]) + other = "arcadedb-jvm" # the other new package: the shim must not drift onto the JVM build unnoticed monkeypatch.setitem( build_shims.TARGETS, dist, build_shims.Target(dist=other, module=IMPORT_NAME) ) @@ -326,26 +313,14 @@ def test_version_follows_the_counting_rule(dist): build_shims.check_version(dist, check_pypi=False) -def test_all_shim_modules_share_one_body(): - bodies = [] - for dist, module in CASES: - text = (DEPRECATION / dist / "src" / module / "__init__.py").read_text( - encoding="utf-8" - ) - assert BODY_MARKER in text - bodies.append(text[text.index(BODY_MARKER) :]) - assert len(bodies) == 3 - assert all(body == bodies[0] for body in bodies) - - -def test_the_native_shim_ships_the_old_native_import_name(): - """The old arcadedb-embedded-native package imported as `arcadedb_native`; without this module an upgrader gets a bare ModuleNotFoundError.""" - assert "arcadedb_native" in MODULES["arcadedb-embedded-native"] - assert "arcadedb_native" not in MODULES["arcadedb-embedded"] - shim = DEPRECATION / "arcadedb-embedded-native" - assert (shim / "src" / "arcadedb_native" / "__init__.py").is_file() - pyproject = (shim / "pyproject.toml").read_text(encoding="utf-8") - assert '"src/arcadedb_native"' in pyproject +def test_the_shim_module_has_its_constants_and_the_body(): + text = ( + DEPRECATION / "arcadedb-embedded" / "src" / "arcadedb_embedded" / "__init__.py" + ).read_text(encoding="utf-8") + assert BODY_MARKER in text + assert '_OLD_DIST = "arcadedb-embedded"\n' in text + assert '_NEW_DIST = "arcadedb"\n' in text + assert '_NEW_MODULE = "arcadedb"\n' in text @pytest.mark.parametrize(("dist", "module"), CASES) @@ -355,14 +330,15 @@ def test_warning_fires_once_and_is_visible_by_default(dist, module, sites): category, message, filename = result["warnings"][0] assert category == "FutureWarning" assert filename == "" # attributed to the importing code, not to the shim - new = SHIMS[dist] - assert f"Install {new!r} instead (`pip install {new}`)" in message - # `pip install arcadedb` must not appear in the JVM shim's text as the command to run (it is the native build) - assert (re.search(r"pip install arcadedb(?![-\w])", message) is not None) == ( - new == "arcadedb" + # plain words: the package is now arcadedb, that is the native build, the JVM build is arcadedb-jvm + assert ( + "The package is now 'arcadedb': install it with `pip install arcadedb`." + in message ) + assert "`arcadedb` is the native build, which runs without a JVM." in message + assert "install `arcadedb-jvm` (`pip install arcadedb-jvm`)" in message + assert WHICH_PACKAGE_URL in message assert f"`import {IMPORT_NAME}` where you wrote `import {module}`" in message - assert ("same JVM build" in message) == (new == "arcadedb-jvm") assert "https://github.com/ArcadeData/arcadedb-python" in message assert "https://docs.arcadedb.com/arcadedb-python/" in message @@ -403,42 +379,6 @@ def test_attributes_and_submodules_come_from_the_new_module(dist, module, sites) assert result["missing_module"] is True -BOTH = """ -import sys, warnings -sys.path.insert(0, sys.argv[1]) -with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always") - import arcadedb_embedded - import arcadedb_native -import arcadedb -# a lazily imported submodule must resolve under each old name, not only under the first one imported -import arcadedb_embedded.extra -import arcadedb_native.extra -assert arcadedb_embedded.extra is arcadedb.extra, "arcadedb_embedded.extra" -assert arcadedb_native.extra is arcadedb.extra, "arcadedb_native.extra" -from arcadedb_native.sub import deep -assert deep is arcadedb.sub.deep -assert [w.category.__name__ for w in caught] == ["FutureWarning", "FutureWarning"], caught -assert [str(w.message).rsplit("`", 2)[-2] for w in caught] == [ - "import arcadedb_embedded", - "import arcadedb_native", -] -print("ok") -""" - - -def test_the_native_shim_serves_both_old_names_in_one_process(sites): - done = subprocess.run( # nosec B603 - fixed argument list, no shell - [sys.executable, "-I", "-c", BOTH, str(sites["arcadedb-embedded-native"])], - capture_output=True, - text=True, - timeout=120, - check=False, - ) - assert done.returncode == 0, done.stderr - assert done.stdout.strip() == "ok" - - def test_the_real_package_has_no_warning(): """The benchmark harness imports `arcadedb_embedded` from the real package until the split: it must stay quiet.""" source = (BINDINGS / "src" / "arcadedb_embedded" / "__init__.py").read_text( From 481b53d60dffbddff142167833290a059373398e Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 21:23:38 +0900 Subject: [PATCH 7/8] Shim v3 follow-up: forward only the extras the native arcadedb defines, say the runtime change plainly, note that arcadedb-embedded-native is archived - build_shims.py: NATIVE_EXTRAS is the one place that lists the extras of the real native `arcadedb` wheel (numpy, arrow, pandas, polars; source ArcadeData/arcadedb-python, branch native-two-dist, commit 56aab39, native/wheel/pyproject.toml, not the root pyproject.toml that builds arcadedb-jvm). check_target() fails when the shim forwards anything else, or when EXTRAS no longer covers that set, and when the warning stops naming `pip install arcadedb-jvm`. JVM_DIST names the JVM package; the comments describe layout v3. - The FutureWarning, the PyPI README and the project description now say plainly that this package was the JVM build and `arcadedb` is the native build, so the runtime changes for pip users of the old name; `arcadedb-jvm` (same `import arcadedb`) is the way to stay on the JVM build. - MIGRATION.md, deprecation/README.md, the shim README, the root README and release.md: `arcadedb-embedded-native` is archived (no further releases, no shim, no files on PyPI today), install `arcadedb`, whose import name is `arcadedb`, not `arcadedb_native`. The old test, dev and examples extras do not exist in `arcadedb`. - Tests (14): the forwarded extras equal the native set, a table or shim that forwards to an extra the native wheel lacks (or a native wheel that gains one) stops check_target(), the runtime-change sentence is in the warning and the README, the archived note is in the migration texts. Co-Authored-By: Claude Sonnet 5.5 --- bindings/python/deprecation/MIGRATION.md | 6 +-- bindings/python/deprecation/README.md | 4 +- .../deprecation/arcadedb-embedded/README.md | 6 ++- .../arcadedb-embedded/pyproject.toml | 4 +- .../src/arcadedb_embedded/__init__.py | 3 +- bindings/python/deprecation/build_shims.py | 36 ++++++++++--- bindings/python/docs/development/release.md | 2 +- .../python/tests/test_deprecation_shims.py | 52 +++++++++++++++++-- 8 files changed, 93 insertions(+), 20 deletions(-) diff --git a/bindings/python/deprecation/MIGRATION.md b/bindings/python/deprecation/MIGRATION.md index ca5c4a7dde9..86c45201653 100644 --- a/bindings/python/deprecation/MIGRATION.md +++ b/bindings/python/deprecation/MIGRATION.md @@ -2,7 +2,7 @@ The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python), under new PyPI names: `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build). `arcadedb-embedded` was the JVM package. Its last release is a shim that installs `arcadedb`, the native build. If you need the JVM build, install `arcadedb-jvm` yourself. This repository is archived after the move. Documentation: [docs.arcadedb.com/arcadedb-python](https://docs.arcadedb.com/arcadedb-python/). To decide between the two packages, read [Which package?](https://docs.arcadedb.com/arcadedb-python/which-package.html). -`arcadedb-embedded-native` (the native preview package) has no shim and no new release. Its PyPI project has no files and is archived. If you used a preview of it from another source, install `arcadedb`. +`arcadedb-embedded-native` (the native preview package) is archived: no further releases and no shim. No files of it remain on PyPI today (its simple index lists none). Install `arcadedb` instead: it is the native build, and its import name is `arcadedb`, not `arcadedb_native`. ## Install and import @@ -15,7 +15,7 @@ The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [A | `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | `from arcadedb import open_database` | | `arcadedb_embedded.core`, `arcadedb_embedded.jvm`, ... | `arcadedb.core`, ... | `arcadedb.core`, `arcadedb.jvm`, ... | -The package for `arcadedb-embedded` users is now `arcadedb`, which is the native build: the engine runs as a native library, without a JVM. It is a different runtime from the one you had. If your code needs the JVM build, for example because it uses Java classes through JPype, install `arcadedb-jvm`: it is the same JVM build as `arcadedb-embedded`, so nothing changes at run time. The extra `vector` of `arcadedb-embedded` (numpy) is the extra `numpy` of `arcadedb`; `arrow`, `pandas` and `polars` keep their names. +The package for `arcadedb-embedded` users is now `arcadedb`, which is the native build: the engine runs as a native library, without a JVM. It is a different runtime from the one you had. If your code needs the JVM build, for example because it uses Java classes through JPype, install `arcadedb-jvm`: it is the same JVM build as `arcadedb-embedded`, so nothing changes at run time. The extra `vector` of `arcadedb-embedded` (numpy) is the extra `numpy` of `arcadedb`; `arrow`, `pandas` and `polars` keep their names. `arcadedb` defines no other extras: the old `test`, `dev` and `examples` do not exist there. Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-jvm`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded`. @@ -26,7 +26,7 @@ From 26.11.1 the package version is the ArcadeDB engine version, as before. | Old | Last release | New | |---|---|---| | `arcadedb-embedded` (JVM, bundled JRE) | 26.10.2.post1 on PyPI, then the shim 26.11.1 | `arcadedb` 26.11.1 and later (native), or `arcadedb-jvm` 26.11.1 and later (JVM) | -| `arcadedb-embedded-native` (preview) | 26.10.2.dev1 (no files on PyPI), no shim | `arcadedb` 26.11.1 and later | +| `arcadedb-embedded-native` (preview, archived) | no further releases, no shim | `arcadedb` 26.11.1 and later | The 26.11.1 release of `arcadedb-embedded` is a shim and the last one. It installs `arcadedb` at exactly 26.11.1. Importing `arcadedb_embedded` then emits one `FutureWarning` and re-exports `arcadedb`, submodules included, so unchanged code keeps running until you edit the import. The warning says that the package is now `arcadedb`, that this is the native build, and that `arcadedb-jvm` is the JVM build. The shim never upgrades: for newer versions install `arcadedb` or `arcadedb-jvm`. Older releases of the old name stay on PyPI for pinned installs. The shim is published only after the native `arcadedb` 26.11.1 final release exists. diff --git a/bindings/python/deprecation/README.md b/bindings/python/deprecation/README.md index d2b6b935b17..079286860d3 100644 --- a/bindings/python/deprecation/README.md +++ b/bindings/python/deprecation/README.md @@ -2,13 +2,13 @@ The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python) as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build), both importing as `arcadedb`. This folder holds the last release of the old name `arcadedb-embedded`: a pure-Python shim that depends on `arcadedb` at the same version, warns once, and re-exports it. The old JVM package therefore moves to the native build, on purpose (decided 2026-10-25). The warning and the README say so in plain words, name `arcadedb-jvm` as the way to keep the JVM build, and link the Which package? page of the new documentation. Users: see [MIGRATION.md](MIGRATION.md). -`arcadedb-embedded-native` (the native preview package) gets no shim. Its PyPI project has no files and is archived, so there is nothing to redirect. +`arcadedb-embedded-native` (the native preview package) is archived: no further releases and no shim. No files of it remain on PyPI today, so there is nothing to redirect; the shim README and MIGRATION.md tell people to install `arcadedb` (import name `arcadedb`, not `arcadedb_native`). | Folder | PyPI project | Depends on | Version | |---|---|---|---| | `arcadedb-embedded/` | `arcadedb-embedded` (old JVM package) | `arcadedb==26.11.1` (the native build) | 26.11.1 | -The shim module is `arcadedb_embedded`. The mapping above lives in one place, `TARGETS` in `build_shims.py`, together with the one assumption behind it: `arcadedb` provides the import name `arcadedb` (the layout says so, but it has no real release yet). `EXTRAS` there maps the extras of the old package to the extras of `arcadedb` (`vector`, the numpy extra, becomes `numpy`; `arcadedb` has no `vector`). `build_shims.py` refuses to build when the shim's `pyproject.toml` (name, description, dependencies, extras, `Replaced by` URL) or its `_NEW_DIST` and `_NEW_MODULE` constants disagree with `TARGETS`. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. +The shim module is `arcadedb_embedded`. The mapping above lives in one place, `TARGETS` in `build_shims.py`, together with the one assumption behind it: `arcadedb` provides the import name `arcadedb` (the layout says so, but it has no real release yet). `EXTRAS` there maps the extras of the old package to the extras of `arcadedb` (`vector`, the numpy extra, becomes `numpy`; `arcadedb` has no `vector`), and `NATIVE_EXTRAS` is the set the native `arcadedb` wheel defines (`numpy`, `arrow`, `pandas`, `polars`; source: ArcadeData/arcadedb-python, branch `native-two-dist`, `native/wheel/pyproject.toml`). `build_shims.py` fails when the shim forwards anything else, so re-read that file when the native wheel's extras change. `build_shims.py` refuses to build when the shim's `pyproject.toml` (name, description, dependencies, extras, `Replaced by` URL) or its `_NEW_DIST` and `_NEW_MODULE` constants disagree with `TARGETS`. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. **This shim publishes only after the native `arcadedb` 26.11.1 FINAL exists.** It pins `arcadedb==26.11.1`, and a `26.11.1.devN` release does not satisfy that pin. Until the final release is out, do not publish this shim: `pip install arcadedb-embedded` would fail to resolve. diff --git a/bindings/python/deprecation/arcadedb-embedded/README.md b/bindings/python/deprecation/arcadedb-embedded/README.md index e6417217563..88865e826e9 100644 --- a/bindings/python/deprecation/arcadedb-embedded/README.md +++ b/bindings/python/deprecation/arcadedb-embedded/README.md @@ -2,6 +2,8 @@ **This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. The package is now [`arcadedb`](https://pypi.org/project/arcadedb/), which is the **native build**: the engine runs as a native library, without a JVM. If you need the JVM build, install [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) instead. +**The runtime changes for pip users of the old name.** `arcadedb-embedded` was the JVM build; `pip install arcadedb` gives the native build. Both new packages are imported as `arcadedb`. + | | Old | New | |---|---|---| | PyPI package | `arcadedb-embedded` | [`arcadedb`](https://pypi.org/project/arcadedb/) (native build) or [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) (JVM build) | @@ -31,6 +33,8 @@ pip install arcadedb-jvm It is a shim. It installs `arcadedb==26.11.1`, and `import arcadedb_embedded` emits one `FutureWarning` and then gives you the `arcadedb` module, submodules included, so existing code keeps running. It is pinned to `arcadedb==26.11.1` and gets no updates: to receive newer ArcadeDB versions, switch to `arcadedb` (or `arcadedb-jvm`). -The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to `arcadedb`. The old `vector` extra (numpy) maps to the `numpy` extra of `arcadedb`. +The extras `vector`, `arrow`, `pandas` and `polars` are forwarded to `arcadedb`. The old `vector` extra (numpy) maps to the `numpy` extra of `arcadedb`, which defines `numpy`, `arrow`, `pandas` and `polars`; the old `test`, `dev` and `examples` extras do not exist there. + +The sibling package `arcadedb-embedded-native` (the native preview) is archived: no further releases, no shim. Install `arcadedb` instead; its import name is `arcadedb`, not `arcadedb_native`. Older `arcadedb-embedded` releases stay on PyPI for pinned installs. For the full old-to-new table, see the [migration guide](https://github.com/humemai/arcadedb-embedded-python/blob/main/bindings/python/deprecation/MIGRATION.md). diff --git a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml index 51202b3334e..819b54eff4e 100644 --- a/bindings/python/deprecation/arcadedb-embedded/pyproject.toml +++ b/bindings/python/deprecation/arcadedb-embedded/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "arcadedb-embedded" version = "26.11.1" -description = "Deprecated: arcadedb-embedded has moved to arcadedb (import arcadedb). Last release, a thin shim." +description = "Deprecated: arcadedb-embedded has moved to arcadedb (import arcadedb), now the native build, not the JVM build; for the JVM build use arcadedb-jvm. Last release, a thin shim." readme = "README.md" requires-python = ">=3.11" license = "Apache-2.0" @@ -26,7 +26,7 @@ classifiers = [ # The shim installs exactly the package it points to, at the same version. dependencies = ["arcadedb==26.11.1"] -# The extras people installed from the old name, forwarded to the new one (the old `vector` extra was numpy; `arcadedb` has no `vector`). +# The extras people installed from the old name, forwarded to the extras of `arcadedb` (the native wheel defines numpy, arrow, pandas and polars; the old `vector` extra was numpy). build_shims.py checks this block. [project.optional-dependencies] vector = ["arcadedb[numpy]==26.11.1"] arrow = ["arcadedb[arrow]==26.11.1"] diff --git a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py index 1e337866e6c..a63c592259e 100644 --- a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py @@ -11,7 +11,8 @@ _NEW_MODULE = "arcadedb" _NOTE = ( "`arcadedb` is the native build, which runs without a JVM. " - "If you need the JVM build, install `arcadedb-jvm` (`pip install arcadedb-jvm`). " + "This package was the JVM build, so for you the runtime changes. " + "If you need the JVM build, install `arcadedb-jvm` (`pip install arcadedb-jvm`; it is also `import arcadedb`). " "Which package to pick: https://docs.arcadedb.com/arcadedb-python/which-package.html." ) # ---- body (tests/test_deprecation_shims.py checks that it is there) ---- diff --git a/bindings/python/deprecation/build_shims.py b/bindings/python/deprecation/build_shims.py index facd51ef148..00b87510adf 100644 --- a/bindings/python/deprecation/build_shims.py +++ b/bindings/python/deprecation/build_shims.py @@ -25,6 +25,7 @@ import argparse import importlib.util import os +import re import shutil import subprocess # nosec B404 - runs `uv build` with a fixed argument list import sys @@ -46,17 +47,20 @@ class Target(NamedTuple): module: str # the import name that distribution provides: what the shim re-exports -# Where each old name points (the packaging layout decided on 2026-10-25, "layout v2" in ArcadeData/arcadedb-python .notes/MIGRATION.md, -# and the decision of 2026-10-25 that the old JVM package moves to the native build): -# arcadedb-embedded (the old JVM package) -> arcadedb, the NATIVE build. The warning and the README say so in plain words and name -# `arcadedb-jvm` as the way to keep the JVM build. This shim therefore publishes only after the native `arcadedb` 26.11.1 FINAL exists. -# ASSUMPTION: `arcadedb` provides the import name `arcadedb`. The layout says "import arcadedb for all". `arcadedb` has no real release on PyPI yet (0.0.1 is a placeholder), so this cannot be checked here. +# Where each old name points (the packaging layout v3 decided on 2026-10-25, see ArcadeData/arcadedb-python .notes/MIGRATION.md). +# arcadedb-embedded (the old JVM package) -> `arcadedb`, which is the NATIVE build. The runtime therefore changes for pip users of the old name, and the warning, the README and MIGRATION.md say so in plain words and name `arcadedb-jvm` as the way to keep the JVM build. +# This shim publishes only after the native `arcadedb` 26.11.1 FINAL exists, because it pins `arcadedb==26.11.1` and a `26.11.1.devN` release does not satisfy that pin. +# ASSUMPTION: `arcadedb` provides the import name `arcadedb`. The native wheel does (packages.find and package-data in native/wheel/pyproject.toml on the branch named below), and `arcadedb-jvm` imports as `arcadedb` too, but `arcadedb` has no final release on PyPI yet (0.0.1 is a placeholder), so the published wheel cannot be checked here. # If the package gets another import name, change it here and in the `_NEW_MODULE` constant of the shim's `__init__.py`; check_target() fails until both agree. IMPORT_NAME = "arcadedb" TARGETS = { "arcadedb-embedded": Target(dist="arcadedb", module=IMPORT_NAME), } -# the extras people installed from the old name -> the extra of the target they are forwarded to (the old `vector` extra was numpy; `arcadedb` has no `vector`) +# The JVM build the warning and the README name for people who need it (not a dependency of the shim). +JVM_DIST = "arcadedb-jvm" +# The extras of the real `arcadedb` (native) distribution, and nothing else may be forwarded to. Source: ArcadeData/arcadedb-python, branch native-two-dist (commit 56aab39), file native/wheel/pyproject.toml, [project.optional-dependencies]. The root pyproject.toml of that repo builds `arcadedb-jvm` instead (extras test, dev, vector, arrow, pandas, polars, examples) and is NOT the source. +NATIVE_EXTRAS = frozenset({"numpy", "arrow", "pandas", "polars"}) +# The extras people installed from the old name -> the extra of `arcadedb` they are forwarded to (the old `vector` extra was numpy; `arcadedb` has no `vector`). The old test, dev and examples extras are not forwarded: `arcadedb` does not define them. EXTRAS = {"vector": "numpy", "arrow": "arrow", "pandas": "pandas", "polars": "polars"} @@ -84,7 +88,7 @@ def check_target(shim: str) -> None: project = tomllib.load(handle)["project"] expected = { "name": shim, - "description": f"Deprecated: {shim} has moved to {target.dist} (import {target.module}). Last release, a thin shim.", + "description": f"Deprecated: {shim} has moved to {target.dist} (import {target.module}), now the native build, not the JVM build; for the JVM build use {JVM_DIST}. Last release, a thin shim.", "dependencies": [f"{target.dist}=={version}"], "optional-dependencies": { old: [f"{target.dist}[{new}]=={version}"] for old, new in EXTRAS.items() @@ -95,6 +99,20 @@ def check_target(shim: str) -> None: for key, want in expected.items() if project.get(key) != want ] + forwarded = { + match.group(1) + for requirements in project.get("optional-dependencies", {}).values() + for requirement in requirements + if ( + match := re.fullmatch( + rf"{re.escape(target.dist)}\[(\w+)\]==.+", requirement + ) + ) + } + if forwarded != NATIVE_EXTRAS or set(EXTRAS.values()) != NATIVE_EXTRAS: + problems.append( + f"the shim forwards the extras {sorted(forwarded)} (EXTRAS: {sorted(set(EXTRAS.values()))}), but {target.dist} defines {sorted(NATIVE_EXTRAS)} (NATIVE_EXTRAS)" + ) replaced_by = project["urls"].get("Replaced by") if replaced_by != f"https://pypi.org/project/{target.dist}/": problems.append(f"project.urls['Replaced by'] is {replaced_by!r}") @@ -107,6 +125,10 @@ def check_target(shim: str) -> None: ): if f'{name} = "{want}"\n' not in text: problems.append(f'{path.relative_to(HERE)}: {name} is not "{want}"') + if f"`pip install {JVM_DIST}`" not in text: + problems.append( + f"{path.relative_to(HERE)}: the warning does not name `pip install {JVM_DIST}`" + ) if problems: raise SystemExit( f"{shim} does not point at {target.dist}:\n " + "\n ".join(problems) diff --git a/bindings/python/docs/development/release.md b/bindings/python/docs/development/release.md index 0eda370355e..b7fd90ae754 100644 --- a/bindings/python/docs/development/release.md +++ b/bindings/python/docs/development/release.md @@ -383,7 +383,7 @@ uv run --project .. --group docs mike alias --update-aliases \ ## The Last Releases: Deprecation Shims -The bindings move to ArcadeData/arcadedb-python as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build). The old name `arcadedb-embedded` ends with one shim release, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that warns once and depends on `arcadedb`, the native build, at the same version; the warning names `arcadedb-jvm` for the JVM build). It publishes only after the native `arcadedb` 26.11.1 FINAL exists. `arcadedb-embedded-native` gets no shim: its PyPI project has no files and is archived. They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. +The bindings move to ArcadeData/arcadedb-python as `arcadedb` (the native build) and `arcadedb-jvm` (the JVM build). The old name `arcadedb-embedded` ends with one shim release, 26.11.1, from `bindings/python/deprecation/` (a pure-Python wheel that warns once and depends on `arcadedb`, the native build, at the same version; the warning names `arcadedb-jvm` for the JVM build). It publishes only after the native `arcadedb` 26.11.1 FINAL exists. `arcadedb-embedded-native` is archived: no further releases and no shim, and its PyPI project has no files. They are not released with the steps above: never push a `26.11.1` tag in this repository, because the release workflow would build and publish the real wheels. The cutover order and the publish options are in `bindings/python/deprecation/README.md`. ## Troubleshooting diff --git a/bindings/python/tests/test_deprecation_shims.py b/bindings/python/tests/test_deprecation_shims.py index b2036db622a..55548e8b162 100644 --- a/bindings/python/tests/test_deprecation_shims.py +++ b/bindings/python/tests/test_deprecation_shims.py @@ -41,6 +41,8 @@ BODY_MARKER = "# ---- body" # the extras of the old package -> the extra of `arcadedb` they are forwarded to (`vector` was numpy; `arcadedb` has no `vector`) EXTRAS = {"arrow": "arrow", "pandas": "pandas", "polars": "polars", "vector": "numpy"} +# the extras the native `arcadedb` wheel defines (ArcadeData/arcadedb-python, branch native-two-dist, native/wheel/pyproject.toml); written out here on purpose, apart from build_shims.NATIVE_EXTRAS +NATIVE_EXTRAS = {"numpy", "arrow", "pandas", "polars"} STUB_FILES = { "arcadedb/__init__.py": """ @@ -230,7 +232,7 @@ def test_the_shim_requires_arcadedb_the_native_build_and_not_arcadedb_jvm(wheels assert _required_names(metadata) == {"arcadedb"} assert "arcadedb-jvm" not in _required_names(metadata) assert "arcadedb-native" not in _required_names(metadata) - assert "arcadedb-embedded-native" not in metadata + assert "arcadedb-embedded-native" not in _required_names(metadata) # the extras of the old package are forwarded to the matching extras of arcadedb assert sorted( re.findall(r"^Provides-Extra: (.+)$", metadata, flags=re.MULTILINE) @@ -239,8 +241,14 @@ def test_the_shim_requires_arcadedb_the_native_build_and_not_arcadedb_jvm(wheels assert ( f"Requires-Dist: arcadedb[{new}]=={VERSION}; extra == '{old}'\n" in metadata ) + # only extras the native arcadedb defines are forwarded to: no vector, test, dev or examples + forwarded = set( + re.findall(r"^Requires-Dist: arcadedb\[(\w+)\]==", metadata, flags=re.MULTILINE) + ) + assert forwarded == NATIVE_EXTRAS assert "Project-URL: Replaced by, https://pypi.org/project/arcadedb/\n" in metadata - assert "has moved to arcadedb (import arcadedb)" in metadata + assert "has moved to arcadedb (import arcadedb), now the native build" in metadata + assert "for the JVM build use arcadedb-jvm" in metadata def test_the_pypi_readme_says_arcadedb_is_the_native_build_and_names_the_jvm_package( @@ -253,6 +261,18 @@ def test_the_pypi_readme_says_arcadedb_is_the_native_build_and_names_the_jvm_pac # the JVM build is named as the other choice, with the install line, and the Which package? page is linked assert "pip install arcadedb-jvm\n" in metadata assert WHICH_PACKAGE_URL in metadata + # the runtime change is said plainly, and the archived sibling is named with its import name + assert "The runtime changes for pip users of the old name." in metadata + assert "`arcadedb-embedded-native` (the native preview) is archived" in metadata + assert "not `arcadedb_native`" in metadata + + +def test_the_migration_texts_say_arcadedb_embedded_native_is_archived(): + for text in ("MIGRATION.md", "README.md"): + doc = (DEPRECATION / text).read_text(encoding="utf-8") + assert "`arcadedb-embedded-native`" in doc and "archived" in doc, text + assert "no further releases" in doc and "no shim" in doc, text + assert "not `arcadedb_native`" in doc, text def test_there_is_no_shim_for_the_native_preview_package(): @@ -272,6 +292,9 @@ def test_build_shims_has_one_table_with_the_decided_mapping(): assert {k: v.dist for k, v in build_shims.TARGETS.items()} == SHIMS assert {v.module for v in build_shims.TARGETS.values()} == {IMPORT_NAME} assert dict(build_shims.EXTRAS) == EXTRAS + assert set(build_shims.NATIVE_EXTRAS) == NATIVE_EXTRAS + assert set(build_shims.EXTRAS.values()) == NATIVE_EXTRAS + assert build_shims.JVM_DIST == "arcadedb-jvm" for dist in SHIMS: build_shims.check_target( dist @@ -301,6 +324,28 @@ def test_build_shims_refuses_a_shim_that_points_elsewhere(dist, monkeypatch): assert "_NEW_MODULE" in str(refused.value) +@pytest.mark.parametrize("dist", SHIMS) +def test_build_shims_refuses_extras_that_the_native_arcadedb_does_not_define( + dist, monkeypatch +): + """The shim may forward only to extras of the native `arcadedb` wheel: a drift either way stops the build.""" + build_shims = _load_build_shims() + # (a) the table forwards `vector` to an extra that `arcadedb` does not have + monkeypatch.setitem(build_shims.EXTRAS, "vector", "vector") + with pytest.raises(SystemExit) as refused: + build_shims.check_target(dist) + assert "NATIVE_EXTRAS" in str(refused.value) + monkeypatch.setitem(build_shims.EXTRAS, "vector", "numpy") + build_shims.check_target(dist) + # (b) the native wheel gains an extra that the shim does not forward yet + monkeypatch.setattr( + build_shims, "NATIVE_EXTRAS", frozenset(NATIVE_EXTRAS | {"parquet"}) + ) + with pytest.raises(SystemExit) as refused: + build_shims.check_target(dist) + assert "defines" in str(refused.value) and "parquet" in str(refused.value) + + @pytest.mark.parametrize("dist", SHIMS) def test_version_follows_the_counting_rule(dist): cv = _load_check_version() @@ -336,7 +381,8 @@ def test_warning_fires_once_and_is_visible_by_default(dist, module, sites): in message ) assert "`arcadedb` is the native build, which runs without a JVM." in message - assert "install `arcadedb-jvm` (`pip install arcadedb-jvm`)" in message + assert "This package was the JVM build, so for you the runtime changes." in message + assert "install `arcadedb-jvm` (`pip install arcadedb-jvm`;" in message assert WHICH_PACKAGE_URL in message assert f"`import {IMPORT_NAME}` where you wrote `import {module}`" in message assert "https://github.com/ArcadeData/arcadedb-python" in message From e6e7b3aca010518dcf8dd3aab16db656e1c7081c Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Thu, 5 Nov 2026 15:14:51 +0900 Subject: [PATCH 8/8] Shim text: uv add / uv remove instead of pip, in the warning, the PyPI README and MIGRATION.md User-facing text says uv add, never pip install (the project rule). The warning every remaining arcadedb-embedded user sees, the shim's PyPI README and MIGRATION.md now say uv add arcadedb, uv add arcadedb-jvm and uv remove arcadedb-embedded; the maintainer steps check PyPI with curl and verify the shim with uv run --with. build_shims.py's check and the tests follow the new wording. Co-Authored-By: Claude Opus 5.5 --- bindings/python/deprecation/MIGRATION.md | 8 ++++---- bindings/python/deprecation/README.md | 6 +++--- .../python/deprecation/arcadedb-embedded/README.md | 10 +++++----- .../src/arcadedb_embedded/__init__.py | 4 ++-- bindings/python/deprecation/build_shims.py | 6 +++--- bindings/python/tests/test_deprecation_shims.py | 13 ++++++------- 6 files changed, 23 insertions(+), 24 deletions(-) diff --git a/bindings/python/deprecation/MIGRATION.md b/bindings/python/deprecation/MIGRATION.md index 86c45201653..c5cee5bf133 100644 --- a/bindings/python/deprecation/MIGRATION.md +++ b/bindings/python/deprecation/MIGRATION.md @@ -8,8 +8,8 @@ The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [A | Before | After (native build) | After (JVM build) | |---|---|---| -| `pip install arcadedb-embedded` | `pip install arcadedb` | `pip install arcadedb-jvm` | -| `pip install 'arcadedb-embedded[arrow,pandas]'` | `pip install 'arcadedb[arrow,pandas]'` | `pip install 'arcadedb-jvm[arrow,pandas]'` | +| `uv add arcadedb-embedded` | `uv add arcadedb` | `uv add arcadedb-jvm` | +| `uv add 'arcadedb-embedded[arrow,pandas]'` | `uv add 'arcadedb[arrow,pandas]'` | `uv add 'arcadedb-jvm[arrow,pandas]'` | | `uv add arcadedb-embedded` | `uv add arcadedb` | `uv add arcadedb-jvm` | | `import arcadedb_embedded` | `import arcadedb` | `import arcadedb` | | `from arcadedb_embedded import open_database` | `from arcadedb import open_database` | `from arcadedb import open_database` | @@ -17,7 +17,7 @@ The ArcadeDB Python bindings moved from `humemai/arcadedb-embedded-python` to [A The package for `arcadedb-embedded` users is now `arcadedb`, which is the native build: the engine runs as a native library, without a JVM. It is a different runtime from the one you had. If your code needs the JVM build, for example because it uses Java classes through JPype, install `arcadedb-jvm`: it is the same JVM build as `arcadedb-embedded`, so nothing changes at run time. The extra `vector` of `arcadedb-embedded` (numpy) is the extra `numpy` of `arcadedb`; `arrow`, `pandas` and `polars` keep their names. `arcadedb` defines no other extras: the old `test`, `dev` and `examples` do not exist there. -Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-jvm`, not both. Uninstall the old package first: `pip uninstall arcadedb-embedded`. +Both new packages provide the module `arcadedb`, so an environment holds `arcadedb` or `arcadedb-jvm`, not both. Remove the old package first: `uv remove arcadedb-embedded`. ## Version mapping @@ -40,6 +40,6 @@ The 26.11.1 release of `arcadedb-embedded` is a shim and the last one. It instal ## What changes - The distribution and import names, as above. -- The default runtime: `pip install arcadedb` is the native build, not the JVM build. +- The default runtime: `uv add arcadedb` installs the native build, not the JVM build. - Issues and pull requests go to [ArcadeData/arcadedb-python](https://github.com/ArcadeData/arcadedb-python/issues). Benchmarks stay with HumemAI, in [humemai/dbbench](https://github.com/humemai/dbbench). - Engine changes between 26.10.2 and 26.11.1: read the 26.11.1 release notes, as for any upgrade. diff --git a/bindings/python/deprecation/README.md b/bindings/python/deprecation/README.md index 079286860d3..17c765d1329 100644 --- a/bindings/python/deprecation/README.md +++ b/bindings/python/deprecation/README.md @@ -10,7 +10,7 @@ The Python bindings move to [ArcadeData/arcadedb-python](https://github.com/Arca The shim module is `arcadedb_embedded`. The mapping above lives in one place, `TARGETS` in `build_shims.py`, together with the one assumption behind it: `arcadedb` provides the import name `arcadedb` (the layout says so, but it has no real release yet). `EXTRAS` there maps the extras of the old package to the extras of `arcadedb` (`vector`, the numpy extra, becomes `numpy`; `arcadedb` has no `vector`), and `NATIVE_EXTRAS` is the set the native `arcadedb` wheel defines (`numpy`, `arrow`, `pandas`, `polars`; source: ArcadeData/arcadedb-python, branch `native-two-dist`, `native/wheel/pyproject.toml`). `build_shims.py` fails when the shim forwards anything else, so re-read that file when the native wheel's extras change. `build_shims.py` refuses to build when the shim's `pyproject.toml` (name, description, dependencies, extras, `Replaced by` URL) or its `_NEW_DIST` and `_NEW_MODULE` constants disagree with `TARGETS`. The real package in `bindings/python/src` is not changed and does not warn: the benchmark harness imports it until the split. -**This shim publishes only after the native `arcadedb` 26.11.1 FINAL exists.** It pins `arcadedb==26.11.1`, and a `26.11.1.devN` release does not satisfy that pin. Until the final release is out, do not publish this shim: `pip install arcadedb-embedded` would fail to resolve. +**This shim publishes only after the native `arcadedb` 26.11.1 FINAL exists.** It pins `arcadedb==26.11.1`, and a `26.11.1.devN` release does not satisfy that pin. Until the final release is out, do not publish this shim: `uv add arcadedb-embedded` would fail to resolve. ## Build and test (never uploads) @@ -23,11 +23,11 @@ taskset -c 0-3 uv run pytest bindings/python/tests/test_deprecation_shims.py # ## Cutover day -1. Release the native `arcadedb` 26.11.1 FINAL from ArcadeData/arcadedb-python first (and `arcadedb-jvm` 26.11.1, which the warning names). Check `pip download --no-deps arcadedb==26.11.1` works: the shim pins this exact version. If the first final `arcadedb` is not out (only `26.11.1.devN`), do not publish the shim. +1. Release the native `arcadedb` 26.11.1 FINAL from ArcadeData/arcadedb-python first (and `arcadedb-jvm` 26.11.1, which the warning names). Check that PyPI serves it (`curl -sf https://pypi.org/pypi/arcadedb/26.11.1/json` succeeds): the shim pins this exact version. If the first final `arcadedb` is not out (only `26.11.1.devN`), do not publish the shim. 2. Never push a `26.11.1` tag in this repository: `release-python-packages.yml` would build and publish the real wheels under `arcadedb-embedded`. 3. Build and check: `python3 bindings/python/deprecation/build_shims.py --out dist --check-pypi && uvx twine check dist/*`. 4. Publish the shim. Trusted publishing works only from a workflow, so either (a) register a trusted publisher on the PyPI project `arcadedb-embedded` (repository `humemai/arcadedb-embedded-python`, workflow `publish-deprecation-shims.yml`, environment `pypi`), add the workflow below to `.github/workflows/` (and to `FORK_GITHUB_ALLOWLIST` in `sync-upstream.sh` if another sync will still run), and dispatch it with `publish` on; or (b) upload from a laptop with `uvx twine upload dist/*` and a project-scoped API token. -5. In a clean environment: `pip install arcadedb-embedded==26.11.1` installs `arcadedb` (the native build, not `arcadedb-jvm`: check `pip list`), and `python -W default -c "import arcadedb_embedded"` shows one FutureWarning that names `arcadedb-jvm` for the JVM build. +5. In a clean environment, `uv run --no-project --with arcadedb-embedded==26.11.1 python -W default -c "import arcadedb_embedded, importlib.metadata as m; print(m.version('arcadedb'))"` installs `arcadedb` (the native build, not `arcadedb-jvm`: it prints `26.11.1`) and shows one FutureWarning that names `arcadedb-jvm` for the JVM build. ```yaml name: Publish deprecation shims diff --git a/bindings/python/deprecation/arcadedb-embedded/README.md b/bindings/python/deprecation/arcadedb-embedded/README.md index 88865e826e9..9b80aa23171 100644 --- a/bindings/python/deprecation/arcadedb-embedded/README.md +++ b/bindings/python/deprecation/arcadedb-embedded/README.md @@ -2,7 +2,7 @@ **This package is deprecated. 26.11.1 is its last release.** The ArcadeDB Python bindings now live in the ArcadeData organization, under a new package name. The package is now [`arcadedb`](https://pypi.org/project/arcadedb/), which is the **native build**: the engine runs as a native library, without a JVM. If you need the JVM build, install [`arcadedb-jvm`](https://pypi.org/project/arcadedb-jvm/) instead. -**The runtime changes for pip users of the old name.** `arcadedb-embedded` was the JVM build; `pip install arcadedb` gives the native build. Both new packages are imported as `arcadedb`. +**The runtime changes for users of the old name.** `arcadedb-embedded` was the JVM build; `arcadedb` is the native build. Both new packages are imported as `arcadedb`. | | Old | New | |---|---|---| @@ -14,8 +14,8 @@ ## What to do ```bash -pip uninstall arcadedb-embedded -pip install arcadedb +uv remove arcadedb-embedded +uv add arcadedb ``` Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_embedded import X` to `from arcadedb import X`). @@ -23,8 +23,8 @@ Then change `import arcadedb_embedded` to `import arcadedb` (and `from arcadedb_ `arcadedb-embedded` was the JVM build, so `arcadedb` is a different runtime. If your code needs the JVM build, for example because it uses Java classes through JPype or a long-running load that suits the JVM, install it instead: ```bash -pip uninstall arcadedb-embedded -pip install arcadedb-jvm +uv remove arcadedb-embedded +uv add arcadedb-jvm ``` [Which package?](https://docs.arcadedb.com/arcadedb-python/which-package.html) explains the difference. `arcadedb` and `arcadedb-jvm` both provide the module `arcadedb`: install one of them in an environment, not both. diff --git a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py index a63c592259e..4f143dae00b 100644 --- a/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py +++ b/bindings/python/deprecation/arcadedb-embedded/src/arcadedb_embedded/__init__.py @@ -12,7 +12,7 @@ _NOTE = ( "`arcadedb` is the native build, which runs without a JVM. " "This package was the JVM build, so for you the runtime changes. " - "If you need the JVM build, install `arcadedb-jvm` (`pip install arcadedb-jvm`; it is also `import arcadedb`). " + "If you need the JVM build, install `arcadedb-jvm` (`uv add arcadedb-jvm`; it is also `import arcadedb`). " "Which package to pick: https://docs.arcadedb.com/arcadedb-python/which-package.html." ) # ---- body (tests/test_deprecation_shims.py checks that it is there) ---- @@ -26,7 +26,7 @@ _MESSAGE = ( f"The {_OLD_DIST!r} package has moved and this is its last release. " - f"The package is now {_NEW_DIST!r}: install it with `pip install {_NEW_DIST}`. {_NOTE} " + f"The package is now {_NEW_DIST!r}: install it with `uv add {_NEW_DIST}`. {_NOTE} " f"Write `import {_NEW_MODULE}` where you wrote `import {__name__}`. " "New home: https://github.com/ArcadeData/arcadedb-python, " "docs: https://docs.arcadedb.com/arcadedb-python/" diff --git a/bindings/python/deprecation/build_shims.py b/bindings/python/deprecation/build_shims.py index 00b87510adf..e534e374485 100644 --- a/bindings/python/deprecation/build_shims.py +++ b/bindings/python/deprecation/build_shims.py @@ -48,7 +48,7 @@ class Target(NamedTuple): # Where each old name points (the packaging layout v3 decided on 2026-10-25, see ArcadeData/arcadedb-python .notes/MIGRATION.md). -# arcadedb-embedded (the old JVM package) -> `arcadedb`, which is the NATIVE build. The runtime therefore changes for pip users of the old name, and the warning, the README and MIGRATION.md say so in plain words and name `arcadedb-jvm` as the way to keep the JVM build. +# arcadedb-embedded (the old JVM package) -> `arcadedb`, which is the NATIVE build. The runtime therefore changes for users of the old name, and the warning, the README and MIGRATION.md say so in plain words and name `arcadedb-jvm` as the way to keep the JVM build. # This shim publishes only after the native `arcadedb` 26.11.1 FINAL exists, because it pins `arcadedb==26.11.1` and a `26.11.1.devN` release does not satisfy that pin. # ASSUMPTION: `arcadedb` provides the import name `arcadedb`. The native wheel does (packages.find and package-data in native/wheel/pyproject.toml on the branch named below), and `arcadedb-jvm` imports as `arcadedb` too, but `arcadedb` has no final release on PyPI yet (0.0.1 is a placeholder), so the published wheel cannot be checked here. # If the package gets another import name, change it here and in the `_NEW_MODULE` constant of the shim's `__init__.py`; check_target() fails until both agree. @@ -125,9 +125,9 @@ def check_target(shim: str) -> None: ): if f'{name} = "{want}"\n' not in text: problems.append(f'{path.relative_to(HERE)}: {name} is not "{want}"') - if f"`pip install {JVM_DIST}`" not in text: + if f"`uv add {JVM_DIST}`" not in text: problems.append( - f"{path.relative_to(HERE)}: the warning does not name `pip install {JVM_DIST}`" + f"{path.relative_to(HERE)}: the warning does not name `uv add {JVM_DIST}`" ) if problems: raise SystemExit( diff --git a/bindings/python/tests/test_deprecation_shims.py b/bindings/python/tests/test_deprecation_shims.py index 55548e8b162..b05ee37e10b 100644 --- a/bindings/python/tests/test_deprecation_shims.py +++ b/bindings/python/tests/test_deprecation_shims.py @@ -255,14 +255,14 @@ def test_the_pypi_readme_says_arcadedb_is_the_native_build_and_names_the_jvm_pac wheels, ): metadata = _metadata(wheels["arcadedb-embedded"]) - assert "pip install arcadedb\n" in metadata - assert "pip uninstall arcadedb-embedded\n" in metadata + assert "uv add arcadedb\n" in metadata + assert "uv remove arcadedb-embedded\n" in metadata assert "native build" in metadata # the JVM build is named as the other choice, with the install line, and the Which package? page is linked - assert "pip install arcadedb-jvm\n" in metadata + assert "uv add arcadedb-jvm\n" in metadata assert WHICH_PACKAGE_URL in metadata # the runtime change is said plainly, and the archived sibling is named with its import name - assert "The runtime changes for pip users of the old name." in metadata + assert "The runtime changes for users of the old name." in metadata assert "`arcadedb-embedded-native` (the native preview) is archived" in metadata assert "not `arcadedb_native`" in metadata @@ -377,12 +377,11 @@ def test_warning_fires_once_and_is_visible_by_default(dist, module, sites): assert filename == "" # attributed to the importing code, not to the shim # plain words: the package is now arcadedb, that is the native build, the JVM build is arcadedb-jvm assert ( - "The package is now 'arcadedb': install it with `pip install arcadedb`." - in message + "The package is now 'arcadedb': install it with `uv add arcadedb`." in message ) assert "`arcadedb` is the native build, which runs without a JVM." in message assert "This package was the JVM build, so for you the runtime changes." in message - assert "install `arcadedb-jvm` (`pip install arcadedb-jvm`;" in message + assert "install `arcadedb-jvm` (`uv add arcadedb-jvm`;" in message assert WHICH_PACKAGE_URL in message assert f"`import {IMPORT_NAME}` where you wrote `import {module}`" in message assert "https://github.com/ArcadeData/arcadedb-python" in message