From 925cb0b1526be6b67800b38f65c1acb104d32559 Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 01:14:57 +0900 Subject: [PATCH 1/3] scripts: codemod for the rename to arcadedb, and the old-name check scripts/rename_to_arcadedb.py moves src/arcadedb_embedded to src/arcadedb and rewrites every use of arcadedb_embedded / arcadedb-embedded (imports, pyproject, build and verify scripts, CI paths and names, tests, examples, README and the Antora pages) without touching Java package names, ARCADEDB_* variables, engine artifact names, history, or the release guard. It is deterministic and idempotent, and prints what it changed by category and by rule. scripts/check_no_embedded_name.py fails when the old name reappears outside an allowlist (docs/superpowers, changelogs, the codemod, issue references of the old repository, the "published as" line). The codemod wires it into pre-commit and tests/test_no_embedded_name.py when it runs, so nothing fails on main before the rename. tests/test_rename_codemod.py runs both on a synthetic repository; it needs no wheel. Co-Authored-By: Claude Sonnet 5.5 --- scripts/check_no_embedded_name.py | 174 ++++++++++ scripts/rename_to_arcadedb.py | 518 ++++++++++++++++++++++++++++++ tests/test_rename_codemod.py | 144 +++++++++ 3 files changed, 836 insertions(+) create mode 100644 scripts/check_no_embedded_name.py create mode 100644 scripts/rename_to_arcadedb.py create mode 100644 tests/test_rename_codemod.py diff --git a/scripts/check_no_embedded_name.py b/scripts/check_no_embedded_name.py new file mode 100644 index 00000000..a756f3c9 --- /dev/null +++ b/scripts/check_no_embedded_name.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +"""Fail when the old package name comes back. + +The distribution is `arcadedb` (JVM wheel) and `arcadedb-native` (native wheel), and both import +as `arcadedb`. The names `arcadedb_embedded` and `arcadedb-embedded` belong to the old PyPI +packages, which get a deprecation shim from the humemai repository, not from this one. This script +scans every tracked (and every untracked, not ignored) text file and every path in the repository +and exits 1 for each place the old name appears outside the allowlist below. + +The generic word "embedded" (embedded mode, embedded database) is fine and is not checked. Only the +package names are: `arcadedb_embedded`, `arcadedb-embedded` (this also catches `arcadedb-embedded-native` +and the old repository name `arcadedb-embedded-python`) and the title `ArcadeDB Embedded Python`. + +What the allowlist lets through, and why: + +* Whole files that are history or that must name the old package to do their job: the plan and spec + documents under docs/superpowers/, a changelog, the rename codemod, this script, and the codemod test. +* References to issues of the old repository, written `humemai/arcadedb-embedded-python#123`. They + point at real threads, so they keep their text. +* The sentence "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." + and lines that say "formerly arcadedb-embedded". +* Any line that carries the marker `allow-old-name`, for the rare mention that needs it. + +usage: check_no_embedded_name.py [--root DIR] + +Exit 0 when clean, 1 when a violation is found. Output is ASCII only. +""" + +from __future__ import annotations + +import argparse +import fnmatch +import re +import subprocess # nosec B404 - runs git to list files +import sys +from pathlib import Path + +# The old names. `arcadedb-embedded` also matches inside `arcadedb-embedded-native` and +# `arcadedb-embedded-python`; those are old names too. +OLD_NAME = re.compile(r"arcadedb[_-]embedded|ArcadeDB Embedded Python", re.IGNORECASE) + +# Files (fnmatch patterns on the path relative to the repository root) that may name the old package. +ALLOWED_PATHS: tuple[str, ...] = ( + "docs/superpowers/*", # plans and specs of the move, history + "CHANGELOG*", + "HISTORY*", + "NEWS*", + "scripts/rename_to_arcadedb.py", + "scripts/check_no_embedded_name.py", + "tests/test_rename_codemod.py", +) + +# Text that is removed from a line before it is scanned: issue references of the old repository. +ALLOWED_TOKENS = re.compile(r"humemai/arcadedb-embedded-python#\d+") + +# A line that matches is allowed as a whole. +ALLOWED_LINES = re.compile( + r"allow-old-name" + r"|This package was published as arcadedb-embedded until 26\.10\.x; install arcadedb now\." + r"|formerly `?arcadedb[_-]embedded", + re.IGNORECASE, +) + +SKIP_DIRS = { + ".git", + "build", + "dist", + "node_modules", + ".venv", + "venv", + "__pycache__", + ".mypy_cache", +} +MAX_BYTES = 5_000_000 + + +def is_allowed_path(rel: str) -> bool: + return any(fnmatch.fnmatch(rel, pat) for pat in ALLOWED_PATHS) + + +def line_hits(line: str) -> bool: + """True when this line names the old package outside the allowlist.""" + if ALLOWED_LINES.search(line): + return False + return bool(OLD_NAME.search(ALLOWED_TOKENS.sub("", line))) + + +def list_files(root: Path) -> list[str]: + """Repository files, relative, with forward slashes: tracked plus untracked-not-ignored.""" + try: + out = subprocess.run( # nosec B603 B607 - fixed git command, no shell + [ + "git", + "-C", + str(root), + "ls-files", + "--cached", + "--others", + "--exclude-standard", + "-z", + ], + check=True, + capture_output=True, + ).stdout + files = [p for p in out.decode("utf-8", "surrogateescape").split("\0") if p] + except (OSError, subprocess.CalledProcessError): + files = [ + p.relative_to(root).as_posix() + for p in root.rglob("*") + if p.is_file() and not SKIP_DIRS.intersection(p.relative_to(root).parts) + ] + # `git ls-files` still lists a path that was deleted in the working tree and not yet staged. + return sorted(p for p in set(files) if (root / p).is_file()) + + +def find_violations(root: Path) -> list[tuple[str, int, str]]: + """(path, line number, text) for every use of the old name outside the allowlist. + + A path that carries the old name is reported with line number 0. + """ + found: list[tuple[str, int, str]] = [] + for rel in list_files(root): + if is_allowed_path(rel): + continue + if OLD_NAME.search(rel): + found.append((rel, 0, "the path itself names the old package")) + path = root / rel + try: + if path.stat().st_size > MAX_BYTES: + continue + data = path.read_bytes() + except OSError: + continue + if b"\0" in data: + continue + try: + text = data.decode("utf-8") + except UnicodeDecodeError: + continue + for number, line in enumerate(text.splitlines(), start=1): + if line_hits(line): + found.append((rel, number, line.strip()[:160])) + return found + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + description="Fail when the old package name reappears." + ) + parser.add_argument( + "--root", + type=Path, + default=Path(__file__).resolve().parent.parent, + help="repository root (default: the parent of scripts/)", + ) + args = parser.parse_args(argv) + found = find_violations(args.root.resolve()) + for rel, number, text in found: + where = rel if number == 0 else f"{rel}:{number}" + print(f"{where}: {text}") + if found: + print( + f"\n{len(found)} use(s) of the old package name. The package is arcadedb (import arcadedb); " + "see ALLOWED_PATHS and ALLOWED_LINES in scripts/check_no_embedded_name.py for the few " + "places that may name the old one.", + file=sys.stderr, + ) + return 1 + print("ok: no use of the old package name outside the allowlist") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/rename_to_arcadedb.py b/scripts/rename_to_arcadedb.py new file mode 100644 index 00000000..b84a0b02 --- /dev/null +++ b/scripts/rename_to_arcadedb.py @@ -0,0 +1,518 @@ +#!/usr/bin/env python3 +"""Rename this repository to the final package names. Re-runnable, deterministic, idempotent. + +Final names (owner decision 2026-10-25): distribution `arcadedb` (JVM wheel) and `arcadedb-native` +(native wheel, when ported); the import name is `arcadedb` for BOTH. No `arcadedb_embedded` and no +`arcadedb-embedded` anywhere in this repository. There is no compatibility shim here: the old PyPI +packages get a deprecation shim from the humemai repository. + +Why a script and not a branch full of edits: the rename touches about 130 files, and other pull +requests keep merging into them. A hand-made rename conflicts with each of those. This script is run +on a fresh checkout of main once the open stack has merged, so it never conflicts, and running it +again changes nothing. + +usage (from a clean checkout of main, on the rename branch): + + python3 scripts/rename_to_arcadedb.py # rewrite, format, check + python3 scripts/rename_to_arcadedb.py --dry-run # report only, change nothing + python3 scripts/rename_to_arcadedb.py --no-format # skip the pre-commit formatting pass + +What it does, in order: + +1. Moves `src/arcadedb_embedded` to `src/arcadedb` with `git mv` (file by file when both exist). +2. Applies a few exact-text edits to prose that stops being true: the release workflow comments, the + package attributes in docs/antora.yml, the two statements that the native wheel installs beside the + JVM wheel (it cannot: both import as `arcadedb`), and the rows of the package table for the placeholder + names. Each edit reports "applied", "already", or "MISSING" (the text moved; fix by hand). +3. Rewrites every other use of the old name in every text file, line by line: + `import arcadedb_embedded as arcadedb` becomes `import arcadedb`; `arcadedb-embedded-native` becomes + `arcadedb-native`; the repository URL becomes https://github.com/ArcadeData/arcadedb-python; + `ArcadeDB Embedded Python` becomes `ArcadeDB Python`; any other `arcadedb_embedded` or + `arcadedb-embedded` becomes `arcadedb`. That covers imports, pyproject name, packages and + package-data, build.sh, build-native.sh, Dockerfile.build, setup.py, .gitignore, CI workflow paths + and names, wheel-content checks, the tests and conftest, the examples, README and the Antora pages. +4. Adds the docs line "This package was published as arcadedb-embedded until 26.10.x; install arcadedb + now." to README.md and docs/modules/ROOT/pages/installation.adoc. +5. Wires the check in: a local pre-commit hook and tests/test_no_embedded_name.py, both running + scripts/check_no_embedded_name.py. +6. Runs the repository's pre-commit hooks (black, isort, shfmt) on the Python and shell files it touched + (twice, so the second pass proves them clean) unless --no-format, then the check. + +What it never touches: + +* Java package names (com.arcadedb...), environment variables (ARCADEDB_*), engine artifact names + (arcadedb-engine, the arcadedata/arcadedb image), and every `arcadedb` that is not followed by + `_embedded` or `-embedded`. +* History: files and lines on the allowlist of scripts/check_no_embedded_name.py (docs/superpowers/, + changelogs, the "published as"/"formerly" lines, `humemai/arcadedb-embedded-python#123` issue references). +* The release workflow's guard logic: only two comments that describe the old state are edited. +* The generic word "embedded" (embedded mode, embedded database). + +Output lists what changed by category (path-based) and by rule. Exit 0 when the tree is clean after the +run, 1 when the check still finds the old name, 2 when --strict and an exact-text edit is MISSING. +Output is ASCII only. +""" + +from __future__ import annotations + +import argparse +import importlib.util +import re +import subprocess # nosec B404 - runs git and pre-commit +import sys +from collections import Counter +from pathlib import Path + +OLD_PKG = "arcadedb_embedded" +NEW_PKG = "arcadedb" +OLD_DIR = "src/arcadedb_embedded" +NEW_DIR = "src/arcadedb" + +DOCS_LINE = "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." + + +def _load_checker(): + path = Path(__file__).resolve().with_name("check_no_embedded_name.py") + spec = importlib.util.spec_from_file_location("check_no_embedded_name", path) + assert ( + spec is not None and spec.loader is not None + ) # nosec B101 - import machinery contract + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +checker = _load_checker() + +# --- generic rules: (name, pattern, replacement), applied in this order to every line ------------- +RULES: tuple[tuple[str, re.Pattern[str], str], ...] = ( + ( + "repository URL", + re.compile(r"github\.com/humemai/arcadedb-embedded-python"), + "github.com/ArcadeData/arcadedb-python", + ), + ("repository name", re.compile(r"arcadedb-embedded-python"), "arcadedb-python"), + ( + "native distribution name", + re.compile(r"arcadedb-embedded-native"), + "arcadedb-native", + ), + ("project title", re.compile(r"ArcadeDB Embedded Python"), "ArcadeDB Python"), + ( + "import statement", + re.compile(r"\bimport arcadedb_embedded as arcadedb(?![A-Za-z0-9_])"), + "import arcadedb", + ), + ("distribution or package name", re.compile(r"arcadedb[_-]embedded"), "arcadedb"), +) + +# --- exact-text edits: (path, label, old, new). "already" when new is present, "MISSING" otherwise --- +EDITS: tuple[tuple[str, str, str, str], ...] = ( + ( + ".github/workflows/release.yml", + "release.yml header comment", + "# Until the distribution is renamed (tracker #19), pyproject.toml still names it arcadedb-embedded.\n" + "# The guard in the publish job refuses to publish anything that is not `arcadedb`, so this workflow\n" + "# cannot publish arcadedb-embedded from here by accident. The rename is a separate change.\n", + "# pyproject.toml names the distribution arcadedb (tracker #19).\n" + "# The guard in the publish job refuses to publish anything that is not `arcadedb`, so this workflow\n" + "# cannot publish another distribution from here by accident.\n", + ), + ( + ".github/workflows/release.yml", + "release.yml guard comment", + " # GUARD: this workflow publishes the distribution `arcadedb` and nothing else. While the\n" + " # package is still named arcadedb-embedded (tracker #19, rename is a separate change), the\n" + " # job stops here, before anything reaches PyPI. Two checks, so neither can drift alone:\n", + " # GUARD: this workflow publishes the distribution `arcadedb` and nothing else. A wheel with\n" + " # another name stops the job here, before anything reaches PyPI. Two checks, so neither can drift alone:\n", + ), + ( + "docs/antora.yml", + "antora package attributes", + " # Package names. After the rename of the distribution these are the only\n" + " # lines to change; every page takes the names from here.\n", + " # Package names. Every page takes them from here.\n", + ), + ( + "docs/antora.yml", + "antora native import name", + " native-import-name: arcadedb_native\n" + " # Other names a reader meets on PyPI (the pages that compare packages use these).\n" + " short-pkg-name: arcadedb\n" + " short-native-pkg-name: arcadedb-native\n" + " jvm-pkg-name: arcadedb-jvm\n", + " native-import-name: arcadedb\n" + " # Other names a reader meets on PyPI (the pages that compare packages use these).\n" + " jvm-pkg-name: arcadedb-jvm\n", + ), + ( + "docs/modules/ROOT/pages/packages.adoc", + "packages table: placeholder rows", + "| `{short-pkg-name}`\n" + "| Embedded, JVM build\n" + "| The name `{pkg-name}` moves to in the middle of November. Until then a placeholder on PyPI that installs `{pkg-name}`.\n" + "\n" + "| `{short-native-pkg-name}`\n" + "| Embedded, native build (preview)\n" + "| The name `{native-pkg-name}` moves to at the same time. Until then an alias that installs it.\n" + "\n" + "| `{jvm-pkg-name}`\n" + "| Embedded, JVM build\n" + "| A reserved name for the JVM build. Until the move it installs `{pkg-name}`.\n", + "| `{jvm-pkg-name}`\n" + "| Embedded, JVM build\n" + "| A reserved name for the JVM build. It installs `{pkg-name}`.\n", + ), + ( + "docs/modules/ROOT/pages/index.adoc", + "index page: side by side", + "The preview installs beside the default package. Its public API follows the one of `{import-name}`, so the pages in this component apply to both;", + "Both packages import as `{import-name}`, so an environment holds one of them. The preview's public API follows the one of the default package, so the pages in this component apply to both;", + ), + ( + "docs/modules/ROOT/pages/installation.adoc", + "installation page: side by side", + "It installs beside `{pkg-name}`: the distribution names differ, and so do the import names (`{native-import-name}` instead of `{import-name}`).", + "It imports as `{native-import-name}`, the same name as `{pkg-name}`, so install only one of the two in an environment.", + ), + # The docs line, after the generic rewrite has produced the install commands it follows. + ( + "README.md", + "README docs line", + "```bash\nuv add arcadedb # or: pip install arcadedb\n```\n", + "```bash\nuv add arcadedb # or: pip install arcadedb\n```\n\n" + + DOCS_LINE + + "\n", + ), + ( + "docs/modules/ROOT/pages/installation.adoc", + "installation page docs line", + '[source,shell,subs="attributes+"]\n----\npip install {pkg-name}\n----\n', + '[source,shell,subs="attributes+"]\n----\npip install {pkg-name}\n----\n\n' + + DOCS_LINE + + "\n", + ), +) + +# Edits that must run after the generic rewrite (their old text is the rewritten form). +LATE_EDITS = {"README docs line", "installation page docs line"} + +PRE_COMMIT_HOOK = """\ +- repo: local + hooks: + - id: no-old-package-name + name: the old package name does not come back + entry: python3 scripts/check_no_embedded_name.py + language: system + pass_filenames: false + always_run: true +""" + +CHECK_TEST = '''\ +"""The old package name must not come back. + +Runs scripts/check_no_embedded_name.py over the repository. It needs no wheel. The allowlist (history, +the rename codemod, issue references) lives in that script. +""" + +import subprocess # nosec B404 - runs sys.executable on a script this repository owns +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +CHECK = ROOT / "scripts" / "check_no_embedded_name.py" + + +def test_old_package_name_is_absent(): + result = subprocess.run( # nosec B603 - fixed argv, no shell + [sys.executable, "-I", str(CHECK), "--root", str(ROOT)], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stdout + result.stderr +''' + + +def category(rel: str) -> str: + if rel in ("pyproject.toml", "setup.py"): + return "packaging metadata" + if rel.startswith(".github/"): + return "CI workflows" + if rel.startswith("src/"): + return "package source" + if rel.startswith("tests/") and rel.endswith(".py"): + return "tests" + if rel.startswith("examples/") and rel.endswith(".py"): + return "examples" + if rel.startswith("scripts/") or rel in (".gitignore", ".pre-commit-config.yaml"): + return "build and verify scripts" + return "docs" + + +class Report: + def __init__(self) -> None: + self.moves: list[str] = [] + self.files: Counter[str] = Counter() # category -> files changed + self.edits_made: Counter[str] = Counter() # category -> replacements + self.by_rule: Counter[str] = Counter() + self.exact: list[tuple[str, str, str]] = [] # (status, path, label) + self.wiring: list[str] = [] + self.touched: set[str] = ( + set() + ) # paths written or moved, for the formatting pass + self.notes: list[str] = [] + + def show(self, dry: bool) -> None: + mode = " (dry run, nothing written)" if dry else "" + print(f"Rename to arcadedb{mode}\n") + print("Moved paths:") + for m in self.moves or [" none (already moved)"]: + print(m if m.startswith(" ") else f" {m}") + print("\nExact-text edits:") + for status, path, label in self.exact: + print(f" {status:8} {path}: {label}") + print("\nChanged by category (files / replacements):") + cats = sorted(set(self.files) | set(self.edits_made)) + for cat in cats: + print(f" {cat:26} {self.files[cat]:4} / {self.edits_made[cat]}") + print( + f" {'total':26} {sum(self.files.values()):4} / {sum(self.edits_made.values())}" + ) + print("\nChanged by rule (replacements):") + for rule, n in sorted(self.by_rule.items()): + print(f" {rule:36} {n}") + if self.wiring: + print("\nWiring:") + for w in self.wiring: + print(f" {w}") + for n in self.notes: + print(f"\n{n}") + + +def run_git(root: Path, *args: str) -> str: + return subprocess.run( # nosec B603 B607 - fixed git argv, no shell + ["git", "-C", str(root), *args], check=True, capture_output=True, text=True + ).stdout + + +def move_package(root: Path, report: Report, dry: bool) -> None: + old, new = root / OLD_DIR, root / NEW_DIR + if not old.exists(): + return + tracked = [ + p for p in run_git(root, "ls-files", "-z", "--", OLD_DIR).split("\0") if p + ] + if not new.exists(): + report.moves.append( + f"git mv {OLD_DIR} -> {NEW_DIR} ({len(tracked)} tracked files)" + ) + if not dry: + run_git(root, "mv", OLD_DIR, NEW_DIR) + return + # Both exist (a merge brought a new file into the old directory): move file by file. + for rel in tracked: + target = NEW_DIR + rel[len(OLD_DIR) :] + report.moves.append(f"git mv {rel} -> {target}") + if not dry: + (root / target).parent.mkdir(parents=True, exist_ok=True) + run_git(root, "mv", rel, target) + if not dry and old.exists() and not any(old.rglob("*")): + old.rmdir() + + +def text_files(root: Path) -> list[str]: + """Files the rewrite may touch: every repository file that is not on the allowlist.""" + return [f for f in checker.list_files(root) if not checker.is_allowed_path(f)] + + +def read_text(path: Path) -> str | None: + try: + data = path.read_bytes() + except OSError: + return None + if b"\0" in data or len(data) > checker.MAX_BYTES: + return None + try: + return data.decode("utf-8") + except UnicodeDecodeError: + return None + + +def apply_rules(line: str, rules_hit: Counter[str]) -> str: + """Generic rewrite of one line; issue references of the old repository are left as written.""" + if checker.ALLOWED_LINES.search(line): + return line + pieces = re.split(f"({checker.ALLOWED_TOKENS.pattern})", line) + for i in range(0, len(pieces), 2): # even pieces are outside the issue references + for name, pattern, repl in RULES: + pieces[i], n = pattern.subn(repl, pieces[i]) + rules_hit[name] += n + return "".join(pieces) + + +def exact_edit(rel: str, text: str, wanted: set[str], report: Report) -> str: + for path, label, old, new in EDITS: + if path != rel or label not in wanted: + continue + if "\r\n" in text: # a Windows checkout with autocrlf + old, new = old.replace("\n", "\r\n"), new.replace("\n", "\r\n") + if ( + new in text + ): # first: the docs-line edits keep their old text in the new text + report.exact.append(("already", path, label)) + elif old in text: + text = text.replace(old, new, 1) + report.exact.append(("applied", path, label)) + else: + report.exact.append(("MISSING", path, label)) + return text + + +def rewrite_files(root: Path, report: Report, dry: bool) -> None: + early = {label for _, label, _, _ in EDITS if label not in LATE_EDITS} + paths = {p for p, _, _, _ in EDITS} + for rel in text_files(root): + src = root / rel + text = read_text(src) + if text is None: + continue + before = text + rules_hit: Counter[str] = Counter() + if rel in paths: + text = exact_edit(rel, text, early, report) + text = "".join( + apply_rules(line, rules_hit) for line in text.splitlines(keepends=True) + ) + if rel in paths: + text = exact_edit(rel, text, LATE_EDITS, report) + if text != before: + cat = category(rel) + report.files[cat] += 1 + report.edits_made[cat] += sum(rules_hit.values()) + sum( + 1 for s, p, _ in report.exact if p == rel and s == "applied" + ) + report.by_rule.update(rules_hit) + report.touched.add(rel) + if not dry: + src.write_bytes(text.encode("utf-8")) + + +def wire_check(root: Path, report: Report, dry: bool) -> None: + hook_file = root / ".pre-commit-config.yaml" + if hook_file.exists(): + text = hook_file.read_text(encoding="utf-8") + if "check_no_embedded_name.py" in text: + report.wiring.append("already .pre-commit-config.yaml: check hook") + else: + report.wiring.append( + "added .pre-commit-config.yaml: local hook running the check" + ) + if not dry: + hook_file.write_text( + text.rstrip("\n") + "\n" + PRE_COMMIT_HOOK, encoding="utf-8" + ) + test_file = root / "tests" / "test_no_embedded_name.py" + if test_file.exists(): + report.wiring.append("already tests/test_no_embedded_name.py") + elif (root / "tests").is_dir(): + report.wiring.append("added tests/test_no_embedded_name.py") + report.touched.add("tests/test_no_embedded_name.py") + if not dry: + test_file.write_text(CHECK_TEST, encoding="utf-8") + + +def format_tree(root: Path, report: Report) -> bool: + """Format the Python and shell files this run touched, with the hooks the repository already has. + + Only the touched files, and only .py and .sh: `pre-commit run --all-files` would also rewrite files + this run did not change (docs/antora.yml is not clean under the YAML hook on main). The first pass + autofixes; the second proves the files are clean. + """ + files = sorted( + f + for f in report.touched + if f.endswith((".py", ".sh")) + and (root / f).is_file() + and not checker.is_allowed_path(f) + ) + if not files: + report.notes.append("pre-commit: no Python or shell file to format") + return True + for cmd in (["pre-commit"], ["uvx", "pre-commit"]): + try: + for attempt in (1, 2): + result = subprocess.run( # nosec B603 B607 - fixed argv, no shell + [*cmd, "run", "--files", *files], + cwd=root, + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + report.notes.append( + f"pre-commit: {len(files)} file(s) clean after pass {attempt}" + ) + return True + report.notes.append( + "pre-commit: still failing after two passes:\n" + result.stdout[-2000:] + ) + return False + except FileNotFoundError: + continue + report.notes.append( + "pre-commit: not found (tried pre-commit and uvx pre-commit); formatting step skipped" + ) + return True + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + description="Rename arcadedb-embedded to arcadedb (see the module docstring)." + ) + parser.add_argument( + "--root", type=Path, default=Path(__file__).resolve().parent.parent + ) + parser.add_argument( + "--dry-run", action="store_true", help="report what would change, write nothing" + ) + parser.add_argument( + "--no-format", action="store_true", help="skip the pre-commit formatting pass" + ) + parser.add_argument( + "--strict", + action="store_true", + help="exit 2 when an exact-text edit is MISSING", + ) + args = parser.parse_args(argv) + root = args.root.resolve() + report = Report() + + move_package(root, report, args.dry_run) + rewrite_files(root, report, args.dry_run) + wire_check(root, report, args.dry_run) + formatted_ok = True + if not args.dry_run and not args.no_format: + formatted_ok = format_tree(root, report) + report.show(args.dry_run) + + if args.dry_run: + return 0 + leftovers = checker.find_violations(root) + for rel, number, text in leftovers: + print(f"LEFTOVER {rel}:{number}: {text}") + missing = [x for x in report.exact if x[0] == "MISSING"] + print( + f"\ncheck: {len(leftovers)} leftover use(s) of the old name, {len(missing)} MISSING exact edit(s)" + ) + if leftovers or not formatted_ok: + return 1 + if missing and args.strict: + return 2 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_rename_codemod.py b/tests/test_rename_codemod.py new file mode 100644 index 00000000..b1b8ab9b --- /dev/null +++ b/tests/test_rename_codemod.py @@ -0,0 +1,144 @@ +"""The rename codemod (scripts/rename_to_arcadedb.py) and the old-name check. + +Runs both on a small synthetic repository in a temporary directory, so no wheel is needed. What it +pins: the package directory moves, imports and metadata are rewritten, history on the allowlist is not +touched, issue references of the old repository keep their text, a second run changes nothing, and the +check passes on the result and fails on a planted old name. +""" + +import subprocess # nosec B404 - runs git and sys.executable on scripts this repository owns +import sys +from pathlib import Path + +import pytest + +SCRIPTS = Path(__file__).resolve().parent.parent / "scripts" +CODEMOD = SCRIPTS / "rename_to_arcadedb.py" +CHECK = SCRIPTS / "check_no_embedded_name.py" + +FILES = { + "src/arcadedb_embedded/__init__.py": '"""arcadedb_embedded."""\n__version__ = "1"\n', + "src/arcadedb_embedded/core.py": "from arcadedb_embedded import jvm\n", + "pyproject.toml": ( + '[project]\nname = "arcadedb-embedded"\n' + '[tool.setuptools]\npackages = ["arcadedb_embedded"]\n' + '[tool.setuptools.package-data]\narcadedb_embedded = ["jars/*.jar"]\n' + '[project.urls]\nHomepage = "https://github.com/humemai/arcadedb-embedded-python"\n' + ), + "tests/test_x.py": "import arcadedb_embedded as arcadedb\n\n\ndef test_x():\n assert arcadedb\n", + "scripts/build.sh": 'PACKAGE_NAME="arcadedb-embedded"\nls dist/arcadedb_embedded-*.whl\n', + ".gitignore": "src/arcadedb_embedded/jars/\n", + "README.md": ( + "# Title\n\n```bash\nuv add arcadedb-embedded # or: pip install arcadedb-embedded\n```\n\n" + "Fixed in humemai/arcadedb-embedded-python#12 and ARCADEDB_HOME stays.\n" + "Engine: com.arcadedb.Constants and arcadedata/arcadedb stay.\n" + ), + "docs/superpowers/plans/history.md": "The old package arcadedb-embedded lives on in this plan.\n", +} + + +def git(root, *args): + subprocess.run( + ["git", "-C", str(root), *args], check=True, capture_output=True + ) # nosec B603 B607 + + +def run(script, root, *args): + return subprocess.run( # nosec B603 - fixed argv, no shell + [sys.executable, "-I", str(script), "--root", str(root), *args], + capture_output=True, + text=True, + check=False, + ) + + +@pytest.fixture +def repo(tmp_path): + for rel, text in FILES.items(): + path = tmp_path / rel + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(text.encode("utf-8")) # LF on every platform + git(tmp_path, "init", "-q") + git(tmp_path, "add", "-A") + return tmp_path + + +def snapshot(root): + return { + p.relative_to(root).as_posix(): p.read_bytes() + for p in root.rglob("*") + if p.is_file() and ".git/" not in p.as_posix() + } + + +def test_check_fails_on_the_old_name_and_passes_on_the_allowlist(repo): + result = run(CHECK, repo) + assert result.returncode == 1 + assert "pyproject.toml" in result.stdout + assert "docs/superpowers" not in result.stdout # allowlisted history + assert ( + "humemai/arcadedb-embedded-python#12" not in result.stdout + ) # allowlisted issue reference + + +def test_codemod_renames_and_is_idempotent(repo): + first = run(CODEMOD, repo, "--no-format") + assert first.returncode == 0, first.stdout + first.stderr + + assert (repo / "src/arcadedb/__init__.py").is_file() + assert not (repo / "src/arcadedb_embedded").exists() + assert (repo / "src/arcadedb/core.py").read_text() == "from arcadedb import jvm\n" + assert (repo / "tests/test_x.py").read_text().startswith("import arcadedb\n") + pyproject = (repo / "pyproject.toml").read_text() + assert 'name = "arcadedb"' in pyproject + assert 'packages = ["arcadedb"]' in pyproject + assert '\narcadedb = ["jars/*.jar"]' in pyproject + assert "github.com/ArcadeData/arcadedb-python" in pyproject + assert ( + repo / "scripts/build.sh" + ).read_text() == 'PACKAGE_NAME="arcadedb"\nls dist/arcadedb-*.whl\n' + assert (repo / ".gitignore").read_text() == "src/arcadedb/jars/\n" + + readme = (repo / "README.md").read_text() + assert "uv add arcadedb # or: pip install arcadedb\n" in readme + assert ( + "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." + in readme + ) + assert ( + "humemai/arcadedb-embedded-python#12" in readme + ) # issue reference keeps its text + assert ( + "ARCADEDB_HOME" in readme + and "com.arcadedb.Constants" in readme + and "arcadedata/arcadedb" in readme + ) + + history = (repo / "docs/superpowers/plans/history.md").read_text() + assert history == FILES["docs/superpowers/plans/history.md"] # not rewritten + + assert (repo / "tests/test_no_embedded_name.py").is_file() + assert run(CHECK, repo).returncode == 0 + + git(repo, "add", "-A") + before = snapshot(repo) + second = run(CODEMOD, repo, "--no-format") + assert second.returncode == 0, second.stdout + second.stderr + assert snapshot(repo) == before + assert "total" in second.stdout and " 0 / 0" in second.stdout + + +def test_dry_run_changes_nothing(repo): + before = snapshot(repo) + result = run(CODEMOD, repo, "--dry-run") + assert result.returncode == 0, result.stdout + result.stderr + assert snapshot(repo) == before + assert "dry run" in result.stdout + + +def test_check_catches_a_planted_old_name_after_the_rename(repo): + assert run(CODEMOD, repo, "--no-format").returncode == 0 + (repo / "src/arcadedb/core.py").write_text("import arcadedb_embedded\n") + result = run(CHECK, repo) + assert result.returncode == 1 + assert "src/arcadedb/core.py:1" in result.stdout From 9f710e3fb2fb7dd10bcb34c5172334f68e1576ac Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 10:42:17 +0900 Subject: [PATCH 2/3] Rename the package to arcadedb The distribution is now `arcadedb` (JVM wheel), later `arcadedb-native`, and both import as `arcadedb`. No `arcadedb_embedded` or `arcadedb-embedded` name is left outside the allowlist of scripts/check_no_embedded_name.py, which stays as a permanent guard (pre-commit hook and tests/test_no_embedded_name.py). Applied with scripts/rename_to_arcadedb.py on the merged tree (140 files, 406 replacements, a second run changes nothing); the codemod and its test are deleted in this commit. The README packages table and status paragraph are rewritten for the new names, and the docs carry the line "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." Both wheels import as `arcadedb`, so pip overwrites one with the other silently. Importing `arcadedb` now emits a RuntimeWarning when both distributions are installed (importlib.metadata), with unit tests. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/release.yml | 9 +- .github/workflows/test-python-bindings.yml | 32 +- .github/workflows/test-python-examples.yml | 20 +- .gitignore | 6 +- .pre-commit-config.yaml | 8 + CLAUDE.md | 4 +- README.md | 24 +- docs/antora.yml | 17 +- docs/modules/ROOT/pages/index.adoc | 4 +- docs/modules/ROOT/pages/installation.adoc | 6 +- docs/modules/ROOT/pages/packages.adoc | 10 +- examples/01_simple_document_store.py | 4 +- examples/02_social_network_graph.py | 6 +- examples/03_vector_search.py | 4 +- examples/04_csv_import_documents.py | 4 +- examples/05_csv_import_graph.py | 2 +- examples/06_vector_search_recommendations.py | 2 +- examples/07_stackoverflow_tables_oltp.py | 6 +- examples/08_stackoverflow_tables_olap.py | 6 +- examples/09_stackoverflow_graph_oltp.py | 6 +- examples/10_stackoverflow_graph_olap.py | 6 +- examples/11_vector_index_build.py | 4 +- examples/12_vector_search.py | 2 +- examples/13_stackoverflow_hybrid_queries.py | 14 +- examples/14_lifecycle_timing.py | 4 +- ..._database_vs_transactional_table_ingest.py | 2 +- ..._database_vs_transactional_graph_ingest.py | 2 +- examples/17_timeseries_end_to_end.py | 2 +- examples/18_geo_predicates_wkt.py | 2 +- examples/19_hash_index_exact_match.py | 2 +- .../20_graph_algorithms_route_planning.py | 2 +- examples/21_graph_analytical_view_sql.py | 2 +- examples/22_numpy_bulk_io.py | 6 +- examples/23_server_mode_http_access.py | 2 +- .../24_server_http_transactions_timeseries.py | 2 +- .../25_sparse_quantization_and_compact.py | 2 +- .../26_cross_model_transaction_atomicity.py | 2 +- examples/27_dataframe_arrow_ingest.py | 4 +- .../28_python_functions_and_record_events.py | 2 +- examples/29_hybrid_memory_retrieval.py | 4 +- examples/README.md | 2 +- examples/download_data.py | 4 +- pyproject.toml | 16 +- scripts/Dockerfile.build | 8 +- scripts/arrow_transport_probe.py | 4 +- scripts/build-native.sh | 8 +- scripts/build.sh | 14 +- scripts/check_no_embedded_name.py | 4 +- scripts/extract_version.py | 6 +- scripts/rename_to_arcadedb.py | 518 ------------------ scripts/setup_jars.py | 8 +- scripts/write_version.py | 6 +- setup.py | 2 +- .../__init__.py | 7 +- src/arcadedb/_dist_conflict.py | 59 ++ .../_logging.py | 4 +- src/{arcadedb_embedded => arcadedb}/aot.py | 0 .../async_executor.py | 0 .../callbacks.py | 0 src/{arcadedb_embedded => arcadedb}/core.py | 0 .../dataframe.py | 2 +- .../exceptions.py | 0 .../exporter.py | 0 src/{arcadedb_embedded => arcadedb}/graph.py | 0 .../graph_batch.py | 0 .../importer.py | 0 src/{arcadedb_embedded => arcadedb}/jvm.py | 16 +- .../results.py | 6 +- src/{arcadedb_embedded => arcadedb}/schema.py | 0 src/{arcadedb_embedded => arcadedb}/server.py | 2 +- .../transactions.py | 0 .../type_conversion.py | 0 src/{arcadedb_embedded => arcadedb}/vector.py | 0 tests/README.md | 4 +- tests/conftest.py | 4 +- tests/test_aot.py | 4 +- tests/test_async_executor.py | 2 +- tests/test_bridge_fixes.py | 6 +- tests/test_build_no_uv_refresh.py | 4 +- tests/test_build_wheel_check.py | 6 +- tests/test_bulk_insert.py | 22 +- tests/test_bulk_json_paths.py | 4 +- tests/test_columnar_readers.py | 6 +- tests/test_concurrency.py | 6 +- tests/test_conversion_fixes.py | 8 +- tests/test_core.py | 4 +- tests/test_count_pushdown_known_issues.py | 2 +- tests/test_cross_model_atomicity.py | 2 +- tests/test_cypher.py | 2 +- tests/test_database_utils.py | 2 +- tests/test_dataframe_ingest.py | 4 +- tests/test_declared_type_known_issues.py | 2 +- tests/test_dist_conflict.py | 101 ++++ tests/test_dml_plan_cache_known_issues.py | 2 +- tests/test_docs_examples.py | 16 +- tests/test_example29_hybrid_memory.py | 2 +- tests/test_exporter.py | 6 +- tests/test_float_vector_bulk.py | 4 +- tests/test_free_threading.py | 4 +- tests/test_geo_predicate_sql.py | 2 +- tests/test_graph.py | 6 +- tests/test_graph_algorithms_sql.py | 2 +- tests/test_graph_api.py | 4 +- tests/test_graph_batch.py | 4 +- tests/test_hash_index_schema.py | 2 +- tests/test_import_database.py | 2 +- tests/test_importer_api.py | 2 +- tests/test_insert_columns.py | 2 +- tests/test_interrupt_hygiene.py | 8 +- tests/test_jar_closure.py | 12 +- tests/test_jar_provenance.py | 4 +- tests/test_java_package_shadowing.py | 8 +- tests/test_jvm.py | 12 +- tests/test_jvm_args.py | 2 +- tests/test_legacy_temporal_implementations.py | 2 +- .../test_light_edge_and_view_known_issues.py | 4 +- tests/test_log_dir.py | 24 +- tests/test_logging_helper.py | 8 +- tests/test_materialized_view_sql.py | 2 +- tests/test_no_embedded_name.py | 22 + tests/test_null_index_known_issues.py | 2 +- tests/test_numpy_support.py | 2 +- tests/test_open_read_only.py | 4 +- tests/test_parameter_binding.py | 6 +- tests/test_percall_calls.py | 8 +- tests/test_python_callbacks.py | 4 +- tests/test_python_function_interrupt.py | 2 +- tests/test_rename_codemod.py | 144 ----- tests/test_restore_sql.py | 2 +- tests/test_results_after_close.py | 4 +- tests/test_resultset.py | 16 +- tests/test_resultset_arrow.py | 2 +- tests/test_resultset_read_errors.py | 4 +- tests/test_run_in_transaction_retry.py | 4 +- tests/test_runtime_cache.py | 18 +- tests/test_schema.py | 6 +- tests/test_schema_batching.py | 2 +- tests/test_server.py | 6 +- tests/test_server_http_endpoints.py | 2 +- tests/test_server_packaging.py | 14 +- tests/test_server_patterns.py | 2 +- tests/test_server_wire_protocols.py | 12 +- tests/test_sigint.py | 4 +- tests/test_sparse_quantization_compact.py | 2 +- tests/test_stage_jars.py | 4 +- tests/test_temporal_writers_agree.py | 7 +- tests/test_timeseries_sql.py | 2 +- tests/test_transaction_config.py | 2 +- tests/test_transaction_context_interrupt.py | 2 +- tests/test_type_conversion.py | 16 +- tests/test_typed_rows.py | 8 +- tests/test_vector.py | 12 +- tests/test_vector_delta_visibility.py | 2 +- tests/test_vector_params_verification.py | 4 +- tests/test_vector_product_quantization.py | 4 +- tests/test_vector_second_pass.py | 2 +- tests/test_vector_sql.py | 4 +- tests/test_wheel_licences.py | 20 +- tests/test_wheel_platform_tag.py | 14 +- tests/test_xml_limits.py | 10 +- 160 files changed, 608 insertions(+), 1084 deletions(-) delete mode 100644 scripts/rename_to_arcadedb.py rename src/{arcadedb_embedded => arcadedb}/__init__.py (91%) create mode 100644 src/arcadedb/_dist_conflict.py rename src/{arcadedb_embedded => arcadedb}/_logging.py (84%) rename src/{arcadedb_embedded => arcadedb}/aot.py (100%) rename src/{arcadedb_embedded => arcadedb}/async_executor.py (100%) rename src/{arcadedb_embedded => arcadedb}/callbacks.py (100%) rename src/{arcadedb_embedded => arcadedb}/core.py (100%) rename src/{arcadedb_embedded => arcadedb}/dataframe.py (99%) rename src/{arcadedb_embedded => arcadedb}/exceptions.py (100%) rename src/{arcadedb_embedded => arcadedb}/exporter.py (100%) rename src/{arcadedb_embedded => arcadedb}/graph.py (100%) rename src/{arcadedb_embedded => arcadedb}/graph_batch.py (100%) rename src/{arcadedb_embedded => arcadedb}/importer.py (100%) rename src/{arcadedb_embedded => arcadedb}/jvm.py (98%) rename src/{arcadedb_embedded => arcadedb}/results.py (99%) rename src/{arcadedb_embedded => arcadedb}/schema.py (100%) rename src/{arcadedb_embedded => arcadedb}/server.py (99%) rename src/{arcadedb_embedded => arcadedb}/transactions.py (100%) rename src/{arcadedb_embedded => arcadedb}/type_conversion.py (100%) rename src/{arcadedb_embedded => arcadedb}/vector.py (100%) create mode 100644 tests/test_dist_conflict.py create mode 100644 tests/test_no_embedded_name.py delete mode 100644 tests/test_rename_codemod.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e08973c7..67fc5db0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,9 +13,9 @@ name: Release to PyPI # Nothing here stores a token: the publish job authenticates with PyPI trusted publishing (OIDC), # and only that job has the id-token permission. # -# Until the distribution is renamed (tracker #19), pyproject.toml still names it arcadedb-embedded. +# pyproject.toml names the distribution arcadedb (tracker #19). # The guard in the publish job refuses to publish anything that is not `arcadedb`, so this workflow -# cannot publish arcadedb-embedded from here by accident. The rename is a separate change. +# cannot publish another distribution from here by accident. # # TODO (native-wheel port, tracker #19): a second publish job for arcadedb-native, in environment # pypi-native with its own id-token permission and the same kind of guard (distribution name must @@ -240,9 +240,8 @@ jobs: name: release-dist path: dist/ - # GUARD: this workflow publishes the distribution `arcadedb` and nothing else. While the - # package is still named arcadedb-embedded (tracker #19, rename is a separate change), the - # job stops here, before anything reaches PyPI. Two checks, so neither can drift alone: + # GUARD: this workflow publishes the distribution `arcadedb` and nothing else. A wheel with + # another name stops the job here, before anything reaches PyPI. Two checks, so neither can drift alone: # the wheel file name and the Name field of its METADATA, both must be DIST_NAME. - name: Refuse unless every wheel is the distribution ${{ env.DIST_NAME }} run: | diff --git a/.github/workflows/test-python-bindings.yml b/.github/workflows/test-python-bindings.yml index f747756c..ea239256 100644 --- a/.github/workflows/test-python-bindings.yml +++ b/.github/workflows/test-python-bindings.yml @@ -208,19 +208,19 @@ jobs: echo "๐Ÿ“ฆ Downloading JARs from arcadedata/arcadedb:${ARCADEDB_IMAGE_TAG}..." # Create output directory - mkdir -p src/arcadedb_embedded/jars + mkdir -p src/arcadedb/jars # Create a temporary container and copy JARs from it CONTAINER_ID=$(docker create "arcadedata/arcadedb:${ARCADEDB_IMAGE_TAG}") - docker cp $CONTAINER_ID:/home/arcadedb/lib/. src/arcadedb_embedded/jars/ + docker cp $CONTAINER_ID:/home/arcadedb/lib/. src/arcadedb/jars/ docker rm $CONTAINER_ID # Verify JARs were downloaded - ls -lh src/arcadedb_embedded/jars/ - JAR_COUNT=$(ls -1 src/arcadedb_embedded/jars/*.jar 2>/dev/null | wc -l) + ls -lh src/arcadedb/jars/ + JAR_COUNT=$(ls -1 src/arcadedb/jars/*.jar 2>/dev/null | wc -l) echo "โœ… Downloaded $JAR_COUNT JAR files" - ARCADEDB_TAG=$(python3 scripts/extract_version.py src/arcadedb_embedded/jars --format=docker) + ARCADEDB_TAG=$(python3 scripts/extract_version.py src/arcadedb/jars --format=docker) echo "๐Ÿ“Œ ArcadeDB version: $ARCADEDB_TAG" # The build jobs pull arcadedata/arcadedb: as their base image, # so a pinned tag must name that version exactly (26.10.2, not an alias). @@ -236,12 +236,12 @@ jobs: uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: arcadedb-jars - path: src/arcadedb_embedded/jars/*.jar + path: src/arcadedb/jars/*.jar retention-days: 1 # Second job: Build and test on each platform test: - name: Test arcadedb-embedded (${{ matrix.platform }}, Python ${{ matrix.python-version }}) + name: Test arcadedb (${{ matrix.platform }}, Python ${{ matrix.python-version }}) runs-on: ${{ matrix.runs-on }} needs: download-jars if: github.event_name != 'pull_request' || github.event.pull_request.draft == false @@ -301,7 +301,7 @@ jobs: uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: arcadedb-jars - path: src/arcadedb_embedded/jars + path: src/arcadedb/jars - name: Set up Java (for native builds on macOS/Windows) if: matrix.platform != 'linux/amd64' && matrix.platform != 'linux/arm64' @@ -335,17 +335,17 @@ jobs: # ln -s "$PYTHON_DIR/python.exe" "$PYTHON_DIR/python3.exe" || true # python3 --version - - name: Build and test arcadedb-embedded (${{ matrix.platform }}) + - name: Build and test arcadedb (${{ matrix.platform }}) shell: bash env: # The engine version download-jars read from the ARCADEDB_IMAGE_TAG jars; # the Docker build takes its base image by this tag, the jars from the artifact ARCADEDB_TAG: ${{ needs.download-jars.outputs.arcadedb-tag }} run: | - echo "๐Ÿ”จ Building arcadedb-embedded for ${{ matrix.platform }} with Python ${{ matrix.python-version }}..." + echo "๐Ÿ”จ Building arcadedb for ${{ matrix.platform }} with Python ${{ matrix.python-version }}..." # The jars download-jars fetched, on every platform, so all wheels of this # run carry the same engine build (and build.sh checks the integration jar) - ./scripts/build.sh ${{ matrix.platform }} ${{ matrix.python-version }} src/arcadedb_embedded/jars + ./scripts/build.sh ${{ matrix.platform }} ${{ matrix.python-version }} src/arcadedb/jars - name: Extract wheel for additional testing shell: bash @@ -358,7 +358,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - # Note: Java is NOT required for arcadedb-embedded (JRE is bundled) + # Note: Java is NOT required for arcadedb (JRE is bundled) # This package works without any external Java installation! - name: Install wheel and test dependencies @@ -380,7 +380,7 @@ jobs: timeout-minutes: 30 shell: bash run: | - echo "๐Ÿงช Testing arcadedb-embedded (with bundled JRE)..." + echo "๐Ÿงช Testing arcadedb (with bundled JRE)..." # Run pytest with JUnit XML output for structured results # WINDOWS RUNS WITH --capture=sys (humemai/arcadedb-embedded-python#10). pytest's default fd capture swaps @@ -485,7 +485,7 @@ jobs: summary_file = os.environ.get('GITHUB_STEP_SUMMARY', '/dev/null') with open(summary_file, 'a', encoding='utf-8') as f: - f.write("## ๐Ÿงช Test Results: arcadedb-embedded (${{ matrix.platform }})\n") + f.write("## ๐Ÿงช Test Results: arcadedb (${{ matrix.platform }})\n") f.write("\n") # Check test status @@ -623,9 +623,9 @@ jobs: if [ "${{ needs.test.result }}" = "success" ]; then echo "โœ… **All platforms passed testing!**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "The arcadedb-embedded package has been successfully built and tested." >> $GITHUB_STEP_SUMMARY + echo "The arcadedb package has been successfully built and tested." >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "**Package**: arcadedb-embedded" >> $GITHUB_STEP_SUMMARY + echo "**Package**: arcadedb" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "โ„น๏ธ **Note**: Some platform/Python combinations are excluded from testing:" >> $GITHUB_STEP_SUMMARY echo "- Windows ARM64 (no GitHub-hosted runners available)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test-python-examples.yml b/.github/workflows/test-python-examples.yml index 7b5ed5b7..148ed8d0 100644 --- a/.github/workflows/test-python-examples.yml +++ b/.github/workflows/test-python-examples.yml @@ -125,16 +125,16 @@ jobs: echo "๐Ÿ“ฆ Downloading JARs from arcadedata/arcadedb:${ARCADEDB_IMAGE_TAG}..." # Create output directory - mkdir -p src/arcadedb_embedded/jars + mkdir -p src/arcadedb/jars # Create a temporary container and copy JARs from it CONTAINER_ID=$(docker create "arcadedata/arcadedb:${ARCADEDB_IMAGE_TAG}") - docker cp $CONTAINER_ID:/home/arcadedb/lib/. src/arcadedb_embedded/jars/ + docker cp $CONTAINER_ID:/home/arcadedb/lib/. src/arcadedb/jars/ docker rm $CONTAINER_ID - echo "โœ… Downloaded $(ls -1 src/arcadedb_embedded/jars/*.jar | wc -l) JARs" + echo "โœ… Downloaded $(ls -1 src/arcadedb/jars/*.jar | wc -l) JARs" - ARCADEDB_TAG=$(python3 scripts/extract_version.py src/arcadedb_embedded/jars --format=docker) + ARCADEDB_TAG=$(python3 scripts/extract_version.py src/arcadedb/jars --format=docker) echo "๐Ÿ“Œ ArcadeDB version: $ARCADEDB_TAG" # The build jobs pull arcadedata/arcadedb: as their base image, # so a pinned tag must name that version exactly (26.10.2, not an alias). @@ -150,7 +150,7 @@ jobs: uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: arcadedb-jars-examples - path: src/arcadedb_embedded/jars/*.jar + path: src/arcadedb/jars/*.jar retention-days: 1 # Second job: Test examples on each platform @@ -202,7 +202,7 @@ jobs: uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: arcadedb-jars-examples - path: src/arcadedb_embedded/jars + path: src/arcadedb/jars - name: Set up Java (for native builds on macOS/Windows) if: matrix.platform != 'linux/amd64' && matrix.platform != 'linux/arm64' @@ -246,19 +246,19 @@ jobs: # ln -s "$PYTHON_DIR/python.exe" "$PYTHON_DIR/python3.exe" || true # python3 --version - - name: Build arcadedb-embedded (${{ matrix.platform }}) + - name: Build arcadedb (${{ matrix.platform }}) shell: bash env: # The engine version download-jars read from the ARCADEDB_IMAGE_TAG jars; # the Docker build takes its base image by this tag, the jars from the artifact ARCADEDB_TAG: ${{ needs.download-jars.outputs.arcadedb-tag }} run: | - echo "๐Ÿ”จ Building arcadedb-embedded for ${{ matrix.platform }} with Python ${{ matrix.python-version }}..." + echo "๐Ÿ”จ Building arcadedb for ${{ matrix.platform }} with Python ${{ matrix.python-version }}..." # The jars download-jars fetched, on every platform, so all wheels of this # run carry the same engine build (and build.sh checks the integration jar) - ./scripts/build.sh ${{ matrix.platform }} ${{ matrix.python-version }} src/arcadedb_embedded/jars + ./scripts/build.sh ${{ matrix.platform }} ${{ matrix.python-version }} src/arcadedb/jars - # Note: Java is NOT required - arcadedb-embedded has bundled JRE! + # Note: Java is NOT required - arcadedb has bundled JRE! - name: Install ArcadeDB Python bindings shell: bash diff --git a/.gitignore b/.gitignore index a063389a..02d33a86 100644 --- a/.gitignore +++ b/.gitignore @@ -225,7 +225,7 @@ __marimo__/ # SPDX-License-Identifier: Apache-2.0 # Generated during build - do not commit -src/arcadedb_embedded/_version.py +src/arcadedb/_version.py # Build artifacts @@ -280,8 +280,8 @@ scripts/.dev_version_tracker.json src/**/__pycache__/ src/**/*.egg-info/ # the JRE and the jars scripts/build-native.sh bundles into the package -src/arcadedb_embedded/jre/ -src/arcadedb_embedded/jars/ +src/arcadedb/jre/ +src/arcadedb/jars/ # git worktrees for issue branches .worktrees/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 67b8838f..501296e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,3 +28,11 @@ repos: rev: v1.7.12 hooks: - id: actionlint +- repo: local + hooks: + - id: no-old-package-name + name: the old package name does not come back + entry: python3 scripts/check_no_embedded_name.py + language: system + pass_filenames: false + always_run: true diff --git a/CLAUDE.md b/CLAUDE.md index f96a86fb..43b84b29 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -`arcadedb-embedded`: Python bindings for ArcadeDB that run the Java engine in-process through JPype. Each wheel is platform-specific and bundles the ArcadeDB jars plus a jlink'd Java 25 JRE, so users need no Java install. Moved here from `bindings/python` in ArcadeData/arcadedb (history kept; issue numbers in older commit messages refer to that repo). +`arcadedb`: Python bindings for ArcadeDB that run the Java engine in-process through JPype. Each wheel is platform-specific and bundles the ArcadeDB jars plus a jlink'd Java 25 JRE, so users need no Java install. Moved here from `bindings/python` in ArcadeData/arcadedb (history kept; issue numbers in older commit messages refer to that repo). ## Working rules @@ -36,7 +36,7 @@ Tests run against the **built wheel**, not the source tree. `pyproject.toml` has ```bash uv venv # once -uv pip install --reinstall-package arcadedb-embedded "$(ls dist/*.whl | head -1)[test]" # after each build +uv pip install --reinstall-package arcadedb "$(ls dist/*.whl | head -1)[test]" # after each build uv run pytest # all uv run pytest tests/test_core.py -v # one file uv run pytest tests/test_core.py::TestX::test_y # one test diff --git a/README.md b/README.md index 07e7edaf..ec1f9b65 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ArcadeDB Python -[![PyPI](https://img.shields.io/pypi/v/arcadedb-embedded)](https://pypi.org/project/arcadedb-embedded/) -[![Python versions](https://img.shields.io/pypi/pyversions/arcadedb-embedded)](https://pypi.org/project/arcadedb-embedded/) +[![PyPI](https://img.shields.io/pypi/v/arcadedb)](https://pypi.org/project/arcadedb/) +[![Python versions](https://img.shields.io/pypi/pyversions/arcadedb)](https://pypi.org/project/arcadedb/) [![Tests](https://github.com/ArcadeData/arcadedb-python/actions/workflows/test-python-bindings.yml/badge.svg)](https://github.com/ArcadeData/arcadedb-python/actions/workflows/test-python-bindings.yml) [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/ArcadeData/arcadedb-python/blob/main/LICENSE) @@ -10,17 +10,19 @@ Python bindings for [ArcadeDB](https://arcadedb.com), the multi-model database ( ## Install ```bash -pip install arcadedb-embedded # or: uv add arcadedb-embedded +pip install arcadedb # or: uv add arcadedb ``` +This package was published as arcadedb-embedded until 26.10.x; install arcadedb now. + - Python 3.10 to 3.14. CI builds and tests all five versions on every platform below. - Linux x86_64 and ARM64, macOS on Apple Silicon, Windows x86_64. One wheel per platform and Python version. -- Import name: `arcadedb_embedded`. Optional extras: `arrow`, `pandas`, `vector`. +- Import name: `arcadedb`. Optional extras: `arrow`, `pandas`, `vector`. ## Quick start ```python -import arcadedb_embedded as arcadedb +import arcadedb with arcadedb.create_database("./mydb") as db: db.command("sql", "CREATE DOCUMENT TYPE Person") @@ -51,11 +53,11 @@ For several processes, other languages, HA or replication, run the regular [Arca | Package | What it is | Status | |---|---|---| -| [`arcadedb-embedded`](https://pypi.org/project/arcadedb-embedded/) | These bindings: the engine on a bundled JVM. The package this README describes | Release 26.10.2.post1 | -| [`arcadedb-embedded-native`](https://pypi.org/project/arcadedb-embedded-native/) | The same API with the engine compiled ahead of time by GraalVM Community Edition: no JVM, no JPype. Import `arcadedb_native`, Python 3.11 or later | Preview of alpha quality: `pip install --pre arcadedb-embedded-native`, currently 26.10.2.dev1 | -| [`arcadedb`](https://pypi.org/project/arcadedb/) and [`arcadedb-native`](https://pypi.org/project/arcadedb-native/) | Names reserved for the project. Today `arcadedb` 0.0.1 only installs `arcadedb-embedded`, and `arcadedb-native` 26.10.2.dev1 only installs `arcadedb-embedded-native` | Placeholders | +| [`arcadedb`](https://pypi.org/project/arcadedb/) | These bindings: the engine on a bundled JVM. The package this README describes | First release from this repository: 26.11.1 | +| [`arcadedb-native`](https://pypi.org/project/arcadedb-native/) | The same API with the engine compiled ahead of time by GraalVM Community Edition: no JVM, no JPype. Python 3.11 or later. It imports as `arcadedb`, like the JVM wheel, so install only one of the two | Not in this repository yet (porting is tracked in [#19](https://github.com/ArcadeData/arcadedb-python/issues/19)). Until then a placeholder on PyPI | +| Formerly `arcadedb-embedded` and `arcadedb-embedded-native` | The names of these packages until 26.10.x. They import as `arcadedb_embedded` and `arcadedb_native` | Deprecated: install `arcadedb` | -Two things are not true yet. The native build is not in this repository (porting is tracked in [#19](https://github.com/ArcadeData/arcadedb-python/issues/19)), and releases do not come from this repository: the wheels on PyPI are still built and published from [humemai/arcadedb-embedded-python](https://github.com/humemai/arcadedb-embedded-python). A release workflow for this repository is in review in [#22](https://github.com/ArcadeData/arcadedb-python/pull/22). +Releases from this repository start with 26.11.1. The 26.10.x wheels were built and published from [humemai/arcadedb-embedded-python](https://github.com/humemai/arcadedb-embedded-python). ## Documentation @@ -80,11 +82,11 @@ Tests run against the built wheel, not the source tree: install the wheel with i ## Contributing -Open an [issue](https://github.com/ArcadeData/arcadedb-python/issues) or a pull request. Run `pre-commit run --all-files` first, and add a regression test with every fix. The rest of the bindings work from `humemai/arcadedb-embedded-python` is ported here one pull request at a time; [#19](https://github.com/ArcadeData/arcadedb-python/issues/19) lists what is done and what is next. +Open an [issue](https://github.com/ArcadeData/arcadedb-python/issues) or a pull request. Run `pre-commit run --all-files` first, and add a regression test with every fix. The rest of the bindings work from `humemai/arcadedb-embedded-python` is ported here one pull request at a time; [#19](https://github.com/ArcadeData/arcadedb-python/issues/19) lists what is done and what is next. ## Where this code comes from -It moved here from `bindings/python` in [ArcadeData/arcadedb](https://github.com/ArcadeData/arcadedb) at `ee04bd202abce0183c6263a3c4d8a61a4d526960`, with its git history. Issue numbers in older commit messages refer to that repository. [humemai/arcadedb-embedded-python](https://github.com/humemai/arcadedb-embedded-python) is the fork where the bindings kept developing (runtime fixes, JRE trim, native build, benchmarks). Its benchmark tooling stays there. +It moved here from `bindings/python` in [ArcadeData/arcadedb](https://github.com/ArcadeData/arcadedb) at `ee04bd202abce0183c6263a3c4d8a61a4d526960`, with its git history. Issue numbers in older commit messages refer to that repository. [humemai/arcadedb-embedded-python](https://github.com/humemai/arcadedb-embedded-python) is the fork where the bindings kept developing (runtime fixes, JRE trim, native build, benchmarks). Its benchmark tooling stays there. ## License diff --git a/docs/antora.yml b/docs/antora.yml index c8ad8059..857714c1 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -6,20 +6,17 @@ nav: - modules/ROOT/nav.adoc asciidoc: attributes: - # Package names. After the rename of the distribution these are the only - # lines to change; every page takes the names from here. - pkg-name: arcadedb-embedded - import-name: arcadedb_embedded - import-stmt: import arcadedb_embedded as arcadedb - native-pkg-name: arcadedb-embedded-native - native-import-name: arcadedb_native + # Package names. Every page takes them from here. + pkg-name: arcadedb + import-name: arcadedb + import-stmt: import arcadedb + native-pkg-name: arcadedb-native + native-import-name: arcadedb # Other names a reader meets on PyPI (the pages that compare packages use these). - short-pkg-name: arcadedb - short-native-pkg-name: arcadedb-native jvm-pkg-name: arcadedb-jvm driver-pkg-name: arcadedb-driver grpc-driver-pkg-name: arcadedb-driver-grpc community-driver-pkg-name: arcadedb-python community-repo-url: https://github.com/stevereiner/arcadedb-python repo-url: https://github.com/ArcadeData/arcadedb-python - pypi-url: https://pypi.org/project/arcadedb-embedded/ + pypi-url: https://pypi.org/project/arcadedb/ diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 3adb6b7e..aa89c6c2 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -2,7 +2,7 @@ :description: Run the ArcadeDB engine inside your Python process: install, first steps and how-to guides for the {pkg-name} package. // Sources: bindings docs index.md, api-access-methods.md and getting-started/installation.md, -// and the PyPI description of arcadedb-embedded-native. +// and the PyPI description of arcadedb-native. `{pkg-name}` runs the ArcadeDB engine inside your Python process. Calls cross into the engine through https://jpype.readthedocs.io/[JPype], so there is no network hop and no server to start. The wheel bundles its own Java runtime (Java 25), so you do not install Java. @@ -83,7 +83,7 @@ Two packages ship for now. Both give you the same engine and the same API. | `pip install --pre {native-pkg-name}` |=== -The preview installs beside the default package. Its public API follows the one of `{import-name}`, so the pages in this component apply to both; the JVM-specific parts (JVM options, `jvm_kwargs`) do not apply to the native build. Check the preview's https://pypi.org/project/{native-pkg-name}/[PyPI page] for its current status before you rely on it. +Both packages import as `{import-name}`, so an environment holds one of them. The preview's public API follows the one of the default package, so the pages in this component apply to both; the JVM-specific parts (JVM options, `jvm_kwargs`) do not apply to the native build. Check the preview's https://pypi.org/project/{native-pkg-name}/[PyPI page] for its current status before you rely on it. == Requirements diff --git a/docs/modules/ROOT/pages/installation.adoc b/docs/modules/ROOT/pages/installation.adoc index 8ce75492..2f35eeeb 100644 --- a/docs/modules/ROOT/pages/installation.adoc +++ b/docs/modules/ROOT/pages/installation.adoc @@ -2,7 +2,7 @@ :description: Install the {pkg-name} wheel, check that it works, and set JVM options. // Sources: bindings docs getting-started/installation.md, the PyPI metadata of -// arcadedb-embedded and arcadedb-embedded-native. +// arcadedb and arcadedb-native. == Install @@ -11,6 +11,8 @@ pip install {pkg-name} ---- +This package was published as arcadedb-embedded until 26.10.x; install arcadedb now. + Or with https://docs.astral.sh/uv/[uv]: [source,shell,subs="attributes+"] @@ -136,7 +138,7 @@ Each Python process starts its own JVM, and each JVM sees the whole container li pip install --pre {native-pkg-name} ---- -It installs beside `{pkg-name}`: the distribution names differ, and so do the import names (`{native-import-name}` instead of `{import-name}`). Optional extras install the columnar helpers: `[numpy]`, `[arrow]`, `[pandas]` and `[polars]`. Report problems in the {repo-url}/issues[issue tracker] and say that you use the native wheel. +It imports as `{native-import-name}`, the same name as `{pkg-name}`, so install only one of the two in an environment. If both are installed, importing `{import-name}` prints a `RuntimeWarning`, because pip overwrites one with the other without a message. Optional extras install the columnar helpers: `[numpy]`, `[arrow]`, `[pandas]` and `[polars]`. Report problems in the {repo-url}/issues[issue tracker] and say that you use the native wheel. == Next steps diff --git a/docs/modules/ROOT/pages/packages.adoc b/docs/modules/ROOT/pages/packages.adoc index cc96422f..49f55630 100644 --- a/docs/modules/ROOT/pages/packages.adoc +++ b/docs/modules/ROOT/pages/packages.adoc @@ -41,17 +41,9 @@ Which build is ahead depends on whether you measure the first pass after a start | Embedded, GraalVM native build (preview) | The same API with the engine compiled ahead of time: no JVM and no JPype. Import it as `{native-import-name}`. -| `{short-pkg-name}` -| Embedded, JVM build -| The name `{pkg-name}` moves to in the middle of November. Until then a placeholder on PyPI that installs `{pkg-name}`. - -| `{short-native-pkg-name}` -| Embedded, native build (preview) -| The name `{native-pkg-name}` moves to at the same time. Until then an alias that installs it. - | `{jvm-pkg-name}` | Embedded, JVM build -| A reserved name for the JVM build. Until the move it installs `{pkg-name}`. +| A reserved name for the JVM build. It installs `{pkg-name}`. |=== [[drivers]] diff --git a/examples/01_simple_document_store.py b/examples/01_simple_document_store.py index 04e93d02..c982c05b 100644 --- a/examples/01_simple_document_store.py +++ b/examples/01_simple_document_store.py @@ -17,7 +17,7 @@ - Not using VERTEX/EDGE types (those are for graph operations) Requirements: -- arcadedb-embedded +- arcadedb Note: This example creates a database at ./my_test_databases/task_db/ The database files are preserved so you can inspect them after running. @@ -37,7 +37,7 @@ import uuid from datetime import date, datetime -import arcadedb_embedded as arcadedb +import arcadedb print("=" * 70) print("๐ŸŽฎ ArcadeDB Python - Example 01: Simple Document Store") diff --git a/examples/02_social_network_graph.py b/examples/02_social_network_graph.py index dd9906b7..5db8926d 100644 --- a/examples/02_social_network_graph.py +++ b/examples/02_social_network_graph.py @@ -27,7 +27,7 @@ - SQL MATCH and OpenCypher performance varies by dataset and runtime configuration Requirements: -- Python embedded ArcadeDB (arcadedb_embedded package) +- Python embedded ArcadeDB (arcadedb package) Usage: - Run this example from the examples/ directory: @@ -40,7 +40,7 @@ import sys import time -import arcadedb_embedded as arcadedb +import arcadedb def main(): @@ -285,7 +285,7 @@ def create_sample_data(db): columns = list(properties.keys()) assignments = ", ".join(f"{col} = ?" for col in columns) - from arcadedb_embedded.type_conversion import convert_python_to_java + from arcadedb.type_conversion import convert_python_to_java values = [ convert_python_to_java(properties[col]) for col in columns diff --git a/examples/03_vector_search.py b/examples/03_vector_search.py index d5ebd503..ca7a81d9 100644 --- a/examples/03_vector_search.py +++ b/examples/03_vector_search.py @@ -24,7 +24,7 @@ - Question answering (find relevant context) Requirements: -- arcadedb-embedded +- arcadedb - NumPy (for vector operations and mock embeddings) Note: This example uses mock embeddings for demonstration. In production: @@ -48,7 +48,7 @@ import jpype.types as jtypes import numpy as np -import arcadedb_embedded as arcadedb +import arcadedb # Parse command line arguments parser = argparse.ArgumentParser( diff --git a/examples/04_csv_import_documents.py b/examples/04_csv_import_documents.py index 655855b1..33a128fa 100644 --- a/examples/04_csv_import_documents.py +++ b/examples/04_csv_import_documents.py @@ -41,7 +41,7 @@ - STRING, BOOLEAN, DATE, DATETIME, BINARY, EMBEDDED, LIST Requirements: -- arcadedb-embedded +- arcadedb - MovieLens dataset (downloaded via download_data.py) - Sufficient JVM heap memory (8GB recommended for large dataset) @@ -81,7 +81,7 @@ import time from pathlib import Path -import arcadedb_embedded as arcadedb +import arcadedb # Define test queries used for validation # NOTE: ORDER BY clauses ensure consistent results across runs (important for validation) diff --git a/examples/05_csv_import_graph.py b/examples/05_csv_import_graph.py index 37d297bd..a2f44653 100644 --- a/examples/05_csv_import_graph.py +++ b/examples/05_csv_import_graph.py @@ -166,7 +166,7 @@ import numpy as np -import arcadedb_embedded as arcadedb +import arcadedb # Edge statements with every value bound as a ? parameter: the two endpoint # RIDs, then the edge properties. One statement text for every edge also lets diff --git a/examples/06_vector_search_recommendations.py b/examples/06_vector_search_recommendations.py index 932496a3..88d07f27 100644 --- a/examples/06_vector_search_recommendations.py +++ b/examples/06_vector_search_recommendations.py @@ -49,7 +49,7 @@ import numpy as np -import arcadedb_embedded as arcadedb +import arcadedb # Try to import sentence_transformers try: diff --git a/examples/07_stackoverflow_tables_oltp.py b/examples/07_stackoverflow_tables_oltp.py index a6883a32..bd62cdd6 100644 --- a/examples/07_stackoverflow_tables_oltp.py +++ b/examples/07_stackoverflow_tables_oltp.py @@ -315,8 +315,8 @@ def get_duckdb_module(): def get_arcadedb_module(): try: - import arcadedb_embedded as arcadedb - from arcadedb_embedded.exceptions import ArcadeDBError + import arcadedb + from arcadedb.exceptions import ArcadeDBError except ImportError: return None, None return arcadedb, ArcadeDBError @@ -1101,7 +1101,7 @@ def run_oltp_arcadedb( ) -> dict: arcadedb, arcade_error = get_arcadedb_module() if arcadedb is None or arcade_error is None: - raise RuntimeError("arcadedb-embedded is not installed") + raise RuntimeError("arcadedb is not installed") if db_path.exists(): shutil.rmtree(db_path) diff --git a/examples/08_stackoverflow_tables_olap.py b/examples/08_stackoverflow_tables_olap.py index c7080b02..b4a0028c 100644 --- a/examples/08_stackoverflow_tables_olap.py +++ b/examples/08_stackoverflow_tables_olap.py @@ -83,8 +83,8 @@ def build_benchmark_db_name( def get_arcadedb_module(): try: - import arcadedb_embedded as arcadedb - from arcadedb_embedded.exceptions import ArcadeDBError + import arcadedb + from arcadedb.exceptions import ArcadeDBError except ImportError: return None, None return arcadedb, ArcadeDBError @@ -1558,7 +1558,7 @@ def run_olap_arcadedb( ) -> dict: arcadedb, arcade_error = get_arcadedb_module() if arcadedb is None or arcade_error is None: - raise RuntimeError("arcadedb-embedded is not installed") + raise RuntimeError("arcadedb is not installed") if db_path.exists(): shutil.rmtree(db_path) diff --git a/examples/09_stackoverflow_graph_oltp.py b/examples/09_stackoverflow_graph_oltp.py index 58b1e891..cab2c75a 100644 --- a/examples/09_stackoverflow_graph_oltp.py +++ b/examples/09_stackoverflow_graph_oltp.py @@ -288,8 +288,8 @@ def budget_allocation_report(args) -> dict: def get_arcadedb_module(): try: - import arcadedb_embedded as arcadedb - from arcadedb_embedded.exceptions import ArcadeDBError + import arcadedb + from arcadedb.exceptions import ArcadeDBError except ImportError: return None, None return arcadedb, ArcadeDBError @@ -4152,7 +4152,7 @@ def run_graph_oltp_arcadedb( ) -> dict: arcadedb, arcade_error = get_arcadedb_module() if arcadedb is None or arcade_error is None: - raise RuntimeError("arcadedb-embedded is not installed") + raise RuntimeError("arcadedb is not installed") if db_path.exists(): shutil.rmtree(db_path) diff --git a/examples/10_stackoverflow_graph_olap.py b/examples/10_stackoverflow_graph_olap.py index 041c140d..37f04f26 100644 --- a/examples/10_stackoverflow_graph_olap.py +++ b/examples/10_stackoverflow_graph_olap.py @@ -353,8 +353,8 @@ def budget_allocation_report(args) -> dict: def get_arcadedb_module(): try: - import arcadedb_embedded as arcadedb - from arcadedb_embedded.exceptions import ArcadeDBError + import arcadedb + from arcadedb.exceptions import ArcadeDBError except ImportError: return None, None return arcadedb, ArcadeDBError @@ -4224,7 +4224,7 @@ def run_olap_arcadedb( ) -> dict: arcadedb, arcade_error = get_arcadedb_module() if arcadedb is None or arcade_error is None: - raise RuntimeError("arcadedb-embedded is not installed") + raise RuntimeError("arcadedb is not installed") if db_path.exists(): shutil.rmtree(db_path) diff --git a/examples/11_vector_index_build.py b/examples/11_vector_index_build.py index d23d632b..87dde751 100644 --- a/examples/11_vector_index_build.py +++ b/examples/11_vector_index_build.py @@ -531,7 +531,7 @@ def ingest_vectors_arcadedb( if use_int8_encoding and to_java_byte_array is None: raise RuntimeError( - "arcadedb_embedded does not expose to_java_byte_array; update the " + "arcadedb does not expose to_java_byte_array; update the " "local wheel before using --encoding INT8" ) @@ -2113,7 +2113,7 @@ def record(name: str, result, dur: float, rss_start: float, rss_end: float): if args.backend == "arcadedb_sql": stop_cpu = start_cpu_logger(2) - import arcadedb_embedded as arcadedb + import arcadedb jvm_kwargs: Dict[str, str] = {} heap_size = arcadedb_heap_size diff --git a/examples/12_vector_search.py b/examples/12_vector_search.py index fa23f1b7..59e94960 100644 --- a/examples/12_vector_search.py +++ b/examples/12_vector_search.py @@ -2313,7 +2313,7 @@ def record_phase( runtime_versions: dict[str, str | None] = {} if args.backend == "arcadedb_sql": - import arcadedb_embedded as arcadedb + import arcadedb stop_cpu = start_cpu_logger(2) jvm_kwargs: Dict[str, str] = {} diff --git a/examples/13_stackoverflow_hybrid_queries.py b/examples/13_stackoverflow_hybrid_queries.py index 67f4b256..fe7ca4a1 100644 --- a/examples/13_stackoverflow_hybrid_queries.py +++ b/examples/13_stackoverflow_hybrid_queries.py @@ -56,7 +56,7 @@ def get_arcadedb_module(): try: - import arcadedb_embedded as arcadedb + import arcadedb except ImportError: return None return arcadedb @@ -451,7 +451,7 @@ def create_indexes_with_retry( retry_delay: int, max_retries: int, ) -> float: - from arcadedb_embedded import ArcadeDBError + from arcadedb import ArcadeDBError start = time.time() for table, props, unique in index_defs: @@ -573,7 +573,7 @@ def flush() -> None: def insert_table_batch(db, table_name: str, batch: List[Dict[str, Any]]) -> None: - from arcadedb_embedded.type_conversion import convert_python_to_java + from arcadedb.type_conversion import convert_python_to_java with db.transaction(): for record in batch: @@ -1259,7 +1259,7 @@ def count_graph(db) -> Dict[str, Any]: def ensure_embedding_properties(db) -> None: - from arcadedb_embedded import ArcadeDBError + from arcadedb import ArcadeDBError for vertex_type in ("Question", "Answer", "Comment"): for prop_name, prop_type in ( @@ -1399,7 +1399,7 @@ def timed_step(name: str, fn: Callable[[], Any]) -> Dict[str, Any]: def create_activity_timeseries_type(db) -> Tuple[bool, Optional[str]]: - from arcadedb_embedded import ArcadeDBError + from arcadedb import ArcadeDBError try: db.command( @@ -2279,8 +2279,8 @@ def main() -> None: arcadedb = get_arcadedb_module() if arcadedb is None: - print("Missing dependency: arcadedb-embedded") - print("Install with: pip install arcadedb-embedded") + print("Missing dependency: arcadedb") + print("Install with: pip install arcadedb") sys.exit(1) SentenceTransformer = get_sentence_transformer_class() diff --git a/examples/14_lifecycle_timing.py b/examples/14_lifecycle_timing.py index 8b9dee7f..08c94934 100644 --- a/examples/14_lifecycle_timing.py +++ b/examples/14_lifecycle_timing.py @@ -25,8 +25,8 @@ from pathlib import Path from typing import Dict, List -import arcadedb_embedded as arcadedb -from arcadedb_embedded.jvm import start_jvm +import arcadedb +from arcadedb.jvm import start_jvm def server_open_close_timing(root: str, rounds: int = 3) -> None: diff --git a/examples/15_import_database_vs_transactional_table_ingest.py b/examples/15_import_database_vs_transactional_table_ingest.py index 90675516..af79a0ad 100644 --- a/examples/15_import_database_vs_transactional_table_ingest.py +++ b/examples/15_import_database_vs_transactional_table_ingest.py @@ -51,7 +51,7 @@ from pathlib import Path from typing import Dict, List, Tuple -import arcadedb_embedded as arcadedb +import arcadedb ColumnDef = Tuple[str, str] diff --git a/examples/16_import_database_vs_transactional_graph_ingest.py b/examples/16_import_database_vs_transactional_graph_ingest.py index 7089b173..7f8dc619 100644 --- a/examples/16_import_database_vs_transactional_graph_ingest.py +++ b/examples/16_import_database_vs_transactional_graph_ingest.py @@ -76,7 +76,7 @@ from pathlib import Path from typing import Dict, List, Tuple -import arcadedb_embedded as arcadedb +import arcadedb ColumnDef = Tuple[str, str] diff --git a/examples/17_timeseries_end_to_end.py b/examples/17_timeseries_end_to_end.py index 88c43816..7f478167 100644 --- a/examples/17_timeseries_end_to_end.py +++ b/examples/17_timeseries_end_to_end.py @@ -33,7 +33,7 @@ from dataclasses import dataclass from datetime import datetime, timezone -import arcadedb_embedded as arcadedb +import arcadedb START_TS_MS = 1_710_000_000_000 diff --git a/examples/18_geo_predicates_wkt.py b/examples/18_geo_predicates_wkt.py index 4b6cd248..5604c2fe 100644 --- a/examples/18_geo_predicates_wkt.py +++ b/examples/18_geo_predicates_wkt.py @@ -27,7 +27,7 @@ import shutil from pathlib import Path -import arcadedb_embedded as arcadedb +import arcadedb DEPOTS = [ { diff --git a/examples/19_hash_index_exact_match.py b/examples/19_hash_index_exact_match.py index 0b791f14..2bf7edf5 100644 --- a/examples/19_hash_index_exact_match.py +++ b/examples/19_hash_index_exact_match.py @@ -28,7 +28,7 @@ import shutil from pathlib import Path -import arcadedb_embedded as arcadedb +import arcadedb PRODUCTS = [ { diff --git a/examples/20_graph_algorithms_route_planning.py b/examples/20_graph_algorithms_route_planning.py index effd425b..95318ed5 100644 --- a/examples/20_graph_algorithms_route_planning.py +++ b/examples/20_graph_algorithms_route_planning.py @@ -33,7 +33,7 @@ import shutil from pathlib import Path -import arcadedb_embedded as arcadedb +import arcadedb SAFE_IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") diff --git a/examples/21_graph_analytical_view_sql.py b/examples/21_graph_analytical_view_sql.py index 4ac54030..20ff05b5 100644 --- a/examples/21_graph_analytical_view_sql.py +++ b/examples/21_graph_analytical_view_sql.py @@ -42,7 +42,7 @@ import time from pathlib import Path -import arcadedb_embedded as arcadedb +import arcadedb GAV_NAME = "roadNet" CITY_CODE_WIDTH = 6 diff --git a/examples/22_numpy_bulk_io.py b/examples/22_numpy_bulk_io.py index 9ced6a5a..aaa59e0e 100644 --- a/examples/22_numpy_bulk_io.py +++ b/examples/22_numpy_bulk_io.py @@ -22,7 +22,7 @@ Requirements: - numpy (pip install numpy) - pyarrow, optional, only for the to_arrow section - (pip install 'arcadedb-embedded[arrow]') + (pip install 'arcadedb[arrow]') """ from __future__ import annotations @@ -34,7 +34,7 @@ import numpy as np -import arcadedb_embedded as arcadedb +import arcadedb def bulk_documents(db, n_rows: int) -> None: @@ -167,7 +167,7 @@ def nulls_to_arrow(db, n_rows: int) -> None: if table is None: print( "to_arrow : pyarrow not installed, skipping " - "(pip install 'arcadedb-embedded[arrow]')" + "(pip install 'arcadedb[arrow]')" ) return diff --git a/examples/23_server_mode_http_access.py b/examples/23_server_mode_http_access.py index 7414deb6..9e792a35 100644 --- a/examples/23_server_mode_http_access.py +++ b/examples/23_server_mode_http_access.py @@ -33,7 +33,7 @@ from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen -import arcadedb_embedded as arcadedb +import arcadedb ROOT_PASSWORD = "example23pass" ITEMS = [ diff --git a/examples/24_server_http_transactions_timeseries.py b/examples/24_server_http_transactions_timeseries.py index 31f948a0..fc6703e4 100644 --- a/examples/24_server_http_transactions_timeseries.py +++ b/examples/24_server_http_transactions_timeseries.py @@ -28,7 +28,7 @@ import urllib.request from pathlib import Path -import arcadedb_embedded as arcadedb +import arcadedb DB = "httpdemo" diff --git a/examples/25_sparse_quantization_and_compact.py b/examples/25_sparse_quantization_and_compact.py index cbc1f1ce..2bca0531 100644 --- a/examples/25_sparse_quantization_and_compact.py +++ b/examples/25_sparse_quantization_and_compact.py @@ -27,7 +27,7 @@ import jpype.types as jtypes -import arcadedb_embedded as arcadedb +import arcadedb DIMENSIONS = 30_000 TOKENS_PER_DOC = 40 diff --git a/examples/26_cross_model_transaction_atomicity.py b/examples/26_cross_model_transaction_atomicity.py index b543ab20..f2b1469e 100644 --- a/examples/26_cross_model_transaction_atomicity.py +++ b/examples/26_cross_model_transaction_atomicity.py @@ -23,7 +23,7 @@ import shutil import time -import arcadedb_embedded as arcadedb +import arcadedb DIM = 16 K = 5 diff --git a/examples/27_dataframe_arrow_ingest.py b/examples/27_dataframe_arrow_ingest.py index 3287699c..2cdb1d6d 100644 --- a/examples/27_dataframe_arrow_ingest.py +++ b/examples/27_dataframe_arrow_ingest.py @@ -17,7 +17,7 @@ Requirements: - pandas, polars and pyarrow - (pip install 'arcadedb-embedded[pandas,polars]') + (pip install 'arcadedb[pandas,polars]') """ from __future__ import annotations @@ -31,7 +31,7 @@ import polars as pl import pyarrow as pa -import arcadedb_embedded as arcadedb +import arcadedb def documents_from_pandas(db, rows: int) -> None: diff --git a/examples/28_python_functions_and_record_events.py b/examples/28_python_functions_and_record_events.py index 30fd35ae..fd73ca0a 100644 --- a/examples/28_python_functions_and_record_events.py +++ b/examples/28_python_functions_and_record_events.py @@ -26,7 +26,7 @@ import shutil import time -import arcadedb_embedded as arcadedb +import arcadedb def parse_args() -> argparse.Namespace: diff --git a/examples/29_hybrid_memory_retrieval.py b/examples/29_hybrid_memory_retrieval.py index 0c3fac4e..c184b4bd 100644 --- a/examples/29_hybrid_memory_retrieval.py +++ b/examples/29_hybrid_memory_retrieval.py @@ -48,7 +48,7 @@ import numpy as np -import arcadedb_embedded as arcadedb +import arcadedb SEED = 20261024 DIM = 16 @@ -325,7 +325,7 @@ def main() -> None: for t in ("Fact", "Entity", "MENTIONS", "RELATES") } print(f"memory: {counts}, tenants: {len(TENANTS)}") - print(f"engine: arcadedb_embedded {arcadedb.__version__}, form: {args.form}") + print(f"engine: arcadedb {arcadedb.__version__}, form: {args.form}") out = run(db, centroids, args.form) print(f"\nrequested tenant: {REQUESTED_TENANT}, k = {K}") diff --git a/examples/README.md b/examples/README.md index c926be53..999802ac 100644 --- a/examples/README.md +++ b/examples/README.md @@ -401,7 +401,7 @@ DataFrames and Arrow tables loaded straight into a type, and a result back out a column refused before a row is written - `to_polars()` on the result - synthetic data; flags: `--rows` (default 100,000) and `--db-path` -- requires `pip install 'arcadedb-embedded[pandas,polars]'` +- requires `pip install 'arcadedb[pandas,polars]'` **Learn:** How a DataFrame becomes documents or vertices without a row loop, and what each Arrow type is stored as diff --git a/examples/download_data.py b/examples/download_data.py index 0fe5ca06..b8d88bf6 100644 --- a/examples/download_data.py +++ b/examples/download_data.py @@ -749,7 +749,7 @@ def report_progress(block_num, block_size, total_size): # than report a bindings regression that did not happen. print(f"[UNAVAILABLE] Cannot reach the dataset host: {e}") print(f" URL: {url}") - print(" This is a third-party host, not a defect in arcadedb-embedded.") + print(" This is a third-party host, not a defect in arcadedb.") raise DatasetUnavailable(f"{url}: {e}") from e except Exception as e: print(f"[ERROR] Error downloading dataset: {e}") @@ -953,7 +953,7 @@ def has_required_xml_files(dataset_dir: Path) -> bool: # from a broken binding. print(f"[UNAVAILABLE] Cannot reach the dataset host: {e}") print(f" URL: {config['urls'][0]}") - print(" This is a third-party host, not a defect in arcadedb-embedded.") + print(" This is a third-party host, not a defect in arcadedb.") raise DatasetUnavailable(f"{config['urls'][0]}: {e}") from e except Exception as e: print(f"[ERROR] Error downloading dataset: {e}") diff --git a/pyproject.toml b/pyproject.toml index 3b27e692..50973721 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,12 +5,12 @@ requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" -# This file defines the package metadata for arcadedb-embedded. +# This file defines the package metadata for arcadedb. # During build, the version is overwritten from the bundled ArcadeDB engine jar via # scripts/extract_version.py [project] -name = "arcadedb-embedded" +name = "arcadedb" version = "0.0.0" description = "ArcadeDB embedded multi-model database with bundled JRE - no Java installation required" readme = "README.md" @@ -106,7 +106,7 @@ pandas = [ ] # Optional: ResultSet.to_polars() and insert_dataframe() with a Polars frame. Both go # through Arrow, so pyarrow comes with it. insert_dataframe() with a pandas frame needs -# `pip install 'arcadedb-embedded[arrow,pandas]'` (the pandas extra alone has no pyarrow, +# `pip install 'arcadedb[arrow,pandas]'` (the pandas extra alone has no pyarrow, # because to_dataframe() does not need it). polars = [ "polars>=1.0", @@ -118,19 +118,19 @@ examples = [ ] [project.urls] -Homepage = "https://github.com/humemai/arcadedb-embedded-python" +Homepage = "https://github.com/ArcadeData/arcadedb-python" Documentation = "https://docs.humem.ai/arcadedb/" -Repository = "https://github.com/humemai/arcadedb-embedded-python" -"Bug Tracker" = "https://github.com/humemai/arcadedb-embedded-python/issues" +Repository = "https://github.com/ArcadeData/arcadedb-python" +"Bug Tracker" = "https://github.com/ArcadeData/arcadedb-python/issues" "ArcadeDB Upstream" = "https://github.com/ArcadeData/arcadedb" [tool.setuptools] -packages = ["arcadedb_embedded"] +packages = ["arcadedb"] package-dir = { "" = "src" } include-package-data = true [tool.setuptools.package-data] -arcadedb_embedded = [ +arcadedb = [ "jars/*.jar", "jre/**/*", ] diff --git a/scripts/Dockerfile.build b/scripts/Dockerfile.build index a2c07583..16bc5494 100644 --- a/scripts/Dockerfile.build +++ b/scripts/Dockerfile.build @@ -2,17 +2,17 @@ # SPDX-License-Identifier: Apache-2.0 # # Docker build for ArcadeDB Python package -# Builds arcadedb-embedded package with bundled JRE (no external Java required) +# Builds arcadedb package with bundled JRE (no external Java required) # Excludes JARs listed in scripts/jar_exclusions.txt to optimize size # # REQUIRED BUILD ARGS (passed from build.sh): # PYTHON_VERSION - Python version for wheel (e.g., 3.11, 3.12, 3.13, 3.14) -# PACKAGE_NAME - always: arcadedb-embedded +# PACKAGE_NAME - always: arcadedb # PACKAGE_DESCRIPTION - package description # TARGET_PLATFORM - Linux target platform for bundled JRE (linux-x64, linux-arm64) ARG PYTHON_VERSION=3.12 -ARG PACKAGE_NAME=arcadedb-embedded +ARG PACKAGE_NAME=arcadedb ARG PACKAGE_DESCRIPTION="ArcadeDB embedded multi-model database with bundled JRE - no Java installation required" # Deterministic placeholder. The wrapper build script must pass the real ArcadeDB tag. ARG ARCADEDB_TAG=build-arg-required @@ -313,7 +313,7 @@ COPY --from=python-builder /build/tests ./tests/ # Create a quick installation test RUN echo '#!/usr/bin/env python3\n\ -import arcadedb_embedded as arcadedb\n\ +import arcadedb\n\ import tempfile\n\ import shutil\n\ import os\n\ diff --git a/scripts/arrow_transport_probe.py b/scripts/arrow_transport_probe.py index c422b799..98a02d5c 100644 --- a/scripts/arrow_transport_probe.py +++ b/scripts/arrow_transport_probe.py @@ -75,7 +75,7 @@ def main(): import pandas as pd - import arcadedb_embedded as arcadedb + import arcadedb try: import pyarrow # noqa: F401 @@ -94,7 +94,7 @@ def row(*cells): print("\nROW " + "".join(cells), flush=True) print(f"\n rows={args.rows} reps={args.reps} (warmup {args.warmup})") - print(f" arcadedb_embedded {arcadedb.__version__}\n") + print(f" arcadedb {arcadedb.__version__}\n") row( f"{'shape':<10}{'to_columns':>12}{'to_arrow':>11}", f"{'cols->df':>11}{'arrow->df':>11}{'df speedup':>12}", diff --git a/scripts/build-native.sh b/scripts/build-native.sh index d6585676..7df18e33 100755 --- a/scripts/build-native.sh +++ b/scripts/build-native.sh @@ -25,7 +25,7 @@ JRE_MODULE_EXCLUSIONS_FILE="$SCRIPT_DIR/jre_module_exclusions.txt" # Parameters PLATFORM="${1:-}" -PACKAGE_NAME="${2:-arcadedb-embedded}" +PACKAGE_NAME="${2:-arcadedb}" PACKAGE_DESCRIPTION="${3:-ArcadeDB embedded multi-model database with bundled JRE}" ARCADEDB_TAG="${4:-}" BUILD_VERSION="${5:-}" @@ -172,7 +172,7 @@ fi echo "" # Step 1: Stage the ArcadeDB JARs, on every build (scripts/stage_jars.sh) -JARS_DIR="$PY_BINDINGS_DIR/src/arcadedb_embedded/jars" +JARS_DIR="$PY_BINDINGS_DIR/src/arcadedb/jars" # shellcheck source=stage_jars.sh source "$SCRIPT_DIR/stage_jars.sh" stage_jars "$JARS_DIR" "$JARS_SOURCE" "$ARCADEDB_TAG" @@ -294,7 +294,7 @@ fi echo -e "${CYAN}๐Ÿ“ฆ Preparing package...${NC}" # Build and copy JRE -copy_tree_with_symlinks "$PY_BINDINGS_DIR/temp_jre" "$PY_BINDINGS_DIR/src/arcadedb_embedded/jre" "$PYTHON_WITH_BUILD" +copy_tree_with_symlinks "$PY_BINDINGS_DIR/temp_jre" "$PY_BINDINGS_DIR/src/arcadedb/jre" "$PYTHON_WITH_BUILD" JAR_COUNT=$(ls -1 "$JARS_DIR"/*.jar | wc -l) echo -e "${GREEN}โœ… Package prepared (${JAR_COUNT} JARs + JRE)${NC}" @@ -378,7 +378,7 @@ WHEEL_FILE=$(ls "$DIST_DIR"/*.whl | head -n1) if [[ -n "$WHEEL_FILE" ]]; then # Extract components from wheel filename WHEEL_NAME=$(basename "$WHEEL_FILE") - # arcadedb_embedded-25.10.1-py3-none-any.whl -> arcadedb_embedded-25.10.1-py3-none-PLAT_NAME.whl + # arcadedb-25.10.1-py3-none-any.whl -> arcadedb-25.10.1-py3-none-PLAT_NAME.whl NEW_WHEEL_NAME=$(echo "$WHEEL_NAME" | sed "s|-py3-none-any\.whl|-py3-none-${PLAT_NAME}.whl|") if [[ "$WHEEL_NAME" != "$NEW_WHEEL_NAME" ]]; then mv "$WHEEL_FILE" "$DIST_DIR/$NEW_WHEEL_NAME" diff --git a/scripts/build.sh b/scripts/build.sh index 30329914..dc2c31d8 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,6 +1,6 @@ #!/bin/bash # ArcadeDB Python Package Build Script -# Builds arcadedb-embedded with a bundled JRE (no Java install needed). +# Builds arcadedb with a bundled JRE (no Java install needed). # JAR sourcing is explicit: provide a JAR directory to embed those artifacts; # otherwise JARs are pulled from the arcadedata/arcadedb image. # @@ -48,7 +48,7 @@ print_header() { print_usage() { echo "Usage: $0 [PLATFORM] [PYTHON_VERSION] [JAR_LIB_DIR]" echo "" - echo "Builds arcadedb-embedded package with bundled JRE" + echo "Builds arcadedb package with bundled JRE" echo "No external Java installation required!" echo "" echo "PLATFORM:" @@ -261,7 +261,7 @@ else fi echo -e "${CYAN}๐Ÿ“‹ Build Configuration:${NC}" -echo -e " Package: ${YELLOW}arcadedb-embedded${NC}" +echo -e " Package: ${YELLOW}arcadedb${NC}" echo -e " Platform: ${YELLOW}${PLATFORM}${NC}" echo -e " Python Version: ${YELLOW}${PYTHON_VERSION}${NC}" echo -e " JAR Source: ${YELLOW}${JAR_SOURCE_DESC}${NC}" @@ -270,7 +270,7 @@ echo -e " Build Method: ${YELLOW}${BUILD_METHOD}${NC}" echo "" # Package configuration -PACKAGE_NAME="arcadedb-embedded" +PACKAGE_NAME="arcadedb" DESCRIPTION="ArcadeDB embedded multi-model database with bundled JRE - no Java installation required" echo -e "${BLUE}โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”${NC}" @@ -447,10 +447,10 @@ def _is_wanted(wheel_path): _new = [Path(p) for p in os.environ.get("NEW_WHEELS", "").split() if p.endswith(".whl")] -wheels = _new or sorted((p for p in Path("dist").glob("arcadedb_embedded-*.whl") if _is_wanted(p)), +wheels = _new or sorted((p for p in Path("dist").glob("arcadedb-*.whl") if _is_wanted(p)), key=lambda p: p.stat().st_mtime) if not wheels: - print(f"โŒ no arcadedb_embedded-{_want} wheel in dist/", file=sys.stderr) + print(f"โŒ no arcadedb-{_want} wheel in dist/", file=sys.stderr) sys.exit(1) wheel = wheels[-1] if not _is_wanted(wheel): @@ -544,6 +544,6 @@ echo "" echo -e "${BLUE}๐Ÿ’ก Next steps:${NC}" echo -e " ๐Ÿงช Run tests against this wheel (from the repository root; uv venv once first):" -echo -e " ${YELLOW}uv pip install --reinstall-package arcadedb-embedded \"[test]\"${NC}" +echo -e " ${YELLOW}uv pip install --reinstall-package arcadedb \"[test]\"${NC}" echo -e " ${YELLOW}uv run pytest${NC}" echo "" diff --git a/scripts/check_no_embedded_name.py b/scripts/check_no_embedded_name.py index a756f3c9..46272a32 100644 --- a/scripts/check_no_embedded_name.py +++ b/scripts/check_no_embedded_name.py @@ -14,7 +14,7 @@ What the allowlist lets through, and why: * Whole files that are history or that must name the old package to do their job: the plan and spec - documents under docs/superpowers/, a changelog, the rename codemod, this script, and the codemod test. + documents under docs/superpowers/, a changelog, and this script. * References to issues of the old repository, written `humemai/arcadedb-embedded-python#123`. They point at real threads, so they keep their text. * The sentence "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." @@ -45,9 +45,7 @@ "CHANGELOG*", "HISTORY*", "NEWS*", - "scripts/rename_to_arcadedb.py", "scripts/check_no_embedded_name.py", - "tests/test_rename_codemod.py", ) # Text that is removed from a line before it is scanned: issue references of the old repository. diff --git a/scripts/extract_version.py b/scripts/extract_version.py index 4f8ceff4..cad4d8a0 100644 --- a/scripts/extract_version.py +++ b/scripts/extract_version.py @@ -82,7 +82,7 @@ python extract_version.py --format=docker # Output: 25.10.1-SNAPSHOT (no conversion) -# Custom jars directory (default: src/arcadedb_embedded/jars) +# Custom jars directory (default: src/arcadedb/jars) python extract_version.py /path/to/jars --format=pep440 ``` @@ -184,8 +184,8 @@ def increment_dev_version_number(base_version): def default_jars_dir(): - """The jars bundled into the package: src/arcadedb_embedded/jars""" - return Path(__file__).resolve().parent.parent / "src" / "arcadedb_embedded" / "jars" + """The jars bundled into the package: src/arcadedb/jars""" + return Path(__file__).resolve().parent.parent / "src" / "arcadedb" / "jars" def extract_raw_version_from_jars(jars_dir): diff --git a/scripts/rename_to_arcadedb.py b/scripts/rename_to_arcadedb.py deleted file mode 100644 index b84a0b02..00000000 --- a/scripts/rename_to_arcadedb.py +++ /dev/null @@ -1,518 +0,0 @@ -#!/usr/bin/env python3 -"""Rename this repository to the final package names. Re-runnable, deterministic, idempotent. - -Final names (owner decision 2026-10-25): distribution `arcadedb` (JVM wheel) and `arcadedb-native` -(native wheel, when ported); the import name is `arcadedb` for BOTH. No `arcadedb_embedded` and no -`arcadedb-embedded` anywhere in this repository. There is no compatibility shim here: the old PyPI -packages get a deprecation shim from the humemai repository. - -Why a script and not a branch full of edits: the rename touches about 130 files, and other pull -requests keep merging into them. A hand-made rename conflicts with each of those. This script is run -on a fresh checkout of main once the open stack has merged, so it never conflicts, and running it -again changes nothing. - -usage (from a clean checkout of main, on the rename branch): - - python3 scripts/rename_to_arcadedb.py # rewrite, format, check - python3 scripts/rename_to_arcadedb.py --dry-run # report only, change nothing - python3 scripts/rename_to_arcadedb.py --no-format # skip the pre-commit formatting pass - -What it does, in order: - -1. Moves `src/arcadedb_embedded` to `src/arcadedb` with `git mv` (file by file when both exist). -2. Applies a few exact-text edits to prose that stops being true: the release workflow comments, the - package attributes in docs/antora.yml, the two statements that the native wheel installs beside the - JVM wheel (it cannot: both import as `arcadedb`), and the rows of the package table for the placeholder - names. Each edit reports "applied", "already", or "MISSING" (the text moved; fix by hand). -3. Rewrites every other use of the old name in every text file, line by line: - `import arcadedb_embedded as arcadedb` becomes `import arcadedb`; `arcadedb-embedded-native` becomes - `arcadedb-native`; the repository URL becomes https://github.com/ArcadeData/arcadedb-python; - `ArcadeDB Embedded Python` becomes `ArcadeDB Python`; any other `arcadedb_embedded` or - `arcadedb-embedded` becomes `arcadedb`. That covers imports, pyproject name, packages and - package-data, build.sh, build-native.sh, Dockerfile.build, setup.py, .gitignore, CI workflow paths - and names, wheel-content checks, the tests and conftest, the examples, README and the Antora pages. -4. Adds the docs line "This package was published as arcadedb-embedded until 26.10.x; install arcadedb - now." to README.md and docs/modules/ROOT/pages/installation.adoc. -5. Wires the check in: a local pre-commit hook and tests/test_no_embedded_name.py, both running - scripts/check_no_embedded_name.py. -6. Runs the repository's pre-commit hooks (black, isort, shfmt) on the Python and shell files it touched - (twice, so the second pass proves them clean) unless --no-format, then the check. - -What it never touches: - -* Java package names (com.arcadedb...), environment variables (ARCADEDB_*), engine artifact names - (arcadedb-engine, the arcadedata/arcadedb image), and every `arcadedb` that is not followed by - `_embedded` or `-embedded`. -* History: files and lines on the allowlist of scripts/check_no_embedded_name.py (docs/superpowers/, - changelogs, the "published as"/"formerly" lines, `humemai/arcadedb-embedded-python#123` issue references). -* The release workflow's guard logic: only two comments that describe the old state are edited. -* The generic word "embedded" (embedded mode, embedded database). - -Output lists what changed by category (path-based) and by rule. Exit 0 when the tree is clean after the -run, 1 when the check still finds the old name, 2 when --strict and an exact-text edit is MISSING. -Output is ASCII only. -""" - -from __future__ import annotations - -import argparse -import importlib.util -import re -import subprocess # nosec B404 - runs git and pre-commit -import sys -from collections import Counter -from pathlib import Path - -OLD_PKG = "arcadedb_embedded" -NEW_PKG = "arcadedb" -OLD_DIR = "src/arcadedb_embedded" -NEW_DIR = "src/arcadedb" - -DOCS_LINE = "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." - - -def _load_checker(): - path = Path(__file__).resolve().with_name("check_no_embedded_name.py") - spec = importlib.util.spec_from_file_location("check_no_embedded_name", path) - assert ( - spec is not None and spec.loader is not None - ) # nosec B101 - import machinery contract - module = importlib.util.module_from_spec(spec) - sys.modules[spec.name] = module - spec.loader.exec_module(module) - return module - - -checker = _load_checker() - -# --- generic rules: (name, pattern, replacement), applied in this order to every line ------------- -RULES: tuple[tuple[str, re.Pattern[str], str], ...] = ( - ( - "repository URL", - re.compile(r"github\.com/humemai/arcadedb-embedded-python"), - "github.com/ArcadeData/arcadedb-python", - ), - ("repository name", re.compile(r"arcadedb-embedded-python"), "arcadedb-python"), - ( - "native distribution name", - re.compile(r"arcadedb-embedded-native"), - "arcadedb-native", - ), - ("project title", re.compile(r"ArcadeDB Embedded Python"), "ArcadeDB Python"), - ( - "import statement", - re.compile(r"\bimport arcadedb_embedded as arcadedb(?![A-Za-z0-9_])"), - "import arcadedb", - ), - ("distribution or package name", re.compile(r"arcadedb[_-]embedded"), "arcadedb"), -) - -# --- exact-text edits: (path, label, old, new). "already" when new is present, "MISSING" otherwise --- -EDITS: tuple[tuple[str, str, str, str], ...] = ( - ( - ".github/workflows/release.yml", - "release.yml header comment", - "# Until the distribution is renamed (tracker #19), pyproject.toml still names it arcadedb-embedded.\n" - "# The guard in the publish job refuses to publish anything that is not `arcadedb`, so this workflow\n" - "# cannot publish arcadedb-embedded from here by accident. The rename is a separate change.\n", - "# pyproject.toml names the distribution arcadedb (tracker #19).\n" - "# The guard in the publish job refuses to publish anything that is not `arcadedb`, so this workflow\n" - "# cannot publish another distribution from here by accident.\n", - ), - ( - ".github/workflows/release.yml", - "release.yml guard comment", - " # GUARD: this workflow publishes the distribution `arcadedb` and nothing else. While the\n" - " # package is still named arcadedb-embedded (tracker #19, rename is a separate change), the\n" - " # job stops here, before anything reaches PyPI. Two checks, so neither can drift alone:\n", - " # GUARD: this workflow publishes the distribution `arcadedb` and nothing else. A wheel with\n" - " # another name stops the job here, before anything reaches PyPI. Two checks, so neither can drift alone:\n", - ), - ( - "docs/antora.yml", - "antora package attributes", - " # Package names. After the rename of the distribution these are the only\n" - " # lines to change; every page takes the names from here.\n", - " # Package names. Every page takes them from here.\n", - ), - ( - "docs/antora.yml", - "antora native import name", - " native-import-name: arcadedb_native\n" - " # Other names a reader meets on PyPI (the pages that compare packages use these).\n" - " short-pkg-name: arcadedb\n" - " short-native-pkg-name: arcadedb-native\n" - " jvm-pkg-name: arcadedb-jvm\n", - " native-import-name: arcadedb\n" - " # Other names a reader meets on PyPI (the pages that compare packages use these).\n" - " jvm-pkg-name: arcadedb-jvm\n", - ), - ( - "docs/modules/ROOT/pages/packages.adoc", - "packages table: placeholder rows", - "| `{short-pkg-name}`\n" - "| Embedded, JVM build\n" - "| The name `{pkg-name}` moves to in the middle of November. Until then a placeholder on PyPI that installs `{pkg-name}`.\n" - "\n" - "| `{short-native-pkg-name}`\n" - "| Embedded, native build (preview)\n" - "| The name `{native-pkg-name}` moves to at the same time. Until then an alias that installs it.\n" - "\n" - "| `{jvm-pkg-name}`\n" - "| Embedded, JVM build\n" - "| A reserved name for the JVM build. Until the move it installs `{pkg-name}`.\n", - "| `{jvm-pkg-name}`\n" - "| Embedded, JVM build\n" - "| A reserved name for the JVM build. It installs `{pkg-name}`.\n", - ), - ( - "docs/modules/ROOT/pages/index.adoc", - "index page: side by side", - "The preview installs beside the default package. Its public API follows the one of `{import-name}`, so the pages in this component apply to both;", - "Both packages import as `{import-name}`, so an environment holds one of them. The preview's public API follows the one of the default package, so the pages in this component apply to both;", - ), - ( - "docs/modules/ROOT/pages/installation.adoc", - "installation page: side by side", - "It installs beside `{pkg-name}`: the distribution names differ, and so do the import names (`{native-import-name}` instead of `{import-name}`).", - "It imports as `{native-import-name}`, the same name as `{pkg-name}`, so install only one of the two in an environment.", - ), - # The docs line, after the generic rewrite has produced the install commands it follows. - ( - "README.md", - "README docs line", - "```bash\nuv add arcadedb # or: pip install arcadedb\n```\n", - "```bash\nuv add arcadedb # or: pip install arcadedb\n```\n\n" - + DOCS_LINE - + "\n", - ), - ( - "docs/modules/ROOT/pages/installation.adoc", - "installation page docs line", - '[source,shell,subs="attributes+"]\n----\npip install {pkg-name}\n----\n', - '[source,shell,subs="attributes+"]\n----\npip install {pkg-name}\n----\n\n' - + DOCS_LINE - + "\n", - ), -) - -# Edits that must run after the generic rewrite (their old text is the rewritten form). -LATE_EDITS = {"README docs line", "installation page docs line"} - -PRE_COMMIT_HOOK = """\ -- repo: local - hooks: - - id: no-old-package-name - name: the old package name does not come back - entry: python3 scripts/check_no_embedded_name.py - language: system - pass_filenames: false - always_run: true -""" - -CHECK_TEST = '''\ -"""The old package name must not come back. - -Runs scripts/check_no_embedded_name.py over the repository. It needs no wheel. The allowlist (history, -the rename codemod, issue references) lives in that script. -""" - -import subprocess # nosec B404 - runs sys.executable on a script this repository owns -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parent.parent -CHECK = ROOT / "scripts" / "check_no_embedded_name.py" - - -def test_old_package_name_is_absent(): - result = subprocess.run( # nosec B603 - fixed argv, no shell - [sys.executable, "-I", str(CHECK), "--root", str(ROOT)], - capture_output=True, - text=True, - check=False, - ) - assert result.returncode == 0, result.stdout + result.stderr -''' - - -def category(rel: str) -> str: - if rel in ("pyproject.toml", "setup.py"): - return "packaging metadata" - if rel.startswith(".github/"): - return "CI workflows" - if rel.startswith("src/"): - return "package source" - if rel.startswith("tests/") and rel.endswith(".py"): - return "tests" - if rel.startswith("examples/") and rel.endswith(".py"): - return "examples" - if rel.startswith("scripts/") or rel in (".gitignore", ".pre-commit-config.yaml"): - return "build and verify scripts" - return "docs" - - -class Report: - def __init__(self) -> None: - self.moves: list[str] = [] - self.files: Counter[str] = Counter() # category -> files changed - self.edits_made: Counter[str] = Counter() # category -> replacements - self.by_rule: Counter[str] = Counter() - self.exact: list[tuple[str, str, str]] = [] # (status, path, label) - self.wiring: list[str] = [] - self.touched: set[str] = ( - set() - ) # paths written or moved, for the formatting pass - self.notes: list[str] = [] - - def show(self, dry: bool) -> None: - mode = " (dry run, nothing written)" if dry else "" - print(f"Rename to arcadedb{mode}\n") - print("Moved paths:") - for m in self.moves or [" none (already moved)"]: - print(m if m.startswith(" ") else f" {m}") - print("\nExact-text edits:") - for status, path, label in self.exact: - print(f" {status:8} {path}: {label}") - print("\nChanged by category (files / replacements):") - cats = sorted(set(self.files) | set(self.edits_made)) - for cat in cats: - print(f" {cat:26} {self.files[cat]:4} / {self.edits_made[cat]}") - print( - f" {'total':26} {sum(self.files.values()):4} / {sum(self.edits_made.values())}" - ) - print("\nChanged by rule (replacements):") - for rule, n in sorted(self.by_rule.items()): - print(f" {rule:36} {n}") - if self.wiring: - print("\nWiring:") - for w in self.wiring: - print(f" {w}") - for n in self.notes: - print(f"\n{n}") - - -def run_git(root: Path, *args: str) -> str: - return subprocess.run( # nosec B603 B607 - fixed git argv, no shell - ["git", "-C", str(root), *args], check=True, capture_output=True, text=True - ).stdout - - -def move_package(root: Path, report: Report, dry: bool) -> None: - old, new = root / OLD_DIR, root / NEW_DIR - if not old.exists(): - return - tracked = [ - p for p in run_git(root, "ls-files", "-z", "--", OLD_DIR).split("\0") if p - ] - if not new.exists(): - report.moves.append( - f"git mv {OLD_DIR} -> {NEW_DIR} ({len(tracked)} tracked files)" - ) - if not dry: - run_git(root, "mv", OLD_DIR, NEW_DIR) - return - # Both exist (a merge brought a new file into the old directory): move file by file. - for rel in tracked: - target = NEW_DIR + rel[len(OLD_DIR) :] - report.moves.append(f"git mv {rel} -> {target}") - if not dry: - (root / target).parent.mkdir(parents=True, exist_ok=True) - run_git(root, "mv", rel, target) - if not dry and old.exists() and not any(old.rglob("*")): - old.rmdir() - - -def text_files(root: Path) -> list[str]: - """Files the rewrite may touch: every repository file that is not on the allowlist.""" - return [f for f in checker.list_files(root) if not checker.is_allowed_path(f)] - - -def read_text(path: Path) -> str | None: - try: - data = path.read_bytes() - except OSError: - return None - if b"\0" in data or len(data) > checker.MAX_BYTES: - return None - try: - return data.decode("utf-8") - except UnicodeDecodeError: - return None - - -def apply_rules(line: str, rules_hit: Counter[str]) -> str: - """Generic rewrite of one line; issue references of the old repository are left as written.""" - if checker.ALLOWED_LINES.search(line): - return line - pieces = re.split(f"({checker.ALLOWED_TOKENS.pattern})", line) - for i in range(0, len(pieces), 2): # even pieces are outside the issue references - for name, pattern, repl in RULES: - pieces[i], n = pattern.subn(repl, pieces[i]) - rules_hit[name] += n - return "".join(pieces) - - -def exact_edit(rel: str, text: str, wanted: set[str], report: Report) -> str: - for path, label, old, new in EDITS: - if path != rel or label not in wanted: - continue - if "\r\n" in text: # a Windows checkout with autocrlf - old, new = old.replace("\n", "\r\n"), new.replace("\n", "\r\n") - if ( - new in text - ): # first: the docs-line edits keep their old text in the new text - report.exact.append(("already", path, label)) - elif old in text: - text = text.replace(old, new, 1) - report.exact.append(("applied", path, label)) - else: - report.exact.append(("MISSING", path, label)) - return text - - -def rewrite_files(root: Path, report: Report, dry: bool) -> None: - early = {label for _, label, _, _ in EDITS if label not in LATE_EDITS} - paths = {p for p, _, _, _ in EDITS} - for rel in text_files(root): - src = root / rel - text = read_text(src) - if text is None: - continue - before = text - rules_hit: Counter[str] = Counter() - if rel in paths: - text = exact_edit(rel, text, early, report) - text = "".join( - apply_rules(line, rules_hit) for line in text.splitlines(keepends=True) - ) - if rel in paths: - text = exact_edit(rel, text, LATE_EDITS, report) - if text != before: - cat = category(rel) - report.files[cat] += 1 - report.edits_made[cat] += sum(rules_hit.values()) + sum( - 1 for s, p, _ in report.exact if p == rel and s == "applied" - ) - report.by_rule.update(rules_hit) - report.touched.add(rel) - if not dry: - src.write_bytes(text.encode("utf-8")) - - -def wire_check(root: Path, report: Report, dry: bool) -> None: - hook_file = root / ".pre-commit-config.yaml" - if hook_file.exists(): - text = hook_file.read_text(encoding="utf-8") - if "check_no_embedded_name.py" in text: - report.wiring.append("already .pre-commit-config.yaml: check hook") - else: - report.wiring.append( - "added .pre-commit-config.yaml: local hook running the check" - ) - if not dry: - hook_file.write_text( - text.rstrip("\n") + "\n" + PRE_COMMIT_HOOK, encoding="utf-8" - ) - test_file = root / "tests" / "test_no_embedded_name.py" - if test_file.exists(): - report.wiring.append("already tests/test_no_embedded_name.py") - elif (root / "tests").is_dir(): - report.wiring.append("added tests/test_no_embedded_name.py") - report.touched.add("tests/test_no_embedded_name.py") - if not dry: - test_file.write_text(CHECK_TEST, encoding="utf-8") - - -def format_tree(root: Path, report: Report) -> bool: - """Format the Python and shell files this run touched, with the hooks the repository already has. - - Only the touched files, and only .py and .sh: `pre-commit run --all-files` would also rewrite files - this run did not change (docs/antora.yml is not clean under the YAML hook on main). The first pass - autofixes; the second proves the files are clean. - """ - files = sorted( - f - for f in report.touched - if f.endswith((".py", ".sh")) - and (root / f).is_file() - and not checker.is_allowed_path(f) - ) - if not files: - report.notes.append("pre-commit: no Python or shell file to format") - return True - for cmd in (["pre-commit"], ["uvx", "pre-commit"]): - try: - for attempt in (1, 2): - result = subprocess.run( # nosec B603 B607 - fixed argv, no shell - [*cmd, "run", "--files", *files], - cwd=root, - capture_output=True, - text=True, - check=False, - ) - if result.returncode == 0: - report.notes.append( - f"pre-commit: {len(files)} file(s) clean after pass {attempt}" - ) - return True - report.notes.append( - "pre-commit: still failing after two passes:\n" + result.stdout[-2000:] - ) - return False - except FileNotFoundError: - continue - report.notes.append( - "pre-commit: not found (tried pre-commit and uvx pre-commit); formatting step skipped" - ) - return True - - -def main(argv: list[str] | None = None) -> int: - parser = argparse.ArgumentParser( - description="Rename arcadedb-embedded to arcadedb (see the module docstring)." - ) - parser.add_argument( - "--root", type=Path, default=Path(__file__).resolve().parent.parent - ) - parser.add_argument( - "--dry-run", action="store_true", help="report what would change, write nothing" - ) - parser.add_argument( - "--no-format", action="store_true", help="skip the pre-commit formatting pass" - ) - parser.add_argument( - "--strict", - action="store_true", - help="exit 2 when an exact-text edit is MISSING", - ) - args = parser.parse_args(argv) - root = args.root.resolve() - report = Report() - - move_package(root, report, args.dry_run) - rewrite_files(root, report, args.dry_run) - wire_check(root, report, args.dry_run) - formatted_ok = True - if not args.dry_run and not args.no_format: - formatted_ok = format_tree(root, report) - report.show(args.dry_run) - - if args.dry_run: - return 0 - leftovers = checker.find_violations(root) - for rel, number, text in leftovers: - print(f"LEFTOVER {rel}:{number}: {text}") - missing = [x for x in report.exact if x[0] == "MISSING"] - print( - f"\ncheck: {len(leftovers)} leftover use(s) of the old name, {len(missing)} MISSING exact edit(s)" - ) - if leftovers or not formatted_ok: - return 1 - if missing and args.strict: - return 2 - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/scripts/setup_jars.py b/scripts/setup_jars.py index aa72fd02..e0f66c1a 100644 --- a/scripts/setup_jars.py +++ b/scripts/setup_jars.py @@ -16,7 +16,7 @@ def find_jar_files(): """Find all ArcadeDB JAR files (already filtered upstream).""" - print("๐Ÿ“ฆ Building arcadedb-embedded with bundled JRE") + print("๐Ÿ“ฆ Building arcadedb with bundled JRE") # Look for JAR files copied from the ArcadeDB Docker image # The Dockerfile copies JARs from the full distribution image to these locations @@ -49,7 +49,7 @@ def find_jar_files(): def copy_jars_to_package(): """Copy JAR files to the Python package.""" package_dir = Path(__file__).parent.parent - jar_dir = package_dir / "src" / "arcadedb_embedded" / "jars" + jar_dir = package_dir / "src" / "arcadedb" / "jars" # Remove any previous jars so the wheel reflects the current build inputs exactly. if jar_dir.exists(): @@ -95,7 +95,7 @@ def copy_jre(): package_dir = Path(__file__).parent.parent jre_source = Path("/build/jre") - jre_dest = package_dir / "src" / "arcadedb_embedded" / "jre" + jre_dest = package_dir / "src" / "arcadedb" / "jre" # Check if JRE source exists if not jre_source.exists(): @@ -134,7 +134,7 @@ def main(): """Main function.""" print("๐ŸŽฎ ArcadeDB Python Package Setup") print("=" * 40) - print("๐Ÿ“ฆ Package: arcadedb-embedded (with bundled JRE)") + print("๐Ÿ“ฆ Package: arcadedb (with bundled JRE)") print() # Copy JAR files diff --git a/scripts/write_version.py b/scripts/write_version.py index 32255d59..3a406290 100644 --- a/scripts/write_version.py +++ b/scripts/write_version.py @@ -15,7 +15,7 @@ def write_version_file(ver, out_path): """Write version to _version.py""" - version_file_content = f'''"""Version information for arcadedb_embedded package. + version_file_content = f'''"""Version information for arcadedb package. Generated automatically during build from the bundled ArcadeDB engine jar. """ @@ -41,8 +41,8 @@ def write_version_file(ver, out_path): try: version = extract_version(jars_dir) - # Write to src/arcadedb_embedded/_version.py - output_path = bindings_dir / "src/arcadedb_embedded/_version.py" + # Write to src/arcadedb/_version.py + output_path = bindings_dir / "src/arcadedb/_version.py" write_version_file(version, output_path) except (ValueError, FileNotFoundError, OSError) as e: diff --git a/setup.py b/setup.py index c15fb195..dfae8bbc 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ """ -Setup configuration for arcadedb-embedded. +Setup configuration for arcadedb. This file forces the wheel to be platform-specific (not py3-none-any) because we bundle platform-specific JRE binaries. diff --git a/src/arcadedb_embedded/__init__.py b/src/arcadedb/__init__.py similarity index 91% rename from src/arcadedb_embedded/__init__.py rename to src/arcadedb/__init__.py index 0cdf4955..d99e6be2 100644 --- a/src/arcadedb_embedded/__init__.py +++ b/src/arcadedb/__init__.py @@ -15,13 +15,18 @@ try: from importlib.metadata import version as _dist_version - __version__ = _dist_version("arcadedb-embedded") + __version__ = _dist_version("arcadedb") except Exception: # not installed: fall back to the build-time file try: from ._version import __version__ except ModuleNotFoundError: __version__ = "0.0.0" +# The JVM and the native wheel both import as `arcadedb`; pip overwrites one with the other silently. +from ._dist_conflict import warn_if_both_installed as _warn_if_both_installed + +_warn_if_both_installed(stacklevel=3) # blame the importing line + # Import async execution from .async_executor import AsyncExecutor diff --git a/src/arcadedb/_dist_conflict.py b/src/arcadedb/_dist_conflict.py new file mode 100644 index 00000000..c318f41d --- /dev/null +++ b/src/arcadedb/_dist_conflict.py @@ -0,0 +1,59 @@ +"""Warn when both distributions that provide the `arcadedb` import are installed. + +The JVM wheel (`arcadedb`) and the native wheel (`arcadedb-native`) both import as `arcadedb`. Their +files land in the same directory, so installing one after the other overwrites files of the first with +files of the second, and pip says nothing. The result is a mix of two builds. Package metadata still +lists both, so this module looks there. +""" + +from __future__ import annotations + +import warnings +from importlib import metadata +from typing import Callable + +# Distributions that put an `arcadedb` package into site-packages. +DISTRIBUTIONS = ("arcadedb", "arcadedb-native") + + +def installed_distributions( + names: tuple[str, ...] = DISTRIBUTIONS, + lookup: Callable[[str], str] = metadata.version, +) -> list[str]: + """The names from `names` that are installed, in the order given. + + `lookup` raises `importlib.metadata.PackageNotFoundError` for a name that is not installed. + """ + found = [] + for name in names: + try: + lookup(name) + except metadata.PackageNotFoundError: + continue + found.append(name) + return found + + +def warn_if_both_installed( + lookup: Callable[[str], str] = metadata.version, stacklevel: int = 2 +) -> bool: + """Emit a RuntimeWarning when both distributions are installed. True when it did. + + Never raises: an import must not fail because the metadata could not be read. + """ + try: + found = installed_distributions(lookup=lookup) + except Exception: # unreadable metadata, an odd sys.path entry: say nothing + return False + if len(found) < len(DISTRIBUTIONS): + return False + warnings.warn( + "Both the 'arcadedb' and the 'arcadedb-native' distributions are installed. " + "Both provide the Python package 'arcadedb', so each install overwrote files of the " + "other without a message, and what you import now is a mix of the two builds. " + "Install only one: pip uninstall arcadedb arcadedb-native, then pip install arcadedb " + "(or arcadedb-native).", + RuntimeWarning, + stacklevel=stacklevel, + ) + return True diff --git a/src/arcadedb_embedded/_logging.py b/src/arcadedb/_logging.py similarity index 84% rename from src/arcadedb_embedded/_logging.py rename to src/arcadedb/_logging.py index 2a2dc0c8..716afcfc 100644 --- a/src/arcadedb_embedded/_logging.py +++ b/src/arcadedb/_logging.py @@ -1,4 +1,4 @@ -"""Internal logging helpers for arcadedb_embedded. +"""Internal logging helpers for arcadedb. Centralises the pattern for swallowing exceptions in finalizers and best-effort cleanup paths so the suppression is observable at DEBUG level @@ -9,7 +9,7 @@ def get_logger(name: str) -> logging.Logger: - """Return a namespaced logger for an arcadedb_embedded submodule.""" + """Return a namespaced logger for an arcadedb submodule.""" return logging.getLogger(name) diff --git a/src/arcadedb_embedded/aot.py b/src/arcadedb/aot.py similarity index 100% rename from src/arcadedb_embedded/aot.py rename to src/arcadedb/aot.py diff --git a/src/arcadedb_embedded/async_executor.py b/src/arcadedb/async_executor.py similarity index 100% rename from src/arcadedb_embedded/async_executor.py rename to src/arcadedb/async_executor.py diff --git a/src/arcadedb_embedded/callbacks.py b/src/arcadedb/callbacks.py similarity index 100% rename from src/arcadedb_embedded/callbacks.py rename to src/arcadedb/callbacks.py diff --git a/src/arcadedb_embedded/core.py b/src/arcadedb/core.py similarity index 100% rename from src/arcadedb_embedded/core.py rename to src/arcadedb/core.py diff --git a/src/arcadedb_embedded/dataframe.py b/src/arcadedb/dataframe.py similarity index 99% rename from src/arcadedb_embedded/dataframe.py rename to src/arcadedb/dataframe.py index 270f397c..527f44f4 100644 --- a/src/arcadedb_embedded/dataframe.py +++ b/src/arcadedb/dataframe.py @@ -19,7 +19,7 @@ _PYARROW_HINT = ( "pyarrow is required for {what}. Install with: pip install pyarrow " - "(or pip install 'arcadedb-embedded[arrow]')" + "(or pip install 'arcadedb[arrow]')" ) # Declared property types (the engine's Type names) that an Arrow column kind diff --git a/src/arcadedb_embedded/exceptions.py b/src/arcadedb/exceptions.py similarity index 100% rename from src/arcadedb_embedded/exceptions.py rename to src/arcadedb/exceptions.py diff --git a/src/arcadedb_embedded/exporter.py b/src/arcadedb/exporter.py similarity index 100% rename from src/arcadedb_embedded/exporter.py rename to src/arcadedb/exporter.py diff --git a/src/arcadedb_embedded/graph.py b/src/arcadedb/graph.py similarity index 100% rename from src/arcadedb_embedded/graph.py rename to src/arcadedb/graph.py diff --git a/src/arcadedb_embedded/graph_batch.py b/src/arcadedb/graph_batch.py similarity index 100% rename from src/arcadedb_embedded/graph_batch.py rename to src/arcadedb/graph_batch.py diff --git a/src/arcadedb_embedded/importer.py b/src/arcadedb/importer.py similarity index 100% rename from src/arcadedb_embedded/importer.py rename to src/arcadedb/importer.py diff --git a/src/arcadedb_embedded/jvm.py b/src/arcadedb/jvm.py similarity index 98% rename from src/arcadedb_embedded/jvm.py rename to src/arcadedb/jvm.py index edea1489..c4fb5b07 100644 --- a/src/arcadedb_embedded/jvm.py +++ b/src/arcadedb/jvm.py @@ -91,7 +91,7 @@ def _extract_runtime_resource(resource_name: str) -> Path: project_dir = _project_dir() dist_dir = project_dir / "dist" wheels = sorted( - dist_dir.glob("arcadedb_embedded-*.whl"), + dist_dir.glob("arcadedb-*.whl"), key=lambda p: p.stat().st_mtime_ns, reverse=True, ) @@ -102,7 +102,7 @@ def _extract_runtime_resource(resource_name: str) -> Path: stamp = f"{wheel.name} {st.st_size} {st.st_mtime_ns}" cache_root = project_dir / ".runtime-cache" - extracted_root = cache_root / "arcadedb_embedded" + extracted_root = cache_root / "arcadedb" extracted_resource_dir = extracted_root / resource_name stamp_file = cache_root / f".extracted-from-{resource_name}" @@ -119,7 +119,7 @@ def _extract_runtime_resource(resource_name: str) -> Path: stamp_file.unlink(missing_ok=True) cache_root.mkdir(parents=True, exist_ok=True) - prefix = f"arcadedb_embedded/{resource_name}/" + prefix = f"arcadedb/{resource_name}/" with zipfile.ZipFile(wheel) as wheel_zip: members = [name for name in wheel_zip.namelist() if name.startswith(prefix)] @@ -165,7 +165,7 @@ def jar_fingerprint(per_jar: bool = False) -> dict: So: hash what is actually on disk. Two installs agree iff this agrees. - >>> import arcadedb_embedded as adb + >>> import arcadedb as adb >>> adb.jar_fingerprint()["sha256"][:12] 'a3f1c0d8b214' @@ -431,7 +431,7 @@ def start_jvm( generated logging.properties. Default: a per-user state directory, never the working directory (Linux ``$XDG_STATE_HOME`` or ``~/.local/state``, macOS ``~/Library/Logs``, Windows ``%LOCALAPPDATA%``, - then ``arcadedb-embedded/log``). A ``-Darcadedb.server.logsDirectory=`` or + then ``arcadedb/log``). A ``-Darcadedb.server.logsDirectory=`` or ``-Djava.util.logging.config.file=`` in ``jvm_args`` wins over it. ARCADEDB_JVM_ERROR_FILE (optional) @@ -727,13 +727,13 @@ def _state_base_dir() -> Path: def _default_log_dir() -> Path: """Where the engine log, the crash log and logging.properties go. - ``ARCADEDB_LOG_DIR`` if set, else ``arcadedb-embedded/log`` under the + ``ARCADEDB_LOG_DIR`` if set, else ``arcadedb/log`` under the per-user state directory. Never the working directory. """ override = os.environ.get(_LOG_DIR_ENV) if override: return Path(os.path.abspath(os.path.expanduser(override))) - return _state_base_dir() / "arcadedb-embedded" / "log" + return _state_base_dir() / "arcadedb" / "log" def _read_engine_log_properties(jar_files: Iterable[str]) -> Optional[str]: @@ -780,7 +780,7 @@ def _prepare_log_dir(log_dir: Path, jar_files: Iterable[str]) -> Path: if not os.access(log_dir, os.W_OK | os.X_OK): raise OSError(f"{log_dir} is not writable") except OSError as exc: - fallback = Path(tempfile.mkdtemp(prefix="arcadedb-embedded-log-")) + fallback = Path(tempfile.mkdtemp(prefix="arcadedb-log-")) _LOGGER.warning( "Cannot use %s for ArcadeDB logs (%s); using %s. Set %s to choose one.", log_dir, diff --git a/src/arcadedb_embedded/results.py b/src/arcadedb/results.py similarity index 99% rename from src/arcadedb_embedded/results.py rename to src/arcadedb/results.py index 019f1ed7..1dcfca8d 100644 --- a/src/arcadedb_embedded/results.py +++ b/src/arcadedb/results.py @@ -660,7 +660,7 @@ def to_polars( Reads :meth:`to_arrow` and hands the table to Polars, so nulls keep their type (a nullable integer stays an integer column) and a DECIMAL column is a Polars ``Decimal``. Requires polars and pyarrow - (``pip install 'arcadedb-embedded[polars]'``). + (``pip install 'arcadedb[polars]'``). Args: batch_size: Rows per bridge crossing, as in :meth:`to_arrow`. @@ -682,14 +682,14 @@ def to_polars( except ImportError as exc: raise ImportError( "polars is required for to_polars(). " - "Install with: pip install 'arcadedb-embedded[polars]'" + "Install with: pip install 'arcadedb[polars]'" ) from exc table = self.to_arrow(batch_size=batch_size, columns=columns) if table is None: raise ImportError( "to_polars() reads through Arrow, which needs pyarrow and numpy " "and the bundled bridge jar. " - "Install with: pip install 'arcadedb-embedded[polars]'" + "Install with: pip install 'arcadedb[polars]'" ) return pl.from_arrow(table) diff --git a/src/arcadedb_embedded/schema.py b/src/arcadedb/schema.py similarity index 100% rename from src/arcadedb_embedded/schema.py rename to src/arcadedb/schema.py diff --git a/src/arcadedb_embedded/server.py b/src/arcadedb/server.py similarity index 99% rename from src/arcadedb_embedded/server.py rename to src/arcadedb/server.py index de823b65..e92c8577 100644 --- a/src/arcadedb_embedded/server.py +++ b/src/arcadedb/server.py @@ -321,7 +321,7 @@ def create_server( To customize locations, set environment variables BEFORE importing: os.environ["ARCADEDB_JVM_ERROR_FILE"] = "/custom/path/errors.log" - import arcadedb_embedded as arcadedb + import arcadedb Note: Application log location is hardcoded in ArcadeDB's Java code and cannot be changed via environment variables. diff --git a/src/arcadedb_embedded/transactions.py b/src/arcadedb/transactions.py similarity index 100% rename from src/arcadedb_embedded/transactions.py rename to src/arcadedb/transactions.py diff --git a/src/arcadedb_embedded/type_conversion.py b/src/arcadedb/type_conversion.py similarity index 100% rename from src/arcadedb_embedded/type_conversion.py rename to src/arcadedb/type_conversion.py diff --git a/src/arcadedb_embedded/vector.py b/src/arcadedb/vector.py similarity index 100% rename from src/arcadedb_embedded/vector.py rename to src/arcadedb/vector.py diff --git a/tests/README.md b/tests/README.md index 3d2df583..5296e1fa 100644 --- a/tests/README.md +++ b/tests/README.md @@ -12,7 +12,7 @@ uv venv # After each build: the wheel and its test extra into .venv. --reinstall-package, # as a rebuild keeps the version (26.11.1.dev0) -uv pip install --reinstall-package arcadedb-embedded "$(ls dist/*.whl | head -1)[test]" +uv pip install --reinstall-package arcadedb "$(ls dist/*.whl | head -1)[test]" # Run all tests uv run pytest @@ -34,5 +34,5 @@ The server tests do not skip when the server stack is absent: the wheel always s ## Need Help? -- **Found a bug?** [Open an issue](https://github.com/humemai/arcadedb-embedded-python/issues) +- **Found a bug?** [Open an issue](https://github.com/ArcadeData/arcadedb-python/issues) - **Contributing?** Read the [Contributing Guide](https://docs.humem.ai/arcadedb/latest/development/contributing/) diff --git a/tests/conftest.py b/tests/conftest.py index 01b5a176..1f1e4d43 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -170,7 +170,7 @@ def temp_db_path(): @pytest.fixture def temp_db(): """Create a temporary database, yield it, and clean up.""" - import arcadedb_embedded as arcadedb + import arcadedb temp_dir = tempfile.mkdtemp(prefix="arcadedb_test_db_") db_path = os.path.join(temp_dir, "test_db") @@ -246,7 +246,7 @@ def pytest_unconfigure(config): """ import sys - from arcadedb_embedded.jvm import shutdown_jvm + from arcadedb.jvm import shutdown_jvm # Flush all output to ensure we see test results sys.stdout.flush() diff --git a/tests/test_aot.py b/tests/test_aot.py index 3c4bb39a..37c545cb 100644 --- a/tests/test_aot.py +++ b/tests/test_aot.py @@ -1,4 +1,4 @@ -"""Opt-in JDK AOT cache (arcadedb_embedded.aot): key, paths, fallbacks. No JVM is started.""" +"""Opt-in JDK AOT cache (arcadedb.aot): key, paths, fallbacks. No JVM is started.""" import hashlib import json @@ -8,7 +8,7 @@ import pytest -from arcadedb_embedded import aot, jvm +from arcadedb import aot, jvm def _fake_jre(tmp_path: Path, version="25.0.4.1", libsize=1234) -> Path: diff --git a/tests/test_async_executor.py b/tests/test_async_executor.py index 336c2092..41393f4e 100644 --- a/tests/test_async_executor.py +++ b/tests/test_async_executor.py @@ -8,7 +8,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb # Submissions used by the record-loss tests. 9,742 is the count from the # original report: 9,742 Movie vertices submitted, 2,436 stored. diff --git a/tests/test_bridge_fixes.py b/tests/test_bridge_fixes.py index b9989264..e0f49410 100644 --- a/tests/test_bridge_fixes.py +++ b/tests/test_bridge_fixes.py @@ -10,7 +10,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def test_insert_many_nested_values_read_as_python_in_the_same_transaction( @@ -47,8 +47,8 @@ def test_insert_many_nested_values_read_as_python_in_the_same_transaction( import sys from datetime import date -import arcadedb_embedded as arcadedb -from arcadedb_embedded.jvm import start_jvm +import arcadedb +from arcadedb.jvm import start_jvm start_jvm(heap_size="1g", jvm_args=["-Duser.timezone=" + sys.argv[2]]) db = arcadedb.create_database(sys.argv[1]) diff --git a/tests/test_build_no_uv_refresh.py b/tests/test_build_no_uv_refresh.py index 0f0894b5..2e24edbc 100644 --- a/tests/test_build_no_uv_refresh.py +++ b/tests/test_build_no_uv_refresh.py @@ -35,8 +35,8 @@ def test_the_scan_sees_the_old_refresh(): old = ( " (\n" ' cd "$REPO_ROOT" &&\n' - " uv lock --upgrade-package arcadedb-embedded &&\n" - " uv sync --reinstall-package arcadedb-embedded\n" + " uv lock --upgrade-package arcadedb &&\n" + " uv sync --reinstall-package arcadedb\n" " )\n" " # uv lock in a comment is not a command\n" ) diff --git a/tests/test_build_wheel_check.py b/tests/test_build_wheel_check.py index 3ab0b27e..7516a85d 100644 --- a/tests/test_build_wheel_check.py +++ b/tests/test_build_wheel_check.py @@ -5,7 +5,7 @@ version and compares the integration jar inside it with the local one. It turned the engine version into the wheel version with its own string rules, which mapped ``26.11.1-RC1`` to ``26.11.1.RC1``: no RC wheel (``26.11.1rc1``) -ever matched, and an RC build failed with "no arcadedb_embedded-... wheel in +ever matched, and an RC build failed with "no arcadedb-... wheel in dist/" (#7). This runs that heredoc, unchanged, for each kind of version. """ @@ -42,9 +42,9 @@ def test_wheel_check_finds_this_builds_wheel(tmp_path, engine, wheel_version): (tmp_path / "local-jars" / "lib").mkdir(parents=True) (tmp_path / "local-jars" / "lib" / jar_name).write_bytes(b"integration") (tmp_path / "dist").mkdir() - wheel = tmp_path / "dist" / f"arcadedb_embedded-{wheel_version}-py3-none-any.whl" + wheel = tmp_path / "dist" / f"arcadedb-{wheel_version}-py3-none-any.whl" with zipfile.ZipFile(wheel, "w") as zf: - zf.writestr(f"arcadedb_embedded/jars/{jar_name}", b"integration") + zf.writestr(f"arcadedb/jars/{jar_name}", b"integration") env = dict(os.environ, ARCADEDB_VERSION=engine, BUILD_VERSION="", NEW_WHEELS="") out = subprocess.run( # nosec B603 - fixed argv, no shell diff --git a/tests/test_bulk_insert.py b/tests/test_bulk_insert.py index c8ac18e2..36c13bb5 100644 --- a/tests/test_bulk_insert.py +++ b/tests/test_bulk_insert.py @@ -157,7 +157,7 @@ class TestInsertManyParallelReportsFailures: """ def test_duplicate_key_raises_instead_of_dropping(self, temp_db): - import arcadedb_embedded as arcadedb + import arcadedb temp_db.command("sql", "CREATE DOCUMENT TYPE ParDup BUCKETS 4") temp_db.command("sql", "CREATE PROPERTY ParDup.id INTEGER") @@ -287,7 +287,7 @@ def _point_type(self, db, name): db.command("sql", f"CREATE INDEX ON {name} (k) UNIQUE") def test_rows_are_written_while_the_iterable_is_read(self, temp_db): - from arcadedb_embedded import core + from arcadedb import core self._point_type(temp_db, "Stream") chunk = core._INSERT_MANY_CHUNK @@ -306,7 +306,7 @@ def rows(): assert _count(temp_db, "Stream") == 3 * chunk def test_python_memory_does_not_grow_with_the_row_count(self, temp_db): - from arcadedb_embedded import core + from arcadedb import core # Rows produced but not yet handed to the engine are what Python holds. # Counting them at every chunk boundary needs no allocator hook: this @@ -331,7 +331,7 @@ def rows(): assert _count(temp_db, "StreamMem") == n def test_commit_every_counts_across_chunks(self, temp_db): - from arcadedb_embedded import ArcadeDBError, core + from arcadedb import ArcadeDBError, core self._point_type(temp_db, "StreamBatch") chunk = core._INSERT_MANY_CHUNK @@ -345,7 +345,7 @@ def test_commit_every_counts_across_chunks(self, temp_db): assert _count(temp_db, "StreamBatch") == 2 * every def test_commit_every_zero_is_one_transaction(self, temp_db): - from arcadedb_embedded import ArcadeDBError, core + from arcadedb import ArcadeDBError, core self._point_type(temp_db, "StreamOne") rows = [{"k": i} for i in range(2 * core._INSERT_MANY_CHUNK + 5)] @@ -356,7 +356,7 @@ def test_commit_every_zero_is_one_transaction(self, temp_db): assert _count(temp_db, "StreamOne") == 0 def test_an_error_from_the_iterable_keeps_committed_batches(self, temp_db): - from arcadedb_embedded import core + from arcadedb import core self._point_type(temp_db, "StreamGenErr") chunk = core._INSERT_MANY_CHUNK @@ -372,7 +372,7 @@ def rows(): assert _count(temp_db, "StreamGenErr") == chunk def test_a_chunk_off_the_json_path_does_not_change_the_others(self, temp_db): - from arcadedb_embedded import core + from arcadedb import core chunk = core._INSERT_MANY_CHUNK temp_db.command("sql", "CREATE DOCUMENT TYPE StreamMixed") @@ -383,7 +383,7 @@ def test_a_chunk_off_the_json_path_does_not_change_the_others(self, temp_db): @pytest.mark.parametrize("with_fallback_chunk", [False, True]) def test_parallel_streams_every_row(self, temp_db, with_fallback_chunk): - from arcadedb_embedded import core + from arcadedb import core chunk = core._INSERT_MANY_CHUNK temp_db.command("sql", "CREATE DOCUMENT TYPE StreamPar BUCKETS 4") @@ -402,7 +402,7 @@ def rows(): assert _count(temp_db, "StreamPar") == n def test_parallel_duplicate_in_a_later_chunk_raises(self, temp_db): - from arcadedb_embedded import ArcadeDBError, core + from arcadedb import ArcadeDBError, core chunk = core._INSERT_MANY_CHUNK temp_db.command("sql", "CREATE DOCUMENT TYPE StreamParDup BUCKETS 4") @@ -445,7 +445,7 @@ class TestVectorColumns: def test_float_array_column_to_columns(self, temp_db): import numpy as np - import arcadedb_embedded as arcadedb + import arcadedb temp_db.command("sql", "CREATE DOCUMENT TYPE Emb") temp_db.command("sql", "CREATE PROPERTY Emb.vid INTEGER") @@ -601,7 +601,7 @@ def test_primitive_batch_accepts_plain_sequences(self, temp_db): class TestVectorColumnsDataFrame: def test_vector_column_to_dataframe(self, temp_db): - import arcadedb_embedded as arcadedb + import arcadedb pd = __import__("pytest").importorskip("pandas") temp_db.command("sql", "CREATE DOCUMENT TYPE EmbDf") diff --git a/tests/test_bulk_json_paths.py b/tests/test_bulk_json_paths.py index 81244063..555a605a 100644 --- a/tests/test_bulk_json_paths.py +++ b/tests/test_bulk_json_paths.py @@ -11,8 +11,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import type_conversion +import arcadedb +from arcadedb import type_conversion ENTRY_POINTS = ["insert_many", "create_vertices", "new_edges"] diff --git a/tests/test_columnar_readers.py b/tests/test_columnar_readers.py index 1d2df6ed..ef6877e8 100644 --- a/tests/test_columnar_readers.py +++ b/tests/test_columnar_readers.py @@ -14,7 +14,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb pa = pytest.importorskip("pyarrow") pd = pytest.importorskip("pandas") @@ -252,7 +252,7 @@ def test_export_to_csv_header_is_the_union_of_the_rows(events_db, tmp_path): contains fields not in fieldnames" after part of the file was written (#113).""" import csv - from arcadedb_embedded.exporter import export_to_csv + from arcadedb.exporter import export_to_csv path = str(tmp_path / "events.csv") export_to_csv(events_db.query("sql", "SELECT FROM Event ORDER BY n"), path) @@ -273,7 +273,7 @@ def test_export_to_csv_names_a_column_that_first_appears_after_the_header( """Across batches the header cannot grow: the export says which column arrived too late and what to do, not a bare writer error. The batch size of the export is 10,000 rows, so the last of 10,001 rows is in the second.""" - from arcadedb_embedded.exporter import export_to_csv + from arcadedb.exporter import export_to_csv with arcadedb.create_database(temp_db_path) as db: db.command("sql", "CREATE DOCUMENT TYPE Row") diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py index 1cb47267..7e2afd64 100644 --- a/tests/test_concurrency.py +++ b/tests/test_concurrency.py @@ -19,8 +19,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb.exceptions import ArcadeDBError @pytest.fixture @@ -51,7 +51,7 @@ def _open_in_child_process(db_path): another process holds the database's file lock. """ code = textwrap.dedent(f""" - import arcadedb_embedded as arcadedb + import arcadedb try: arcadedb.open_database({db_path!r}).close() print("RESULT OPENED") diff --git a/tests/test_conversion_fixes.py b/tests/test_conversion_fixes.py index 88b856d3..19aca52d 100644 --- a/tests/test_conversion_fixes.py +++ b/tests/test_conversion_fixes.py @@ -7,10 +7,10 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import type_conversion -from arcadedb_embedded.schema import PropertyType -from arcadedb_embedded.vector import to_java_int_array +import arcadedb +from arcadedb import type_conversion +from arcadedb.schema import PropertyType +from arcadedb.vector import to_java_int_array np = pytest.importorskip("numpy") diff --git a/tests/test_core.py b/tests/test_core.py index c319fb8c..0076bd4a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7,7 +7,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def test_database_creation(temp_db_path): @@ -1043,7 +1043,7 @@ def test_failed_open_does_not_hang_process_exit(tmp_path): import sys code = ( - "import arcadedb_embedded as a\n" + "import arcadedb as a\n" "try:\n" f" a.open_database({str(tmp_path / 'missing_db')!r})\n" "except Exception:\n" diff --git a/tests/test_count_pushdown_known_issues.py b/tests/test_count_pushdown_known_issues.py index c5c975a9..0223b30b 100644 --- a/tests/test_count_pushdown_known_issues.py +++ b/tests/test_count_pushdown_known_issues.py @@ -26,7 +26,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb # --------------------------------------------------------------------------------------------- # #9277: another label at the far end of the chain (fixed in 26.10.2, #9288) diff --git a/tests/test_cross_model_atomicity.py b/tests/test_cross_model_atomicity.py index 62cc1b1c..4e615819 100644 --- a/tests/test_cross_model_atomicity.py +++ b/tests/test_cross_model_atomicity.py @@ -4,7 +4,7 @@ import random -import arcadedb_embedded as arcadedb +import arcadedb DIM = 8 diff --git a/tests/test_cypher.py b/tests/test_cypher.py index f7ffc935..4f200f62 100644 --- a/tests/test_cypher.py +++ b/tests/test_cypher.py @@ -2,7 +2,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def _ensure_opencypher(db) -> None: diff --git a/tests/test_database_utils.py b/tests/test_database_utils.py index 6526a056..ed006668 100644 --- a/tests/test_database_utils.py +++ b/tests/test_database_utils.py @@ -2,7 +2,7 @@ Tests for new Database utility methods. """ -import arcadedb_embedded as arcadedb +import arcadedb def test_count_type(temp_db_path): diff --git a/tests/test_dataframe_ingest.py b/tests/test_dataframe_ingest.py index a0b4bd3c..3404cd09 100644 --- a/tests/test_dataframe_ingest.py +++ b/tests/test_dataframe_ingest.py @@ -17,7 +17,7 @@ pa = pytest.importorskip("pyarrow") pl = pytest.importorskip("polars") -from arcadedb_embedded.exceptions import ArcadeDBError # noqa: E402 +from arcadedb.exceptions import ArcadeDBError # noqa: E402 def _rows(db, sql): @@ -717,7 +717,7 @@ def test_missing_polars_is_an_import_error_with_the_fix(self, temp_db, monkeypat monkeypatch.setitem(sys.modules, "polars", None) with pytest.raises( ImportError, - match=r"polars is required for to_polars\(\).*arcadedb-embedded\[polars\]", + match=r"polars is required for to_polars\(\).*arcadedb\[polars\]", ): temp_db.query("sql", "SELECT FROM NoPolars").to_polars() diff --git a/tests/test_declared_type_known_issues.py b/tests/test_declared_type_known_issues.py index bd0f4147..8cfde47b 100644 --- a/tests/test_declared_type_known_issues.py +++ b/tests/test_declared_type_known_issues.py @@ -18,7 +18,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def _five_records_one_without_v(db, name): diff --git a/tests/test_dist_conflict.py b/tests/test_dist_conflict.py new file mode 100644 index 00000000..78735e79 --- /dev/null +++ b/tests/test_dist_conflict.py @@ -0,0 +1,101 @@ +"""The `arcadedb` and `arcadedb-native` distributions both import as `arcadedb`. + +pip overwrites one with the other without a message, so the package warns at import time when it +finds both distributions in the installed metadata. These tests need no engine and no JVM. +""" + +import os +import subprocess # nosec B404 - runs sys.executable on a one-line import +import sys +import warnings +from importlib import metadata + +import pytest + +from arcadedb import _dist_conflict + + +def lookup_with(*installed): + """A stand-in for importlib.metadata.version that knows only the names given.""" + + def lookup(name): + if name in installed: + return "26.11.1" + raise metadata.PackageNotFoundError(name) + + return lookup + + +def test_distribution_names(): + assert _dist_conflict.DISTRIBUTIONS == ("arcadedb", "arcadedb-native") + + +def test_both_installed_warns(): + with pytest.warns(RuntimeWarning, match="arcadedb-native") as record: + assert _dist_conflict.warn_if_both_installed( + lookup_with("arcadedb", "arcadedb-native") + ) + text = str(record[0].message) + assert "'arcadedb'" in text + assert "Install only one" in text + + +@pytest.mark.parametrize( + "installed", + [("arcadedb",), ("arcadedb-native",), ()], + ids=["jvm", "native", "none"], +) +def test_one_or_none_installed_is_silent(installed): + with warnings.catch_warnings(): + warnings.simplefilter("error") + assert not _dist_conflict.warn_if_both_installed(lookup_with(*installed)) + + +def test_unreadable_metadata_never_breaks_the_import(): + def broken(name): + raise OSError("unreadable site-packages") + + with warnings.catch_warnings(): + warnings.simplefilter("error") + assert not _dist_conflict.warn_if_both_installed(broken) + + +def fake_dist_info(site, name, version): + """A minimal installed distribution: -.dist-info/METADATA.""" + info = site / f"{name.replace('-', '_')}-{version}.dist-info" + info.mkdir(parents=True) + (info / "METADATA").write_text( + f"Metadata-Version: 2.1\nName: {name}\nVersion: {version}\n" + ) + + +def import_arcadedb(extra_site): + """Import arcadedb in a fresh interpreter with `extra_site` ahead on sys.path; return stderr.""" + env = dict(os.environ) + # sys.path of this run (installed wheel or source checkout) follows the fake site + env["PYTHONPATH"] = os.pathsep.join([str(extra_site)] + [p for p in sys.path if p]) + result = subprocess.run( # nosec B603 - fixed argv, no shell + [sys.executable, "-W", "default", "-c", "import arcadedb"], + capture_output=True, + text=True, + env=env, + check=False, + ) + assert result.returncode == 0, result.stderr + return result.stderr + + +def test_import_warns_when_both_distributions_are_installed(tmp_path): + site = tmp_path / "site" + fake_dist_info(site, "arcadedb", "26.11.1") + fake_dist_info(site, "arcadedb-native", "26.11.1") + stderr = import_arcadedb(site) + assert "RuntimeWarning" in stderr + assert "arcadedb-native" in stderr + assert "Install only one" in stderr + + +def test_import_is_silent_with_the_jvm_distribution_alone(tmp_path): + site = tmp_path / "site" + fake_dist_info(site, "arcadedb", "26.11.1") + assert "Install only one" not in import_arcadedb(site) diff --git a/tests/test_dml_plan_cache_known_issues.py b/tests/test_dml_plan_cache_known_issues.py index 323a68e1..50a7105c 100644 --- a/tests/test_dml_plan_cache_known_issues.py +++ b/tests/test_dml_plan_cache_known_issues.py @@ -17,7 +17,7 @@ ``:name`` parameters bound by position. """ -import arcadedb_embedded as arcadedb +import arcadedb def _products(db, name): diff --git a/tests/test_docs_examples.py b/tests/test_docs_examples.py index 166c6759..ab23f4e7 100644 --- a/tests/test_docs_examples.py +++ b/tests/test_docs_examples.py @@ -50,10 +50,10 @@ def _run_snippet_subprocess( ] if ( "arcadedb." in combined_source - and "import arcadedb_embedded as arcadedb" not in combined_source - and "from arcadedb_embedded import" not in combined_source + and "import arcadedb" not in combined_source + and "from arcadedb import" not in combined_source ): - parts.extend(["", "import arcadedb_embedded as arcadedb"]) + parts.extend(["", "import arcadedb"]) if preamble.strip(): parts.extend(["", textwrap.dedent(preamble).strip()]) if snippet.strip(): @@ -114,7 +114,7 @@ def _run_doc_block_with_open_db( setup_code = " pass" wrapped = "\n".join( [ - "import arcadedb_embedded as arcadedb", + "import arcadedb", "", f'with arcadedb.create_database("./{db_name}") as db:', setup_code, @@ -268,7 +268,7 @@ def test_docs_installation_examples(temp_dir_factory): ) _run_doc_block( "getting-started/installation.md", - "from arcadedb_embedded.jvm import start_jvm", + "from arcadedb.jvm import start_jvm", base_dir / "installation_jvm", ) _run_doc_block( @@ -314,7 +314,7 @@ def test_docs_index_and_quickstart_examples(temp_dir_factory): ) existing_db_preamble = textwrap.dedent(""" - import arcadedb_embedded as arcadedb + import arcadedb with arcadedb.create_database("./quickstart") as db: db.command("sql", "CREATE DOCUMENT TYPE Person") with db.transaction(): @@ -328,7 +328,7 @@ def test_docs_index_and_quickstart_examples(temp_dir_factory): ) _run_snippet_subprocess( textwrap.dedent(""" - import arcadedb_embedded as arcadedb + import arcadedb with arcadedb.create_database("./mydb") as db: db.command("sql", "CREATE DOCUMENT TYPE Person") @@ -443,7 +443,7 @@ def test_docs_example_pages(temp_dir_factory): setup=simple_setup, ) social_network_script = textwrap.dedent(''' - import arcadedb_embedded as arcadedb + import arcadedb with arcadedb.create_database("./social_network_db") as db: db.command("sql", "CREATE VERTEX TYPE Person") diff --git a/tests/test_example29_hybrid_memory.py b/tests/test_example29_hybrid_memory.py index 66bbc4a3..50e5f14d 100644 --- a/tests/test_example29_hybrid_memory.py +++ b/tests/test_example29_hybrid_memory.py @@ -10,7 +10,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb EXAMPLE_PATH = ( Path(__file__).resolve().parents[1] / "examples" / "29_hybrid_memory_retrieval.py" diff --git a/tests/test_exporter.py b/tests/test_exporter.py index 9156f728..d43663d7 100644 --- a/tests/test_exporter.py +++ b/tests/test_exporter.py @@ -10,7 +10,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb @pytest.fixture @@ -407,7 +407,7 @@ def test_export_to_csv_empty_results(self, sample_db, temp_db_path): def test_export_to_csv_with_resultset(self, sample_db, temp_db_path): """Test CSV export using ResultSet directly.""" - from arcadedb_embedded.exporter import export_to_csv + from arcadedb.exporter import export_to_csv csv_path = os.path.join(temp_db_path, "ratings.csv") @@ -425,7 +425,7 @@ def test_export_to_csv_with_resultset(self, sample_db, temp_db_path): def test_export_to_csv_with_list_of_dicts(self, temp_db_path): """Test CSV export from list of dictionaries.""" - from arcadedb_embedded.exporter import export_to_csv + from arcadedb.exporter import export_to_csv csv_path = os.path.join(temp_db_path, "custom_data.csv") diff --git a/tests/test_float_vector_bulk.py b/tests/test_float_vector_bulk.py index c5f6a69d..2ff71c16 100644 --- a/tests/test_float_vector_bulk.py +++ b/tests/test_float_vector_bulk.py @@ -13,8 +13,8 @@ import numpy as np import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import type_conversion +import arcadedb +from arcadedb import type_conversion DIM = 8 N = 50 diff --git a/tests/test_free_threading.py b/tests/test_free_threading.py index 33fe68b1..cb315143 100644 --- a/tests/test_free_threading.py +++ b/tests/test_free_threading.py @@ -20,7 +20,7 @@ import numpy as np import pytest -import arcadedb_embedded as arcadedb +import arcadedb THREADS = int(os.environ.get("FT_THREADS", "16")) REPEAT = int(os.environ.get("FT_REPEAT", "1")) @@ -287,7 +287,7 @@ def target(tid): FIRST_START = textwrap.dedent(""" import os, sys, threading - import arcadedb_embedded as arcadedb + import arcadedb n = int(sys.argv[1]); tmp = sys.argv[2] barrier = threading.Barrier(n); errors = [] def run(tid): diff --git a/tests/test_geo_predicate_sql.py b/tests/test_geo_predicate_sql.py index 91921073..feb41918 100644 --- a/tests/test_geo_predicate_sql.py +++ b/tests/test_geo_predicate_sql.py @@ -1,6 +1,6 @@ """Geo predicate SQL coverage for Python bindings.""" -import arcadedb_embedded as arcadedb +import arcadedb def test_geo_predicate_sql_within_and_intersects(temp_db_path): diff --git a/tests/test_graph.py b/tests/test_graph.py index 010f9016..e1ac6ab5 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -1,6 +1,6 @@ def test_graph_batch_new_edges_bulk(tmp_path): """new_edges buffers many property-less edges in one crossing.""" - import arcadedb_embedded as arcadedb + import arcadedb with arcadedb.create_database(str(tmp_path / "bulk_edges_db")) as db: db.command("sql", "CREATE VERTEX TYPE BP") @@ -20,7 +20,7 @@ def test_graph_batch_create_vertices_json_bulk_correctness(tmp_path): (and datetimes fall back to the matrix path with types preserved).""" import datetime - import arcadedb_embedded as arcadedb + import arcadedb with arcadedb.create_database(str(tmp_path / "bulk_v_db")) as db: db.command("sql", "CREATE VERTEX TYPE BV") @@ -51,7 +51,7 @@ def test_graph_batch_create_vertices_json_bulk_correctness(tmp_path): def test_graph_batch_new_edges_bulk_with_properties(tmp_path): """new_edges with per-edge property dicts stores values correctly.""" - import arcadedb_embedded as arcadedb + import arcadedb with arcadedb.create_database(str(tmp_path / "bulk_ep_db")) as db: db.command("sql", "CREATE VERTEX TYPE PV") diff --git a/tests/test_graph_algorithms_sql.py b/tests/test_graph_algorithms_sql.py index bf5fbab5..17f2bce9 100644 --- a/tests/test_graph_algorithms_sql.py +++ b/tests/test_graph_algorithms_sql.py @@ -1,6 +1,6 @@ """Graph algorithm SQL coverage for Python bindings.""" -import arcadedb_embedded as arcadedb +import arcadedb def _setup_weighted_graph(db): diff --git a/tests/test_graph_api.py b/tests/test_graph_api.py index 07a9f0a0..3b54991f 100644 --- a/tests/test_graph_api.py +++ b/tests/test_graph_api.py @@ -4,8 +4,8 @@ Tests Document, Vertex, and Edge wrapper classes and their methods. """ -import arcadedb_embedded as arcadedb -from arcadedb_embedded.graph import Document, Edge, Vertex +import arcadedb +from arcadedb.graph import Document, Edge, Vertex def test_vertex_wrapper_creation(temp_db_path): diff --git a/tests/test_graph_batch.py b/tests/test_graph_batch.py index b990d119..c197a9c9 100644 --- a/tests/test_graph_batch.py +++ b/tests/test_graph_batch.py @@ -3,8 +3,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb.exceptions import ArcadeDBError def test_graph_batch_creates_vertices_and_edges(temp_db_path): diff --git a/tests/test_hash_index_schema.py b/tests/test_hash_index_schema.py index a6756029..a3428e40 100644 --- a/tests/test_hash_index_schema.py +++ b/tests/test_hash_index_schema.py @@ -2,7 +2,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def test_hash_index_schema_create_discover_and_query(temp_db_path): diff --git a/tests/test_import_database.py b/tests/test_import_database.py index 1971dd3b..3c15cada 100644 --- a/tests/test_import_database.py +++ b/tests/test_import_database.py @@ -6,7 +6,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb @pytest.fixture diff --git a/tests/test_importer_api.py b/tests/test_importer_api.py index be05a9e4..680bf9c4 100644 --- a/tests/test_importer_api.py +++ b/tests/test_importer_api.py @@ -3,7 +3,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def _write_people_csv(csv_path: Path) -> None: diff --git a/tests/test_insert_columns.py b/tests/test_insert_columns.py index c7d9f008..34ca2ada 100644 --- a/tests/test_insert_columns.py +++ b/tests/test_insert_columns.py @@ -15,7 +15,7 @@ np = pytest.importorskip("numpy") -from arcadedb_embedded.exceptions import ArcadeDBError # noqa: E402 +from arcadedb.exceptions import ArcadeDBError # noqa: E402 def _count(db, type_name, where=""): diff --git a/tests/test_interrupt_hygiene.py b/tests/test_interrupt_hygiene.py index 3ca3f173..dcd9922a 100644 --- a/tests/test_interrupt_hygiene.py +++ b/tests/test_interrupt_hygiene.py @@ -17,10 +17,10 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import ArcadeDBError -from arcadedb_embedded.core import Database -from arcadedb_embedded.server import ArcadeDBServer +import arcadedb +from arcadedb import ArcadeDBError +from arcadedb.core import Database +from arcadedb.server import ArcadeDBServer @pytest.fixture diff --git a/tests/test_jar_closure.py b/tests/test_jar_closure.py index 20d627d3..144d697a 100644 --- a/tests/test_jar_closure.py +++ b/tests/test_jar_closure.py @@ -38,7 +38,8 @@ class refers to a library the wheel does not ship: decide whether the code path import zipfile import pytest -from arcadedb_embedded.jvm import get_jar_path + +from arcadedb.jvm import get_jar_path # Packages the JDK provides. The first test checks the jars only. The bundled JRE # is a jlink image, and the modules it lacks on purpose are checked by the tests @@ -358,7 +359,8 @@ def test_class_reference_parser_reads_a_real_class(): def _runtime_packages(): """The packages of every module in the running JVM's boot layer, as ``a/b/c``.""" import jpype - from arcadedb_embedded.jvm import start_jvm + + from arcadedb.jvm import start_jvm start_jvm() modules = jpype.JClass("java.lang.ModuleLayer").boot().modules() @@ -415,7 +417,8 @@ def _removed_module_use_is_allowed(jar, package): def test_bundled_runtime_lacks_the_excluded_modules(): """The runtime really is the trimmed one, so the closure test below means something.""" import jpype - from arcadedb_embedded.jvm import start_jvm + + from arcadedb.jvm import start_jvm start_jvm() layer = jpype.JClass("java.lang.ModuleLayer").boot() @@ -479,7 +482,8 @@ def test_jackson_and_yaml_work_without_java_beans(tmp_path): YAML mapper does not touch snakeyaml's bean introspector. Run both. """ import jpype - from arcadedb_embedded.jvm import start_jvm + + from arcadedb.jvm import start_jvm start_jvm() mapper = jpype.JClass("com.fasterxml.jackson.databind.ObjectMapper")() diff --git a/tests/test_jar_provenance.py b/tests/test_jar_provenance.py index a49f7ea1..5635aec8 100644 --- a/tests/test_jar_provenance.py +++ b/tests/test_jar_provenance.py @@ -18,7 +18,7 @@ import hashlib import os -import arcadedb_embedded as adb +import arcadedb as adb def test_fingerprint_is_deterministic(): @@ -133,7 +133,7 @@ def test_our_jar_list_still_matches_the_wheel(): If the bridge JAR is ever renamed, every engine comparison silently starts including a non-reproducible JAR again and stops meaning what it says. """ - from arcadedb_embedded.jvm import _OUR_JARS + from arcadedb.jvm import _OUR_JARS names = {j["name"] for j in adb.jar_fingerprint(per_jar=True)["jars"]} missing = _OUR_JARS - names diff --git a/tests/test_java_package_shadowing.py b/tests/test_java_package_shadowing.py index b16b7e79..c13c89f2 100644 --- a/tests/test_java_package_shadowing.py +++ b/tests/test_java_package_shadowing.py @@ -31,7 +31,7 @@ _SCRIPT = textwrap.dedent(""" import os, shutil, tempfile, json, datetime, decimal - import arcadedb_embedded as A + import arcadedb as A root = tempfile.mkdtemp(prefix="shadow") db = A.create_database(os.path.join(root, "db")) try: @@ -74,9 +74,9 @@ def test_a_java_or_com_folder_on_the_path_does_not_break_conversion(tmp_path, fo # child quietly imported the installed, unfixed wheel, and the test measured # the wrong code in both directions. Pin it to the parent's package by # absolute path, and assert below that it got there. - import arcadedb_embedded + import arcadedb - pkg_dir = os.path.dirname(arcadedb_embedded.__file__) + pkg_dir = os.path.dirname(arcadedb.__file__) env = os.environ.copy() env["PYTHONPATH"] = os.pathsep.join( [os.path.dirname(pkg_dir)] @@ -100,7 +100,7 @@ def test_a_java_or_com_folder_on_the_path_does_not_break_conversion(tmp_path, fo assert os.path.realpath(out["loaded_from"]) == os.path.realpath( pkg_dir - ), "the child imported a different arcadedb_embedded than the one under test" + ), "the child imported a different arcadedb than the one under test" assert out["name"] == "Ada", f"strings came back as {out['name']!r}" assert out["name_type"] == "str" diff --git a/tests/test_jvm.py b/tests/test_jvm.py index eb39ce42..3a48a9ef 100644 --- a/tests/test_jvm.py +++ b/tests/test_jvm.py @@ -5,9 +5,9 @@ import jpype import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import jvm -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb import jvm +from arcadedb.exceptions import ArcadeDBError @pytest.fixture(autouse=True) @@ -72,7 +72,7 @@ def test_interpreter_exits_with_unclosed_database(tmp_path): import sys as _sys script = ( - "import arcadedb_embedded as a\n" + "import arcadedb as a\n" f"db = a.create_database({str(tmp_path / 'leak_db')!r})\n" "db.command('sql', 'CREATE DOCUMENT TYPE Doc')\n" "print('OK', flush=True)\n" @@ -141,7 +141,7 @@ def test_default_heap_resolves_in_a_fresh_jvm(tmp_path): import sys as _sys script = ( - "import arcadedb_embedded as a\n" + "import arcadedb as a\n" f"db = a.create_database({str(tmp_path / 'heap_db')!r})\n" "import jpype\n" "Runtime = jpype.JClass('java.lang.Runtime')\n" @@ -195,7 +195,7 @@ def test_xms_larger_than_max_heap_raises_before_start(tmp_path, kwargs): import sys as _sys script = ( - "import arcadedb_embedded as a\n" + "import arcadedb as a\n" "try:\n" f" a.create_database({str(tmp_path / 'xms_db')!r}, jvm_kwargs={kwargs!r})\n" "except ValueError as e:\n" diff --git a/tests/test_jvm_args.py b/tests/test_jvm_args.py index be577c8f..de085dda 100644 --- a/tests/test_jvm_args.py +++ b/tests/test_jvm_args.py @@ -1,7 +1,7 @@ import os from unittest.mock import patch -from arcadedb_embedded.jvm import _build_jvm_args +from arcadedb.jvm import _build_jvm_args def test_defaults_no_env_vars(): diff --git a/tests/test_legacy_temporal_implementations.py b/tests/test_legacy_temporal_implementations.py index 8626359e..8304b8ba 100644 --- a/tests/test_legacy_temporal_implementations.py +++ b/tests/test_legacy_temporal_implementations.py @@ -29,7 +29,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb WRITTEN = datetime(2024, 3, 15, 14, 30, 45, 123000) WRITTEN_DAY = date(2024, 3, 15) diff --git a/tests/test_light_edge_and_view_known_issues.py b/tests/test_light_edge_and_view_known_issues.py index d8d3fa97..2ae60e08 100644 --- a/tests/test_light_edge_and_view_known_issues.py +++ b/tests/test_light_edge_and_view_known_issues.py @@ -23,8 +23,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb.exceptions import ArcadeDBError # --------------------------------------------------------------------------------------------- # #9378: a one-hop count over light edges in an edge type that is not declared LIGHTWEIGHT diff --git a/tests/test_log_dir.py b/tests/test_log_dir.py index 39999187..eba86e5d 100644 --- a/tests/test_log_dir.py +++ b/tests/test_log_dir.py @@ -9,13 +9,13 @@ import pytest -from arcadedb_embedded import jvm -from arcadedb_embedded.jvm import _build_jvm_args, _default_log_dir +from arcadedb import jvm +from arcadedb.jvm import _build_jvm_args, _default_log_dir CHILD = """ import sys -import arcadedb_embedded as arcadedb -from arcadedb_embedded import jvm +import arcadedb +from arcadedb import jvm jvm_args = sys.argv[2:] db = arcadedb.create_database(sys.argv[1], jvm_kwargs={"jvm_args": jvm_args}) @@ -68,7 +68,7 @@ def test_default_logs_land_in_the_state_dir_not_the_cwd(tmp_path): cwd, log_dir, home = _run_child(tmp_path, {}) assert list(cwd.iterdir()) == [] assert home in log_dir.parents - assert log_dir.name == "log" and log_dir.parent.name == "arcadedb-embedded" + assert log_dir.name == "log" and log_dir.parent.name == "arcadedb" assert list(log_dir.glob("arcadedb.log*")), list(log_dir.iterdir()) config = (log_dir / "logging.properties").read_text("iso-8859-1") assert "${arcadedb.server.logsDirectory}/arcadedb.log" in config @@ -117,18 +117,18 @@ def test_state_dir_by_platform(tmp_path): env = {"HOME": str(tmp_path), "USERPROFILE": str(tmp_path)} with patch.dict(os.environ, {**env, "XDG_STATE_HOME": str(tmp_path / "xdg")}): with patch.object(jvm.sys, "platform", "linux"): - assert _default_log_dir() == tmp_path / "xdg" / "arcadedb-embedded" / "log" + assert _default_log_dir() == tmp_path / "xdg" / "arcadedb" / "log" with patch.dict(os.environ, {**env, "XDG_STATE_HOME": "relative"}): with patch.object(jvm.sys, "platform", "linux"): - expected = tmp_path / ".local" / "state" / "arcadedb-embedded" / "log" + expected = tmp_path / ".local" / "state" / "arcadedb" / "log" assert _default_log_dir() == expected with patch.dict(os.environ, env): with patch.object(jvm.sys, "platform", "darwin"): - expected = tmp_path / "Library" / "Logs" / "arcadedb-embedded" / "log" + expected = tmp_path / "Library" / "Logs" / "arcadedb" / "log" assert _default_log_dir() == expected with patch.dict(os.environ, {**env, "LOCALAPPDATA": str(tmp_path / "lad")}): with patch.object(jvm.sys, "platform", "win32"): - assert _default_log_dir() == tmp_path / "lad" / "arcadedb-embedded" / "log" + assert _default_log_dir() == tmp_path / "lad" / "arcadedb" / "log" def test_user_arguments_win_over_the_log_defaults(tmp_path): @@ -185,14 +185,14 @@ def test_windows_branch_needs_no_home_directory(tmp_path): with patch.object(jvm.sys, "platform", "win32"): with patch.dict(os.environ, {"LOCALAPPDATA": str(tmp_path)}, clear=True): with patch.object(Path, "home", side_effect=RuntimeError("no home")): - assert _default_log_dir() == tmp_path / "arcadedb-embedded" / "log" + assert _default_log_dir() == tmp_path / "arcadedb" / "log" with patch.dict(os.environ, {}, clear=True): with patch.object(Path, "home", side_effect=RuntimeError("no home")): - expected = Path(tempfile.gettempdir()) / "arcadedb-embedded" / "log" + expected = Path(tempfile.gettempdir()) / "arcadedb" / "log" assert _default_log_dir() == expected with patch.dict(os.environ, {}, clear=True): with patch.object(Path, "home", return_value=tmp_path): - expected = tmp_path / "AppData" / "Local" / "arcadedb-embedded" / "log" + expected = tmp_path / "AppData" / "Local" / "arcadedb" / "log" assert _default_log_dir() == expected diff --git a/tests/test_logging_helper.py b/tests/test_logging_helper.py index 623192dc..dfb1e383 100644 --- a/tests/test_logging_helper.py +++ b/tests/test_logging_helper.py @@ -2,17 +2,17 @@ import logging -from arcadedb_embedded._logging import get_logger, log_swallowed_exception +from arcadedb._logging import get_logger, log_swallowed_exception def test_get_logger_returns_namespaced_logger(): - logger = get_logger("arcadedb_embedded.foo") + logger = get_logger("arcadedb.foo") assert isinstance(logger, logging.Logger) - assert logger.name == "arcadedb_embedded.foo" + assert logger.name == "arcadedb.foo" def test_log_swallowed_exception_emits_debug(caplog): - logger = get_logger("arcadedb_embedded.test") + logger = get_logger("arcadedb.test") with caplog.at_level(logging.DEBUG, logger=logger.name): try: raise RuntimeError("boom") diff --git a/tests/test_materialized_view_sql.py b/tests/test_materialized_view_sql.py index 122396a0..1dd61bc4 100644 --- a/tests/test_materialized_view_sql.py +++ b/tests/test_materialized_view_sql.py @@ -1,6 +1,6 @@ """Materialized view SQL coverage for Python bindings.""" -import arcadedb_embedded as arcadedb +import arcadedb def test_materialized_view_sql_end_to_end(temp_db_path): diff --git a/tests/test_no_embedded_name.py b/tests/test_no_embedded_name.py new file mode 100644 index 00000000..c0a9c0a4 --- /dev/null +++ b/tests/test_no_embedded_name.py @@ -0,0 +1,22 @@ +"""The old package name must not come back. + +Runs scripts/check_no_embedded_name.py over the repository. It needs no wheel. The allowlist (history, +issue references, the fork's name on lines marked allow-old-name) lives in that script. +""" + +import subprocess # nosec B404 - runs sys.executable on a script this repository owns +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +CHECK = ROOT / "scripts" / "check_no_embedded_name.py" + + +def test_old_package_name_is_absent(): + result = subprocess.run( # nosec B603 - fixed argv, no shell + [sys.executable, "-I", str(CHECK), "--root", str(ROOT)], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stdout + result.stderr diff --git a/tests/test_null_index_known_issues.py b/tests/test_null_index_known_issues.py index baa3f0d6..b6b09727 100644 --- a/tests/test_null_index_known_issues.py +++ b/tests/test_null_index_known_issues.py @@ -34,7 +34,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb # id 1 has p = 1, id 2 has p = null, id 3 has no p; all have q = 7. THREE_RECORDS = ("id = 1, p = 1, q = 7", "id = 2, p = null, q = 7", "id = 3, q = 7") diff --git a/tests/test_numpy_support.py b/tests/test_numpy_support.py index cf93535c..c5c5c900 100644 --- a/tests/test_numpy_support.py +++ b/tests/test_numpy_support.py @@ -4,7 +4,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb np = pytest.importorskip("numpy") diff --git a/tests/test_open_read_only.py b/tests/test_open_read_only.py index c26b1e0b..2dec928b 100644 --- a/tests/test_open_read_only.py +++ b/tests/test_open_read_only.py @@ -8,8 +8,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import ArcadeDBError +import arcadedb +from arcadedb import ArcadeDBError @pytest.fixture diff --git a/tests/test_parameter_binding.py b/tests/test_parameter_binding.py index 1038ebb8..09ab7e9b 100644 --- a/tests/test_parameter_binding.py +++ b/tests/test_parameter_binding.py @@ -10,7 +10,7 @@ import jpype import pytest -import arcadedb_embedded as arcadedb +import arcadedb # Three spellings of "one positional parameter, bound to null". LONE_NULL = [ @@ -176,7 +176,7 @@ def test_async_parameters_reach_one_java_overload(temp_db): """The async executor's ``args`` reach the engine as one ``Object[]`` too. Splatted, a lone ``None`` arrived as a null in place of the whole array, so nothing was bound. """ - from arcadedb_embedded.async_executor import AsyncExecutor + from arcadedb.async_executor import AsyncExecutor seen = [] @@ -223,7 +223,7 @@ class TestScalarParametersTakeTheShortPath: @pytest.fixture def counted(self, monkeypatch): - from arcadedb_embedded import core + from arcadedb import core calls = [] real = core.convert_python_to_java diff --git a/tests/test_percall_calls.py b/tests/test_percall_calls.py index fb61ff48..e42e55cd 100644 --- a/tests/test_percall_calls.py +++ b/tests/test_percall_calls.py @@ -11,10 +11,10 @@ import numpy as np import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import core, results -from arcadedb_embedded.core import Database -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb import core, results +from arcadedb.core import Database +from arcadedb.exceptions import ArcadeDBError @pytest.fixture diff --git a/tests/test_python_callbacks.py b/tests/test_python_callbacks.py index bb80a4d2..061293b4 100644 --- a/tests/test_python_callbacks.py +++ b/tests/test_python_callbacks.py @@ -10,8 +10,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded.callbacks import EVENTS +import arcadedb +from arcadedb.callbacks import EVENTS @pytest.fixture diff --git a/tests/test_python_function_interrupt.py b/tests/test_python_function_interrupt.py index 36d624c3..c2a3332d 100644 --- a/tests/test_python_function_interrupt.py +++ b/tests/test_python_function_interrupt.py @@ -17,7 +17,7 @@ import numpy as np import pytest -import arcadedb_embedded as arcadedb +import arcadedb @pytest.fixture diff --git a/tests/test_rename_codemod.py b/tests/test_rename_codemod.py deleted file mode 100644 index b1b8ab9b..00000000 --- a/tests/test_rename_codemod.py +++ /dev/null @@ -1,144 +0,0 @@ -"""The rename codemod (scripts/rename_to_arcadedb.py) and the old-name check. - -Runs both on a small synthetic repository in a temporary directory, so no wheel is needed. What it -pins: the package directory moves, imports and metadata are rewritten, history on the allowlist is not -touched, issue references of the old repository keep their text, a second run changes nothing, and the -check passes on the result and fails on a planted old name. -""" - -import subprocess # nosec B404 - runs git and sys.executable on scripts this repository owns -import sys -from pathlib import Path - -import pytest - -SCRIPTS = Path(__file__).resolve().parent.parent / "scripts" -CODEMOD = SCRIPTS / "rename_to_arcadedb.py" -CHECK = SCRIPTS / "check_no_embedded_name.py" - -FILES = { - "src/arcadedb_embedded/__init__.py": '"""arcadedb_embedded."""\n__version__ = "1"\n', - "src/arcadedb_embedded/core.py": "from arcadedb_embedded import jvm\n", - "pyproject.toml": ( - '[project]\nname = "arcadedb-embedded"\n' - '[tool.setuptools]\npackages = ["arcadedb_embedded"]\n' - '[tool.setuptools.package-data]\narcadedb_embedded = ["jars/*.jar"]\n' - '[project.urls]\nHomepage = "https://github.com/humemai/arcadedb-embedded-python"\n' - ), - "tests/test_x.py": "import arcadedb_embedded as arcadedb\n\n\ndef test_x():\n assert arcadedb\n", - "scripts/build.sh": 'PACKAGE_NAME="arcadedb-embedded"\nls dist/arcadedb_embedded-*.whl\n', - ".gitignore": "src/arcadedb_embedded/jars/\n", - "README.md": ( - "# Title\n\n```bash\nuv add arcadedb-embedded # or: pip install arcadedb-embedded\n```\n\n" - "Fixed in humemai/arcadedb-embedded-python#12 and ARCADEDB_HOME stays.\n" - "Engine: com.arcadedb.Constants and arcadedata/arcadedb stay.\n" - ), - "docs/superpowers/plans/history.md": "The old package arcadedb-embedded lives on in this plan.\n", -} - - -def git(root, *args): - subprocess.run( - ["git", "-C", str(root), *args], check=True, capture_output=True - ) # nosec B603 B607 - - -def run(script, root, *args): - return subprocess.run( # nosec B603 - fixed argv, no shell - [sys.executable, "-I", str(script), "--root", str(root), *args], - capture_output=True, - text=True, - check=False, - ) - - -@pytest.fixture -def repo(tmp_path): - for rel, text in FILES.items(): - path = tmp_path / rel - path.parent.mkdir(parents=True, exist_ok=True) - path.write_bytes(text.encode("utf-8")) # LF on every platform - git(tmp_path, "init", "-q") - git(tmp_path, "add", "-A") - return tmp_path - - -def snapshot(root): - return { - p.relative_to(root).as_posix(): p.read_bytes() - for p in root.rglob("*") - if p.is_file() and ".git/" not in p.as_posix() - } - - -def test_check_fails_on_the_old_name_and_passes_on_the_allowlist(repo): - result = run(CHECK, repo) - assert result.returncode == 1 - assert "pyproject.toml" in result.stdout - assert "docs/superpowers" not in result.stdout # allowlisted history - assert ( - "humemai/arcadedb-embedded-python#12" not in result.stdout - ) # allowlisted issue reference - - -def test_codemod_renames_and_is_idempotent(repo): - first = run(CODEMOD, repo, "--no-format") - assert first.returncode == 0, first.stdout + first.stderr - - assert (repo / "src/arcadedb/__init__.py").is_file() - assert not (repo / "src/arcadedb_embedded").exists() - assert (repo / "src/arcadedb/core.py").read_text() == "from arcadedb import jvm\n" - assert (repo / "tests/test_x.py").read_text().startswith("import arcadedb\n") - pyproject = (repo / "pyproject.toml").read_text() - assert 'name = "arcadedb"' in pyproject - assert 'packages = ["arcadedb"]' in pyproject - assert '\narcadedb = ["jars/*.jar"]' in pyproject - assert "github.com/ArcadeData/arcadedb-python" in pyproject - assert ( - repo / "scripts/build.sh" - ).read_text() == 'PACKAGE_NAME="arcadedb"\nls dist/arcadedb-*.whl\n' - assert (repo / ".gitignore").read_text() == "src/arcadedb/jars/\n" - - readme = (repo / "README.md").read_text() - assert "uv add arcadedb # or: pip install arcadedb\n" in readme - assert ( - "This package was published as arcadedb-embedded until 26.10.x; install arcadedb now." - in readme - ) - assert ( - "humemai/arcadedb-embedded-python#12" in readme - ) # issue reference keeps its text - assert ( - "ARCADEDB_HOME" in readme - and "com.arcadedb.Constants" in readme - and "arcadedata/arcadedb" in readme - ) - - history = (repo / "docs/superpowers/plans/history.md").read_text() - assert history == FILES["docs/superpowers/plans/history.md"] # not rewritten - - assert (repo / "tests/test_no_embedded_name.py").is_file() - assert run(CHECK, repo).returncode == 0 - - git(repo, "add", "-A") - before = snapshot(repo) - second = run(CODEMOD, repo, "--no-format") - assert second.returncode == 0, second.stdout + second.stderr - assert snapshot(repo) == before - assert "total" in second.stdout and " 0 / 0" in second.stdout - - -def test_dry_run_changes_nothing(repo): - before = snapshot(repo) - result = run(CODEMOD, repo, "--dry-run") - assert result.returncode == 0, result.stdout + result.stderr - assert snapshot(repo) == before - assert "dry run" in result.stdout - - -def test_check_catches_a_planted_old_name_after_the_rename(repo): - assert run(CODEMOD, repo, "--no-format").returncode == 0 - (repo / "src/arcadedb/core.py").write_text("import arcadedb_embedded\n") - result = run(CHECK, repo) - assert result.returncode == 1 - assert "src/arcadedb/core.py:1" in result.stdout diff --git a/tests/test_restore_sql.py b/tests/test_restore_sql.py index 2ef53820..86357cd1 100644 --- a/tests/test_restore_sql.py +++ b/tests/test_restore_sql.py @@ -37,7 +37,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def _count_two_ways(db, type_name): diff --git a/tests/test_results_after_close.py b/tests/test_results_after_close.py index 2734c813..3704debb 100644 --- a/tests/test_results_after_close.py +++ b/tests/test_results_after_close.py @@ -4,8 +4,8 @@ import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb.exceptions import ArcadeDBError NAMES = ["Ann", "Bob", "Cy"] diff --git a/tests/test_resultset.py b/tests/test_resultset.py index f73979b8..30c03b84 100644 --- a/tests/test_resultset.py +++ b/tests/test_resultset.py @@ -4,7 +4,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def test_resultset_to_list(temp_db_path): @@ -389,9 +389,9 @@ def test_to_dict_one_crossing_matches_the_per_property_path(temp_db_path): from datetime import date, datetime from decimal import Decimal - import arcadedb_embedded as arcadedb - from arcadedb_embedded.results import _bridge_class - from arcadedb_embedded.type_conversion import convert_java_to_python + import arcadedb + from arcadedb.results import _bridge_class + from arcadedb.type_conversion import convert_java_to_python # Run alone, this is before the JVM starts: the answer must not be cached. _bridge_class("RowAccess") @@ -591,7 +591,7 @@ def counted(*args): @pytest.fixture def counting_bridge(monkeypatch): """Count the calls the Python layer makes into TypedRows.nextRows and RowBatcher.nextJsonBatch.""" - from arcadedb_embedded import results + from arcadedb import results typed_rows = results._bridge_class("TypedRows") row_batcher = results._bridge_class("RowBatcher") @@ -647,7 +647,7 @@ def test_to_json_list_makes_one_call_for_a_short_result( def test_a_full_batch_still_ends_on_the_next_call(self, temp_db, counting_bridge): """Exactly one batch of rows (the to_list batch): the first batch is full, so one more call finds it drained.""" - from arcadedb_embedded import results + from arcadedb import results batch = results._TYPED_BATCH_ROWS temp_db.command("sql", "CREATE DOCUMENT TYPE Exact") @@ -673,7 +673,7 @@ def test_a_drained_set_reads_as_empty_and_is_not_an_error( def test_python_does_not_cross_the_bridge_to_close_a_drained_set( self, three, counting_bridge, monkeypatch ): - from arcadedb_embedded import results + from arcadedb import results closes = [] real_close = results.ResultSet.close @@ -694,7 +694,7 @@ def test_the_bridge_closes_what_it_drains_and_only_that(self, three, batcher): has fewer rows than asked for, and leave one alone that still has rows.""" import jpype - from arcadedb_embedded import results + from arcadedb import results interface = jpype.JClass("com.arcadedb.query.sql.executor.ResultSet") diff --git a/tests/test_resultset_arrow.py b/tests/test_resultset_arrow.py index 5966cfc4..b5ae2666 100644 --- a/tests/test_resultset_arrow.py +++ b/tests/test_resultset_arrow.py @@ -12,7 +12,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb pa = pytest.importorskip("pyarrow") diff --git a/tests/test_resultset_read_errors.py b/tests/test_resultset_read_errors.py index d134206a..782088b2 100644 --- a/tests/test_resultset_read_errors.py +++ b/tests/test_resultset_read_errors.py @@ -19,8 +19,8 @@ import jpype import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import ArcadeDBError +import arcadedb +from arcadedb import ArcadeDBError FAILS_ON_THE_FIRST_ROW = [ pytest.param("SELECT 1 / (i - i) AS f FROM E", id="division-first"), diff --git a/tests/test_run_in_transaction_retry.py b/tests/test_run_in_transaction_retry.py index 30ecf679..59523351 100644 --- a/tests/test_run_in_transaction_retry.py +++ b/tests/test_run_in_transaction_retry.py @@ -19,8 +19,8 @@ import jpype import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import ArcadeDBError +import arcadedb +from arcadedb import ArcadeDBError def _java(name): diff --git a/tests/test_runtime_cache.py b/tests/test_runtime_cache.py index b229ac16..5ac3f2b7 100644 --- a/tests/test_runtime_cache.py +++ b/tests/test_runtime_cache.py @@ -16,17 +16,17 @@ import pytest -from arcadedb_embedded import jvm +from arcadedb import jvm RES = "fixture_res" # a name the package itself never ships, so the fallback runs def _wheel(dist, name, files, mtime): - """Write a wheel containing arcadedb_embedded// entries.""" + """Write a wheel containing arcadedb// entries.""" path = dist / name with zipfile.ZipFile(path, "w") as z: for fname, body in files.items(): - z.writestr(f"arcadedb_embedded/{RES}/{fname}", body) + z.writestr(f"arcadedb/{RES}/{fname}", body) os.utime(path, (mtime, mtime)) return path @@ -41,8 +41,8 @@ def project(tmp_path, monkeypatch): def test_newest_wheel_is_chosen_by_build_time_not_by_name(project): """A reverse string sort ranks 26.9.1 above 26.10.1 because "9" > "1".""" dist = project / "dist" - _wheel(dist, "arcadedb_embedded-26.9.1-py3-none-any.whl", {"v.txt": "old"}, 1_000) - _wheel(dist, "arcadedb_embedded-26.10.1-py3-none-any.whl", {"v.txt": "new"}, 2_000) + _wheel(dist, "arcadedb-26.9.1-py3-none-any.whl", {"v.txt": "old"}, 1_000) + _wheel(dist, "arcadedb-26.10.1-py3-none-any.whl", {"v.txt": "new"}, 2_000) out = jvm._extract_runtime_resource(RES) @@ -52,7 +52,7 @@ def test_newest_wheel_is_chosen_by_build_time_not_by_name(project): def test_a_rebuilt_wheel_replaces_the_cache_and_leaves_nothing_behind(project): """The June 4 failure: a newer wheel appears and the old extraction stays.""" dist = project / "dist" - name = "arcadedb_embedded-26.10.1.dev0-py3-none-any.whl" + name = "arcadedb-26.10.1.dev0-py3-none-any.whl" _wheel(dist, name, {"engine-26.6.1.jar": "june", "only-in-old.jar": "x"}, 1_000) first = jvm._extract_runtime_resource(RES) assert (first / "engine-26.6.1.jar").exists() @@ -70,7 +70,7 @@ def test_a_rebuilt_wheel_replaces_the_cache_and_leaves_nothing_behind(project): def test_an_unchanged_wheel_is_not_re_extracted(project): """The cache is still a cache: the same wheel does not pay extraction twice.""" dist = project / "dist" - _wheel(dist, "arcadedb_embedded-26.10.1-py3-none-any.whl", {"a.jar": "a"}, 1_000) + _wheel(dist, "arcadedb-26.10.1-py3-none-any.whl", {"a.jar": "a"}, 1_000) out = jvm._extract_runtime_resource(RES) (out / "marker").write_text("still here") @@ -82,12 +82,12 @@ def test_an_unchanged_wheel_is_not_re_extracted(project): def test_a_cache_from_before_stamping_is_treated_as_stale(project): """Every cache that exists today predates the stamp, and must not be trusted.""" dist = project / "dist" - legacy = project / ".runtime-cache" / "arcadedb_embedded" / RES + legacy = project / ".runtime-cache" / "arcadedb" / RES legacy.mkdir(parents=True) (legacy / "engine-26.6.1.jar").write_text("june") _wheel( dist, - "arcadedb_embedded-26.10.1-py3-none-any.whl", + "arcadedb-26.10.1-py3-none-any.whl", {"engine-26.10.1.jar": "sep"}, 2_000, ) diff --git a/tests/test_schema.py b/tests/test_schema.py index 3863c3f4..37b37504 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -13,7 +13,7 @@ import pytest -from arcadedb_embedded import ArcadeDBError, IndexType, PropertyType, create_database +from arcadedb import ArcadeDBError, IndexType, PropertyType, create_database @pytest.fixture @@ -669,13 +669,13 @@ def test_get_lsm_vector_index_existing(self, test_db): # Retrieve using auto detection index = schema.get_vector_index("Doc", "embedding") assert index is not None - from arcadedb_embedded.vector import VectorIndex + from arcadedb.vector import VectorIndex assert isinstance(index, VectorIndex) def test_get_lsm_vector_index_persistence(self, test_db): """Test that get_vector_index can load persisted LSM indexes.""" - import arcadedb_embedded as arcadedb + import arcadedb schema = test_db.schema schema.create_vertex_type("Doc") diff --git a/tests/test_schema_batching.py b/tests/test_schema_batching.py index 08f0888e..eae0ad4b 100644 --- a/tests/test_schema_batching.py +++ b/tests/test_schema_batching.py @@ -8,7 +8,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb def test_many_schema_statements_in_one_transaction_all_take_effect(temp_db_path): diff --git a/tests/test_server.py b/tests/test_server.py index 0c2d5f5e..50be8350 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -16,7 +16,7 @@ import pytest -from arcadedb_embedded import ArcadeDBServer +from arcadedb import ArcadeDBServer from tests.conftest import TEST_PASSWORD, free_port @@ -129,7 +129,7 @@ def test_default_host_is_localhost(temp_server_root): host feeds both ``get_studio_url()`` and the underlying ContextConfiguration, so the URL is a faithful proxy for the configured host. """ - from arcadedb_embedded.server import ArcadeDBServer + from arcadedb.server import ArcadeDBServer server = ArcadeDBServer( root_path=temp_server_root, @@ -155,7 +155,7 @@ def test_failed_server_start_does_not_hang_process_exit(tmp_path): code = ( "import socket\n" - "from arcadedb_embedded import create_server\n" + "from arcadedb import create_server\n" "def free():\n" " with socket.socket() as s:\n" " s.bind(('127.0.0.1', 0))\n" diff --git a/tests/test_server_http_endpoints.py b/tests/test_server_http_endpoints.py index 45f43374..ec0e057d 100644 --- a/tests/test_server_http_endpoints.py +++ b/tests/test_server_http_endpoints.py @@ -11,7 +11,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb from tests.conftest import TEST_PASSWORD, free_port diff --git a/tests/test_server_packaging.py b/tests/test_server_packaging.py index 040d770b..d7a017b8 100644 --- a/tests/test_server_packaging.py +++ b/tests/test_server_packaging.py @@ -39,7 +39,7 @@ def _jar_names(): - from arcadedb_embedded.jvm import get_jar_path + from arcadedb.jvm import get_jar_path jar_dir = get_jar_path() assert os.path.isdir(jar_dir), f"jar directory missing: {jar_dir}" @@ -66,9 +66,9 @@ def test_server_api_is_importable_and_exported(): Both halves matter: the classes existing but not being exported is the same breakage from a user's point of view, since the documented import is - ``from arcadedb_embedded import create_server``. + ``from arcadedb import create_server``. """ - import arcadedb_embedded as adb + import arcadedb as adb assert hasattr(adb, "ArcadeDBServer"), "ArcadeDBServer not importable" assert hasattr(adb, "create_server"), "create_server not importable" @@ -85,7 +85,7 @@ def test_studio_jar_carries_no_classes(): and the documentation needs revisiting, so assert the premise rather than trusting it. """ - from arcadedb_embedded.jvm import get_jar_path + from arcadedb.jvm import get_jar_path jar_dir = get_jar_path() studio = [n for n in os.listdir(jar_dir) if n.startswith("arcadedb-studio")] @@ -109,7 +109,7 @@ def test_no_apexcharts_in_the_package(): scripts/verify_wheel_licences.py guards the wheel at build time; this checks what pip actually installed. """ - from arcadedb_embedded.jvm import get_jar_path + from arcadedb.jvm import get_jar_path package_dir = os.path.dirname(get_jar_path()) for root, _dirs, files in os.walk(package_dir): @@ -132,7 +132,7 @@ def test_server_starts_and_serves_http(temp_server_root): work together, which is the claim a user actually cares about. """ requests = pytest.importorskip("requests") - from arcadedb_embedded import create_server + from arcadedb import create_server from tests.conftest import TEST_PASSWORD, free_port with create_server( @@ -182,7 +182,7 @@ def served(temp_server_root): """A started server, a database named ``packaging``, and an authenticated session.""" import requests - from arcadedb_embedded import create_server + from arcadedb import create_server from tests.conftest import TEST_PASSWORD, free_port with create_server( diff --git a/tests/test_server_patterns.py b/tests/test_server_patterns.py index ce342158..c77791d6 100644 --- a/tests/test_server_patterns.py +++ b/tests/test_server_patterns.py @@ -20,7 +20,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb from tests.conftest import TEST_PASSWORD, free_port diff --git a/tests/test_server_wire_protocols.py b/tests/test_server_wire_protocols.py index b43524f2..2973bf97 100644 --- a/tests/test_server_wire_protocols.py +++ b/tests/test_server_wire_protocols.py @@ -72,7 +72,7 @@ def _wait(port, timeout=60.0): @pytest.fixture def wire_server(tmp_path): """A server with all three bundled wire plugins enabled.""" - from arcadedb_embedded import create_server + from arcadedb import create_server ports = _free_ports("http", "postgres", "redis", "bolt") server = create_server( @@ -114,7 +114,7 @@ def test_plugins_are_opt_in(tmp_path): Pinned because the other tests only mean something if enabling the plugins is what turns them on. """ - from arcadedb_embedded import create_server + from arcadedb import create_server http = _free_port() server = create_server( @@ -359,7 +359,7 @@ def test_wire_listeners_stay_on_loopback_by_default(wire_server): def test_host_applies_to_every_wire_listener(tmp_path): - from arcadedb_embedded import create_server + from arcadedb import create_server ports = _free_ports("http", "postgres", "redis", "bolt") server = create_server( @@ -388,7 +388,7 @@ def test_host_applies_to_every_wire_listener(tmp_path): def test_a_protocol_host_overrides_the_server_host_for_that_protocol_only(tmp_path): - from arcadedb_embedded import create_server + from arcadedb import create_server ports = _free_ports("http", "postgres", "redis") server = create_server( @@ -428,8 +428,8 @@ def test_the_host_reaches_exactly_the_listener_bind_settings(): interfaces joins this set (and so follows `host`), and a non-listener setting that happened to match the pattern would show up here too. """ - from arcadedb_embedded.jvm import start_jvm - from arcadedb_embedded.server import _all_interfaces_host_settings + from arcadedb.jvm import start_jvm + from arcadedb.server import _all_interfaces_host_settings start_jvm() assert set(_all_interfaces_host_settings()) == { diff --git a/tests/test_sigint.py b/tests/test_sigint.py index 5cc2f60a..143ded23 100644 --- a/tests/test_sigint.py +++ b/tests/test_sigint.py @@ -31,8 +31,8 @@ _CHILD = """ import atexit, sys, time -import arcadedb_embedded as arcadedb -from arcadedb_embedded.jvm import start_jvm +import arcadedb +from arcadedb.jvm import start_jvm start_jvm(heap_size="512m", interrupt=(sys.argv[3] == "1")) db = arcadedb.create_database(sys.argv[2]) diff --git a/tests/test_sparse_quantization_compact.py b/tests/test_sparse_quantization_compact.py index a554b5c1..98b3008a 100644 --- a/tests/test_sparse_quantization_compact.py +++ b/tests/test_sparse_quantization_compact.py @@ -5,7 +5,7 @@ import jpype.types as jtypes import pytest -import arcadedb_embedded as arcadedb +import arcadedb DIMS = 512 diff --git a/tests/test_stage_jars.py b/tests/test_stage_jars.py index f5926ad1..70144641 100644 --- a/tests/test_stage_jars.py +++ b/tests/test_stage_jars.py @@ -1,6 +1,6 @@ """A native build bundles the jars it was asked for, not the last build's (#9). -build-native.sh reused whatever was in src/arcadedb_embedded/jars: with no +build-native.sh reused whatever was in src/arcadedb/jars: with no jars directory given it never pulled ``arcadedata/arcadedb:${ARCADEDB_TAG}``, so a wheel built after ``latest`` moved carried the old engine; with one given (build.sh's third argument) it ignored it. scripts/stage_jars.sh now stages @@ -120,7 +120,7 @@ def test_build_sh_hands_its_jars_dir_to_the_native_build(): def test_ci_path_restages_the_artifact_from_local_jars(tmp_path): - # CI: build.sh copies its jars dir (the artifact, src/arcadedb_embedded/jars) + # CI: build.sh copies its jars dir (the artifact, src/arcadedb/jars) # into local-jars/lib and hands that over; a previous build's leftovers in # the jars dir go, the artifact's jars come back. jars = _jars( diff --git a/tests/test_temporal_writers_agree.py b/tests/test_temporal_writers_agree.py index f37cdb2a..8cfbd6ce 100644 --- a/tests/test_temporal_writers_agree.py +++ b/tests/test_temporal_writers_agree.py @@ -18,12 +18,13 @@ import datetime as dt import json -import arcadedb_embedded as arcadedb # noqa: F401 (starts the JVM through the fixtures) import jpype import numpy as np import pytest -from arcadedb_embedded import results -from arcadedb_embedded.exceptions import ArcadeDBError + +import arcadedb # noqa: F401 (starts the JVM through the fixtures) +from arcadedb import results +from arcadedb.exceptions import ArcadeDBError MIDNIGHT_UTC_2024_01_02 = 1704153600000 DAY_MS = 86_400_000 diff --git a/tests/test_timeseries_sql.py b/tests/test_timeseries_sql.py index 20ca3335..8eea1c08 100644 --- a/tests/test_timeseries_sql.py +++ b/tests/test_timeseries_sql.py @@ -2,7 +2,7 @@ from datetime import datetime, timezone -import arcadedb_embedded as arcadedb +import arcadedb def _create_timeseries(db): diff --git a/tests/test_transaction_config.py b/tests/test_transaction_config.py index 1634cb22..c2fe61eb 100644 --- a/tests/test_transaction_config.py +++ b/tests/test_transaction_config.py @@ -6,7 +6,7 @@ import pytest -from arcadedb_embedded import ArcadeDBError, create_database +from arcadedb import ArcadeDBError, create_database def test_set_wal_flush_modes(temp_db): diff --git a/tests/test_transaction_context_interrupt.py b/tests/test_transaction_context_interrupt.py index 14d34649..9c796876 100644 --- a/tests/test_transaction_context_interrupt.py +++ b/tests/test_transaction_context_interrupt.py @@ -5,7 +5,7 @@ import pytest -from arcadedb_embedded.transactions import TransactionContext +from arcadedb.transactions import TransactionContext class _FakeDatabase: diff --git a/tests/test_type_conversion.py b/tests/test_type_conversion.py index 023b7470..5e00a334 100644 --- a/tests/test_type_conversion.py +++ b/tests/test_type_conversion.py @@ -5,7 +5,7 @@ from datetime import date, datetime, timedelta, timezone from decimal import Decimal -import arcadedb_embedded as arcadedb +import arcadedb def test_basic_type_conversion(temp_db_path): @@ -121,7 +121,7 @@ def test_offset_datetime_conversion(temp_db_path): """ import jpype - from arcadedb_embedded import convert_java_to_python + from arcadedb import convert_java_to_python with arcadedb.create_database(temp_db_path) as db: # starts the JVM db.command("sql", "CREATE DOCUMENT TYPE OffsetTest") @@ -364,7 +364,7 @@ def test_python_to_java_conversion(temp_db_path): doc.set("active", True) # bool # Convert list to Java ArrayList for compatibility - from arcadedb_embedded.type_conversion import convert_python_to_java + from arcadedb.type_conversion import convert_python_to_java doc.set("tags", convert_python_to_java(["a", "b", "c"])) # list doc.set("metadata", convert_python_to_java({"key": "value"})) # dict @@ -496,7 +496,7 @@ def test_scalar_list_crosses_as_one_array_with_the_same_types(temp_db_path): import jpype import pytest - from arcadedb_embedded.type_conversion import convert_python_to_java + from arcadedb.type_conversion import convert_python_to_java with arcadedb.create_database(temp_db_path) as db: values = [1, -(2**63), 2**63 - 1, 1.5, float("inf"), "x", "", True, None] @@ -553,7 +553,7 @@ def test_array_conversion(temp_db_path): with db.transaction(): doc = db.new_document("ArrayTest") # Use Java collections via conversion - from arcadedb_embedded.type_conversion import convert_python_to_java + from arcadedb.type_conversion import convert_python_to_java doc.set("numbers", convert_python_to_java([1, 2, 3, 4, 5])) doc.set("names", convert_python_to_java(["Alice", "Bob", "Charlie"])) @@ -583,7 +583,7 @@ class TestPrimitiveArrayFormats: def test_int_array(self): import jpype - from arcadedb_embedded.type_conversion import convert_java_to_python + from arcadedb.type_conversion import convert_java_to_python assert convert_java_to_python(jpype.JArray(jpype.JInt)([1, 2, 3])) == [1, 2, 3] # second conversion exercises the cached converter @@ -592,7 +592,7 @@ def test_int_array(self): def test_long_array(self): import jpype - from arcadedb_embedded.type_conversion import convert_java_to_python + from arcadedb.type_conversion import convert_java_to_python big = 2**40 assert convert_java_to_python(jpype.JArray(jpype.JLong)([big, -big])) == [ @@ -601,7 +601,7 @@ def test_long_array(self): ] def test_cache_not_poisoned_by_failure(self): - from arcadedb_embedded import type_conversion as tc + from arcadedb import type_conversion as tc class Boom: pass diff --git a/tests/test_typed_rows.py b/tests/test_typed_rows.py index c1d79ae5..6565511f 100644 --- a/tests/test_typed_rows.py +++ b/tests/test_typed_rows.py @@ -15,9 +15,9 @@ import numpy as np import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import results -from arcadedb_embedded.exceptions import ArcadeDBError +import arcadedb +from arcadedb import results +from arcadedb.exceptions import ArcadeDBError def exact(value): @@ -366,7 +366,7 @@ def test_without_the_bridge_classes_the_plain_paths_answer_the_same( db, graph, monkeypatch ): """A jar without TypedRows or GraphCalls sends every caller down the plain path.""" - from arcadedb_embedded import graph as graph_module + from arcadedb import graph as graph_module wide = [exact(r) for r in db.query("sql", "SELECT FROM Wide").to_list()] people_db, people = graph diff --git a/tests/test_vector.py b/tests/test_vector.py index db8ee8ef..2dbef2b7 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -8,7 +8,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb @pytest.fixture @@ -126,7 +126,7 @@ def test_create_vector_index_build_graph_now_default_true( test_db.command("sql", "CREATE VERTEX TYPE Doc") test_db.command("sql", "CREATE PROPERTY Doc.embedding ARRAY_OF_FLOATS") - from arcadedb_embedded.vector import VectorIndex + from arcadedb.vector import VectorIndex called = {"count": 0} original_build_graph_now = VectorIndex.build_graph_now @@ -148,7 +148,7 @@ def test_create_vector_index_build_graph_now_can_be_disabled( test_db.command("sql", "CREATE VERTEX TYPE Doc") test_db.command("sql", "CREATE PROPERTY Doc.embedding ARRAY_OF_FLOATS") - from arcadedb_embedded.vector import VectorIndex + from arcadedb.vector import VectorIndex called = {"count": 0} original_build_graph_now = VectorIndex.build_graph_now @@ -180,7 +180,7 @@ def test_create_vector_index(self, test_db): assert index is not None # Check if it's the wrapper - from arcadedb_embedded.vector import VectorIndex + from arcadedb.vector import VectorIndex assert isinstance(index, VectorIndex) @@ -730,7 +730,7 @@ def test_lsm_vector_search_looks_up_all_hits_in_one_bridge_call( arcadedb.to_java_float_array([0.9, 0.1, 0.0]), ) - from arcadedb_embedded import graph + from arcadedb import graph calls = graph._graph_calls() assert calls is not None @@ -1003,7 +1003,7 @@ def test_lsm_index_stats(self, test_db): def test_lsm_persistence(self, temp_db_path): """Test that LSM index persists across database restarts.""" - import arcadedb_embedded as arcadedb + import arcadedb # 1. Create DB and Index with arcadedb.create_database(temp_db_path) as db: diff --git a/tests/test_vector_delta_visibility.py b/tests/test_vector_delta_visibility.py index c6816f91..a26e9513 100644 --- a/tests/test_vector_delta_visibility.py +++ b/tests/test_vector_delta_visibility.py @@ -28,7 +28,7 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb DIM = 8 BASE = 300 # threshold = max(100, min(300*0.2, 50_000)) = 100 diff --git a/tests/test_vector_params_verification.py b/tests/test_vector_params_verification.py index 7c1dc0a3..483dc0ce 100644 --- a/tests/test_vector_params_verification.py +++ b/tests/test_vector_params_verification.py @@ -1,6 +1,6 @@ import pytest -import arcadedb_embedded as arcadedb +import arcadedb @pytest.fixture @@ -48,7 +48,7 @@ def test_quantization_param(self, test_db): # In Java: index.getMetadata().quantizationType # JPype allows attribute access for getters or fields # Note: the exact field name depends on the Java class implementation of metadata - # Given src/arcadedb_embedded/vector.py uses .quantizationType: + # Given src/arcadedb/vector.py uses .quantizationType: assert str(idx_to_check.getMetadata().quantizationType) == "INT8" def test_store_vectors_in_graph_param(self, test_db): diff --git a/tests/test_vector_product_quantization.py b/tests/test_vector_product_quantization.py index d5d6d9d0..07d72391 100644 --- a/tests/test_vector_product_quantization.py +++ b/tests/test_vector_product_quantization.py @@ -22,8 +22,8 @@ import numpy as np import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import create_database +import arcadedb +from arcadedb import create_database DIMS = 32 COUNT = 2000 diff --git a/tests/test_vector_second_pass.py b/tests/test_vector_second_pass.py index 9e4b1e07..88caf3fd 100644 --- a/tests/test_vector_second_pass.py +++ b/tests/test_vector_second_pass.py @@ -4,7 +4,7 @@ import random -import arcadedb_embedded as arcadedb +import arcadedb def test_second_pass_returns_identical_neighbours(temp_db): diff --git a/tests/test_vector_sql.py b/tests/test_vector_sql.py index 03bf0c57..03932b23 100644 --- a/tests/test_vector_sql.py +++ b/tests/test_vector_sql.py @@ -10,8 +10,8 @@ import jpype.types as jtypes import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import create_database +import arcadedb +from arcadedb import create_database @pytest.fixture diff --git a/tests/test_wheel_licences.py b/tests/test_wheel_licences.py index d5030f9b..b523a844 100644 --- a/tests/test_wheel_licences.py +++ b/tests/test_wheel_licences.py @@ -52,8 +52,8 @@ def test_a_clean_wheel_passes(tmp_path, capsys): wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", { - "arcadedb_embedded/jars/arcadedb-server-1.jar": _jar({"A.class": b"x"}), - "arcadedb_embedded/server.py": b"", + "arcadedb/jars/arcadedb-server-1.jar": _jar({"A.class": b"x"}), + "arcadedb/server.py": b"", }, ) assert module.main([str(wheel)]) == 0 @@ -65,7 +65,7 @@ def test_a_studio_jar_with_echarts_passes(tmp_path, capsys): wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", { - "arcadedb_embedded/jars/arcadedb-studio-26.11.1.jar": _jar( + "arcadedb/jars/arcadedb-studio-26.11.1.jar": _jar( { "static/index.html": b"x", "static/dist/js/echarts.min.js": b"x", @@ -81,7 +81,7 @@ def test_an_echarts_file_outside_a_jar_passes(tmp_path): module = _load() wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", - {"arcadedb_embedded/static/dist/js/echarts.min.js": b"x"}, + {"arcadedb/static/dist/js/echarts.min.js": b"x"}, ) assert module.main([str(wheel)]) == 0 @@ -90,7 +90,7 @@ def test_an_apexcharts_file_fails_the_wheel(tmp_path, capsys): module = _load() wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", - {"arcadedb_embedded/static/dist/js/apexcharts.min.js": b"x"}, + {"arcadedb/static/dist/js/apexcharts.min.js": b"x"}, ) assert module.main([str(wheel)]) == 1 err = capsys.readouterr().err @@ -103,7 +103,7 @@ def test_a_studio_jar_from_an_older_engine_fails_the_wheel(tmp_path, capsys): wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", { - "arcadedb_embedded/jars/arcadedb-studio-26.10.2.jar": _jar( + "arcadedb/jars/arcadedb-studio-26.10.2.jar": _jar( { "static/index.html": b"x", "static/dist/js/apexcharts.min.js": b"x", @@ -121,11 +121,7 @@ def test_a_renamed_jar_that_carries_apexcharts_fails_the_wheel(tmp_path, capsys) module = _load() wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", - { - "arcadedb_embedded/jars/ui-1.jar": _jar( - {"static/dist/js/ApexCharts.min.js": b"x"} - ) - }, + {"arcadedb/jars/ui-1.jar": _jar({"static/dist/js/ApexCharts.min.js": b"x"})}, ) assert module.main([str(wheel), str(wheel)]) == 1 assert "ui-1.jar contains" in capsys.readouterr().err @@ -135,7 +131,7 @@ def test_a_jar_that_is_not_a_zip_fails_the_wheel(tmp_path, capsys): module = _load() wheel = _wheel( tmp_path / "a-1-py3-none-any.whl", - {"arcadedb_embedded/jars/broken-1.jar": b"not a zip"}, + {"arcadedb/jars/broken-1.jar": b"not a zip"}, ) assert module.main([str(wheel)]) == 1 assert "cannot be checked" in capsys.readouterr().err diff --git a/tests/test_wheel_platform_tag.py b/tests/test_wheel_platform_tag.py index 8651ab8b..8997f0f5 100644 --- a/tests/test_wheel_platform_tag.py +++ b/tests/test_wheel_platform_tag.py @@ -57,7 +57,7 @@ def test_max_glibc_in_dir_picks_highest(tmp_path: Path) -> None: def test_parse_wheel_tag_extracts_version_and_arch(tmp_path: Path) -> None: verifier = _load_verifier() - wheel = tmp_path / "arcadedb_embedded-26.4.2-py3-none-manylinux_2_34_x86_64.whl" + wheel = tmp_path / "arcadedb-26.4.2-py3-none-manylinux_2_34_x86_64.whl" wheel.write_bytes(b"") glibc, arch = verifier.parse_wheel_tag(wheel) @@ -73,7 +73,7 @@ def test_main_succeeds_when_tag_matches( jre_dir.mkdir() _make_elf_with_glibc_symbols(jre_dir / "libjvm.so", ["2.17", "2.34"]) - wheel = tmp_path / "arcadedb_embedded-26.4.2-py3-none-manylinux_2_34_x86_64.whl" + wheel = tmp_path / "arcadedb-26.4.2-py3-none-manylinux_2_34_x86_64.whl" wheel.write_bytes(b"") rc = _run_main(verifier, [str(wheel), str(jre_dir)]) @@ -90,7 +90,7 @@ def test_main_fails_when_tag_higher_than_jre( jre_dir.mkdir() _make_elf_with_glibc_symbols(jre_dir / "libjvm.so", ["2.17", "2.34"]) - wheel = tmp_path / "arcadedb_embedded-26.4.2-py3-none-manylinux_2_35_x86_64.whl" + wheel = tmp_path / "arcadedb-26.4.2-py3-none-manylinux_2_35_x86_64.whl" wheel.write_bytes(b"") rc = _run_main(verifier, [str(wheel), str(jre_dir)]) @@ -109,7 +109,7 @@ def test_main_fails_when_tag_lower_than_jre( jre_dir.mkdir() _make_elf_with_glibc_symbols(jre_dir / "libjvm.so", ["2.17", "2.36"]) - wheel = tmp_path / "arcadedb_embedded-26.4.2-py3-none-manylinux_2_28_x86_64.whl" + wheel = tmp_path / "arcadedb-26.4.2-py3-none-manylinux_2_28_x86_64.whl" wheel.write_bytes(b"") rc = _run_main(verifier, [str(wheel), str(jre_dir)]) @@ -148,10 +148,10 @@ def test_dunder_version_matches_distribution_metadata(): from importlib.metadata import PackageNotFoundError from importlib.metadata import version as dist_version - import arcadedb_embedded + import arcadedb try: - expected = dist_version("arcadedb-embedded") + expected = dist_version("arcadedb") except PackageNotFoundError: # source checkout, nothing to compare against return - assert arcadedb_embedded.__version__ == expected + assert arcadedb.__version__ == expected diff --git a/tests/test_xml_limits.py b/tests/test_xml_limits.py index 8acd4c9c..807dd971 100644 --- a/tests/test_xml_limits.py +++ b/tests/test_xml_limits.py @@ -5,10 +5,10 @@ import jpype import pytest -import arcadedb_embedded as arcadedb -from arcadedb_embedded import jvm -from arcadedb_embedded.exceptions import ArcadeDBError -from arcadedb_embedded.jvm import _build_jvm_args +import arcadedb +from arcadedb import jvm +from arcadedb.exceptions import ArcadeDBError +from arcadedb.jvm import _build_jvm_args LIMIT_PROPERTIES = ( "jdk.xml.maxGeneralEntitySizeLimit", @@ -75,7 +75,7 @@ def test_per_call_option_lifts_the_limits_for_that_call_only(db, big_xml): def test_per_call_option_restores_a_value_that_was_already_set(): - from arcadedb_embedded.importer import _xml_limits_lifted + from arcadedb.importer import _xml_limits_lifted System = jpype.JClass("java.lang.System") name = LIMIT_PROPERTIES[0] From 3c42667243e05c94757087711c35a4f368d66cbd Mon Sep 17 00:00:00 2001 From: Taewoon Kim Date: Sun, 25 Oct 2026 10:48:02 +0900 Subject: [PATCH 3/3] Rename: the examples installed dist/*embed*.whl; use arcadedb-*.whl, and teach the name check about wildcard spellings Co-Authored-By: Claude Opus 5.5 --- .github/workflows/test-python-examples.yml | 2 +- examples/07_stackoverflow_tables_oltp.py | 2 +- examples/08_stackoverflow_tables_olap.py | 2 +- examples/09_stackoverflow_graph_oltp.py | 2 +- examples/10_stackoverflow_graph_olap.py | 2 +- examples/11_vector_index_build.py | 2 +- examples/12_vector_search.py | 2 +- scripts/check_no_embedded_name.py | 7 ++++++- tests/test_no_embedded_name.py | 17 +++++++++++++++++ 9 files changed, 30 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-python-examples.yml b/.github/workflows/test-python-examples.yml index 148ed8d0..93d54b4e 100644 --- a/.github/workflows/test-python-examples.yml +++ b/.github/workflows/test-python-examples.yml @@ -263,7 +263,7 @@ jobs: - name: Install ArcadeDB Python bindings shell: bash run: | - uv pip install --system dist/*embed*.whl + uv pip install --system dist/arcadedb-*.whl - name: Install example dependencies shell: bash diff --git a/examples/07_stackoverflow_tables_oltp.py b/examples/07_stackoverflow_tables_oltp.py index bd62cdd6..648ed2e9 100644 --- a/examples/07_stackoverflow_tables_oltp.py +++ b/examples/07_stackoverflow_tables_oltp.py @@ -2148,7 +2148,7 @@ def run_in_docker(args): arcadedb_wheel_mount_path = None if args.db == "arcadedb_sql": - wheel_candidates = sorted((repo_root / "dist").glob("*embed*.whl")) + wheel_candidates = sorted((repo_root / "dist").glob("arcadedb-*.whl")) if not wheel_candidates: if os.environ.get("GITHUB_ACTIONS", "").lower() == "true": raise RuntimeError( diff --git a/examples/08_stackoverflow_tables_olap.py b/examples/08_stackoverflow_tables_olap.py index b4a0028c..11ee0c74 100644 --- a/examples/08_stackoverflow_tables_olap.py +++ b/examples/08_stackoverflow_tables_olap.py @@ -1449,7 +1449,7 @@ def run_in_docker(args): arcadedb_wheel_mount_path = None if args.db == "arcadedb_sql": - wheel_candidates = sorted((repo_root / "dist").glob("*embed*.whl")) + wheel_candidates = sorted((repo_root / "dist").glob("arcadedb-*.whl")) if not wheel_candidates: if os.environ.get("GITHUB_ACTIONS", "").lower() == "true": raise RuntimeError( diff --git a/examples/09_stackoverflow_graph_oltp.py b/examples/09_stackoverflow_graph_oltp.py index cab2c75a..9da67ea4 100644 --- a/examples/09_stackoverflow_graph_oltp.py +++ b/examples/09_stackoverflow_graph_oltp.py @@ -9747,7 +9747,7 @@ def run_in_docker(args) -> bool: arcadedb_wheel_mount_path = None if args.db.startswith("arcadedb_"): - wheel_candidates = sorted((repo_root / "dist").glob("*embed*.whl")) + wheel_candidates = sorted((repo_root / "dist").glob("arcadedb-*.whl")) if not wheel_candidates: if os.environ.get("GITHUB_ACTIONS", "").lower() == "true": raise RuntimeError( diff --git a/examples/10_stackoverflow_graph_olap.py b/examples/10_stackoverflow_graph_olap.py index 37f04f26..5640cfd4 100644 --- a/examples/10_stackoverflow_graph_olap.py +++ b/examples/10_stackoverflow_graph_olap.py @@ -6520,7 +6520,7 @@ def run_in_docker(args) -> bool: arcadedb_wheel_mount_path = None if args.db == "arcadedb_cypher": - wheel_candidates = sorted((repo_root / "dist").glob("*embed*.whl")) + wheel_candidates = sorted((repo_root / "dist").glob("arcadedb-*.whl")) if not wheel_candidates: if os.environ.get("GITHUB_ACTIONS", "").lower() == "true": raise RuntimeError( diff --git a/examples/11_vector_index_build.py b/examples/11_vector_index_build.py index 87dde751..7da4bbe7 100644 --- a/examples/11_vector_index_build.py +++ b/examples/11_vector_index_build.py @@ -1688,7 +1688,7 @@ def run_in_docker(args) -> bool: arcadedb_wheel_mount_path = None if args.backend == "arcadedb_sql": - wheel_candidates = sorted((repo_root / "dist").glob("*embed*.whl")) + wheel_candidates = sorted((repo_root / "dist").glob("arcadedb-*.whl")) if not wheel_candidates: if os.environ.get("GITHUB_ACTIONS", "").lower() == "true": raise RuntimeError( diff --git a/examples/12_vector_search.py b/examples/12_vector_search.py index 59e94960..4b909f92 100644 --- a/examples/12_vector_search.py +++ b/examples/12_vector_search.py @@ -1955,7 +1955,7 @@ def _to_container_path(value: str) -> str: arcadedb_wheel_mount_path = None if args.backend == "arcadedb_sql": - wheel_candidates = sorted((repo_root / "dist").glob("*embed*.whl")) + wheel_candidates = sorted((repo_root / "dist").glob("arcadedb-*.whl")) if not wheel_candidates: if os.environ.get("GITHUB_ACTIONS", "").lower() == "true": raise RuntimeError( diff --git a/scripts/check_no_embedded_name.py b/scripts/check_no_embedded_name.py index 46272a32..8ad7d0c2 100644 --- a/scripts/check_no_embedded_name.py +++ b/scripts/check_no_embedded_name.py @@ -37,7 +37,11 @@ # The old names. `arcadedb-embedded` also matches inside `arcadedb-embedded-native` and # `arcadedb-embedded-python`; those are old names too. -OLD_NAME = re.compile(r"arcadedb[_-]embedded|ArcadeDB Embedded Python", re.IGNORECASE) +# Also glob/wildcard spellings that only match the old wheel name (`*embed*.whl`, `embedded*`): the rename missed +# `dist/*embed*.whl` in the examples workflow and six examples, and CI failed on it (#42, 2026-10-25). +OLD_NAME = re.compile( + r"arcadedb[_-]embedded|ArcadeDB Embedded Python|\*embed(?:ded)?\*", re.IGNORECASE +) # Files (fnmatch patterns on the path relative to the repository root) that may name the old package. ALLOWED_PATHS: tuple[str, ...] = ( @@ -46,6 +50,7 @@ "HISTORY*", "NEWS*", "scripts/check_no_embedded_name.py", + "tests/test_no_embedded_name.py", # holds the old patterns as test cases ) # Text that is removed from a line before it is scanned: issue references of the old repository. diff --git a/tests/test_no_embedded_name.py b/tests/test_no_embedded_name.py index c0a9c0a4..e44d11f7 100644 --- a/tests/test_no_embedded_name.py +++ b/tests/test_no_embedded_name.py @@ -20,3 +20,20 @@ def test_old_package_name_is_absent(): check=False, ) assert result.returncode == 0, result.stdout + result.stderr + + +def test_wildcards_for_the_old_wheel_name_are_caught_but_markdown_bold_is_not(): + """`dist/*embed*.whl` slipped through the first rename (#42); `**embedding**` is ordinary prose.""" + import importlib.util + + spec = importlib.util.spec_from_file_location("check_no_embedded_name", CHECK) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + for bad in ("uv pip install dist/*embed*.whl", 'glob("*embedded*.whl")'): + assert mod.OLD_NAME.search(bad), bad + for ok in ( + "**embedding** models", + "embedded multi-model database", + "dist/arcadedb-*.whl", + ): + assert not mod.OLD_NAME.search(ok), ok