@styx-api/core 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4461,17 +4461,19 @@ function licenseField(proj) {
4461
4461
  return `{ text = "${tomlStr(proj.license?.description ?? "unknown")}" }`;
4462
4462
  }
4463
4463
  /**
4464
- * Per-suite `pyproject.toml`. The flat layout (`python/<pkg>/bet.py`) makes the
4465
- * directory itself the importable package, so setuptools' `package-dir` maps the
4466
- * import name (`<pkg>`) onto the distribution's root directory. The styxdefs
4464
+ * Per-suite `pyproject.toml`. The suite source stays in a flat directory
4465
+ * (`python/<pkg>/bet.py`), but setuptools' `package-dir` maps that directory onto
4466
+ * the dotted import package `<project>.<pkg>`, so every suite nests under the
4467
+ * metapackage's `<project>/` namespace. That restores `from <project> import
4468
+ * <pkg>` while keeping each suite a separately-installable distribution (and
4469
+ * leaves no top-level `<pkg>` polluting the global namespace). With no project
4470
+ * name the import name is the bare `<pkg>` (top-level fallback). The styxdefs
4467
4471
  * floor is the only runtime dependency.
4468
4472
  *
4469
- * Precondition: `pkg.name` must be a valid Python identifier - the flat layout's
4470
- * relative imports (`from .bet import *`) already require this, and the CLI uses
4471
- * it verbatim as the directory name, so this stays consistent with that.
4473
+ * Precondition: `importName` is a valid (possibly dotted) Python package path -
4474
+ * the caller scrubs the project + package names into identifiers accordingly.
4472
4475
  */
4473
- function generateSubPyproject(proj, pkg) {
4474
- const importName = pkgDir(pkg);
4476
+ function generateSubPyproject(proj, pkg, importName) {
4475
4477
  const cb = new CodeBuilder(" ");
4476
4478
  cb.line("[project]");
4477
4479
  cb.line(`name = "${tomlStr(pyDistName(proj, pkg))}"`);
@@ -4508,11 +4510,24 @@ function generateRootInitPy() {
4508
4510
  }
4509
4511
  /**
4510
4512
  * Root `pyproject.toml`: a metapackage depending on `styxkit[all]` (the runner
4511
- * stack it re-exports) plus each per-suite distribution. `packages` lists only
4512
- * the metapackage's own module so setuptools ships the styxkit re-export without
4513
- * sweeping the sibling suite directories into this distribution.
4513
+ * stack it re-exports) plus each per-suite distribution, pinned to this exact
4514
+ * project version (`niwrap_fsl==1.2.3`). The suites all ride the project version
4515
+ * and ship in lockstep, so an exact pin keeps `pip install niwrap==X` consistent:
4516
+ * during the post-release index-propagation window (or any momentary registry
4517
+ * inconsistency) pip errors cleanly instead of silently grafting an older suite
4518
+ * whose layout no longer matches the metapackage. Mirrors the way the CLI pins
4519
+ * `@styx-api/core` exactly.
4520
+ *
4521
+ * `packages` lists only the metapackage's own module so setuptools ships the
4522
+ * styxkit re-export without sweeping the sibling suite directories into this
4523
+ * distribution. The module is the `<project>/` namespace package the suites nest
4524
+ * into, so installing the metapackage makes `<project>.use_docker()` reachable
4525
+ * alongside the suites' `from <project> import <pkg>`. `moduleDir` is the on-disk
4526
+ * source directory; it differs from the import `moduleName` only when a suite is
4527
+ * named after the project, in which case a `package-dir` remap keeps the import
4528
+ * name intact.
4514
4529
  */
4515
- function generateRootPyproject(proj, distNames, moduleName) {
4530
+ function generateRootPyproject(proj, distNames, moduleName, moduleDir) {
4516
4531
  const cb = new CodeBuilder(" ");
4517
4532
  cb.line("[project]");
4518
4533
  cb.line(`name = "${tomlStr(proj.name ?? "project")}"`);
@@ -4524,11 +4539,13 @@ function generateRootPyproject(proj, distNames, moduleName) {
4524
4539
  cb.line(`requires-python = "${REQUIRES_PYTHON}"`);
4525
4540
  cb.line("dependencies = [");
4526
4541
  for (const dep of PYTHON_RUNNER_DEPS) cb.line(` "${dep}",`);
4527
- for (const dist of distNames) cb.line(` "${tomlStr(dist)}",`);
4542
+ const suitePin = tomlStr(proj.version ?? "0.0.0");
4543
+ for (const dist of distNames) cb.line(` "${tomlStr(dist)}==${suitePin}",`);
4528
4544
  cb.line("]");
4529
4545
  cb.blank();
4530
4546
  cb.line("[tool.setuptools]");
4531
4547
  cb.line(`packages = ["${moduleName}"]`);
4548
+ if (moduleDir !== moduleName) cb.line(`package-dir = { "${moduleName}" = "${moduleDir}" }`);
4532
4549
  cb.blank();
4533
4550
  cb.line("[tool.setuptools.package-data]");
4534
4551
  cb.line(`"${moduleName}" = ["py.typed"]`);
@@ -5548,23 +5565,26 @@ var PythonBackend = class {
5548
5565
  const distNames = [];
5549
5566
  const pkgDirs = [];
5550
5567
  const warnings = [];
5568
+ const nsName = proj.name && proj.name.trim() ? pyScrubIdent(proj.name, PY_RESERVED) : void 0;
5551
5569
  for (const p of packages) {
5552
5570
  const pkg = p.meta ?? {};
5553
5571
  const dir = pkg.name ?? "package";
5554
5572
  pkgDirs.push(dir);
5555
5573
  distNames.push(pyDistName(proj, pkg));
5556
- files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg));
5574
+ const importPkg = nsName ? `${nsName}.${dir}` : dir;
5575
+ files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg, importPkg));
5557
5576
  files.set(`${dir}/README.md`, generateSubReadme(proj, pkg));
5558
5577
  }
5559
- let rootMod = pyScrubIdent(proj.name && proj.name.trim() ? proj.name : "project", PY_RESERVED);
5560
- if (pkgDirs.includes(rootMod)) {
5561
- const collided = rootMod;
5562
- while (pkgDirs.includes(rootMod)) rootMod += "_";
5563
- warnings.push({ message: `metapackage module "${collided}" collides with a suite directory; emitting as "${rootMod}" instead` });
5578
+ const metaImport = nsName ?? "project";
5579
+ let metaDir = metaImport;
5580
+ if (pkgDirs.includes(metaDir)) {
5581
+ const collided = metaDir;
5582
+ while (pkgDirs.includes(metaDir)) metaDir += "_";
5583
+ warnings.push({ message: `metapackage directory "${collided}" collides with a suite directory; emitting its sources in "${metaDir}" instead (import name stays "${metaImport}")` });
5564
5584
  }
5565
- files.set(`${rootMod}/__init__.py`, generateRootInitPy());
5566
- files.set(`${rootMod}/py.typed`, "");
5567
- files.set("pyproject.toml", generateRootPyproject(proj, distNames, rootMod));
5585
+ files.set(`${metaDir}/__init__.py`, generateRootInitPy());
5586
+ files.set(`${metaDir}/py.typed`, "");
5587
+ files.set("pyproject.toml", generateRootPyproject(proj, distNames, metaImport, metaDir));
5568
5588
  files.set("README.md", generateRootReadme(proj, distNames));
5569
5589
  files.set("requirements.txt", generateRequirementsTxt(pkgDirs));
5570
5590
  return {
package/dist/index.mjs CHANGED
@@ -4460,17 +4460,19 @@ function licenseField(proj) {
4460
4460
  return `{ text = "${tomlStr(proj.license?.description ?? "unknown")}" }`;
4461
4461
  }
4462
4462
  /**
4463
- * Per-suite `pyproject.toml`. The flat layout (`python/<pkg>/bet.py`) makes the
4464
- * directory itself the importable package, so setuptools' `package-dir` maps the
4465
- * import name (`<pkg>`) onto the distribution's root directory. The styxdefs
4463
+ * Per-suite `pyproject.toml`. The suite source stays in a flat directory
4464
+ * (`python/<pkg>/bet.py`), but setuptools' `package-dir` maps that directory onto
4465
+ * the dotted import package `<project>.<pkg>`, so every suite nests under the
4466
+ * metapackage's `<project>/` namespace. That restores `from <project> import
4467
+ * <pkg>` while keeping each suite a separately-installable distribution (and
4468
+ * leaves no top-level `<pkg>` polluting the global namespace). With no project
4469
+ * name the import name is the bare `<pkg>` (top-level fallback). The styxdefs
4466
4470
  * floor is the only runtime dependency.
4467
4471
  *
4468
- * Precondition: `pkg.name` must be a valid Python identifier - the flat layout's
4469
- * relative imports (`from .bet import *`) already require this, and the CLI uses
4470
- * it verbatim as the directory name, so this stays consistent with that.
4472
+ * Precondition: `importName` is a valid (possibly dotted) Python package path -
4473
+ * the caller scrubs the project + package names into identifiers accordingly.
4471
4474
  */
4472
- function generateSubPyproject(proj, pkg) {
4473
- const importName = pkgDir(pkg);
4475
+ function generateSubPyproject(proj, pkg, importName) {
4474
4476
  const cb = new CodeBuilder(" ");
4475
4477
  cb.line("[project]");
4476
4478
  cb.line(`name = "${tomlStr(pyDistName(proj, pkg))}"`);
@@ -4507,11 +4509,24 @@ function generateRootInitPy() {
4507
4509
  }
4508
4510
  /**
4509
4511
  * Root `pyproject.toml`: a metapackage depending on `styxkit[all]` (the runner
4510
- * stack it re-exports) plus each per-suite distribution. `packages` lists only
4511
- * the metapackage's own module so setuptools ships the styxkit re-export without
4512
- * sweeping the sibling suite directories into this distribution.
4512
+ * stack it re-exports) plus each per-suite distribution, pinned to this exact
4513
+ * project version (`niwrap_fsl==1.2.3`). The suites all ride the project version
4514
+ * and ship in lockstep, so an exact pin keeps `pip install niwrap==X` consistent:
4515
+ * during the post-release index-propagation window (or any momentary registry
4516
+ * inconsistency) pip errors cleanly instead of silently grafting an older suite
4517
+ * whose layout no longer matches the metapackage. Mirrors the way the CLI pins
4518
+ * `@styx-api/core` exactly.
4519
+ *
4520
+ * `packages` lists only the metapackage's own module so setuptools ships the
4521
+ * styxkit re-export without sweeping the sibling suite directories into this
4522
+ * distribution. The module is the `<project>/` namespace package the suites nest
4523
+ * into, so installing the metapackage makes `<project>.use_docker()` reachable
4524
+ * alongside the suites' `from <project> import <pkg>`. `moduleDir` is the on-disk
4525
+ * source directory; it differs from the import `moduleName` only when a suite is
4526
+ * named after the project, in which case a `package-dir` remap keeps the import
4527
+ * name intact.
4513
4528
  */
4514
- function generateRootPyproject(proj, distNames, moduleName) {
4529
+ function generateRootPyproject(proj, distNames, moduleName, moduleDir) {
4515
4530
  const cb = new CodeBuilder(" ");
4516
4531
  cb.line("[project]");
4517
4532
  cb.line(`name = "${tomlStr(proj.name ?? "project")}"`);
@@ -4523,11 +4538,13 @@ function generateRootPyproject(proj, distNames, moduleName) {
4523
4538
  cb.line(`requires-python = "${REQUIRES_PYTHON}"`);
4524
4539
  cb.line("dependencies = [");
4525
4540
  for (const dep of PYTHON_RUNNER_DEPS) cb.line(` "${dep}",`);
4526
- for (const dist of distNames) cb.line(` "${tomlStr(dist)}",`);
4541
+ const suitePin = tomlStr(proj.version ?? "0.0.0");
4542
+ for (const dist of distNames) cb.line(` "${tomlStr(dist)}==${suitePin}",`);
4527
4543
  cb.line("]");
4528
4544
  cb.blank();
4529
4545
  cb.line("[tool.setuptools]");
4530
4546
  cb.line(`packages = ["${moduleName}"]`);
4547
+ if (moduleDir !== moduleName) cb.line(`package-dir = { "${moduleName}" = "${moduleDir}" }`);
4531
4548
  cb.blank();
4532
4549
  cb.line("[tool.setuptools.package-data]");
4533
4550
  cb.line(`"${moduleName}" = ["py.typed"]`);
@@ -5547,23 +5564,26 @@ var PythonBackend = class {
5547
5564
  const distNames = [];
5548
5565
  const pkgDirs = [];
5549
5566
  const warnings = [];
5567
+ const nsName = proj.name && proj.name.trim() ? pyScrubIdent(proj.name, PY_RESERVED) : void 0;
5550
5568
  for (const p of packages) {
5551
5569
  const pkg = p.meta ?? {};
5552
5570
  const dir = pkg.name ?? "package";
5553
5571
  pkgDirs.push(dir);
5554
5572
  distNames.push(pyDistName(proj, pkg));
5555
- files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg));
5573
+ const importPkg = nsName ? `${nsName}.${dir}` : dir;
5574
+ files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg, importPkg));
5556
5575
  files.set(`${dir}/README.md`, generateSubReadme(proj, pkg));
5557
5576
  }
5558
- let rootMod = pyScrubIdent(proj.name && proj.name.trim() ? proj.name : "project", PY_RESERVED);
5559
- if (pkgDirs.includes(rootMod)) {
5560
- const collided = rootMod;
5561
- while (pkgDirs.includes(rootMod)) rootMod += "_";
5562
- warnings.push({ message: `metapackage module "${collided}" collides with a suite directory; emitting as "${rootMod}" instead` });
5577
+ const metaImport = nsName ?? "project";
5578
+ let metaDir = metaImport;
5579
+ if (pkgDirs.includes(metaDir)) {
5580
+ const collided = metaDir;
5581
+ while (pkgDirs.includes(metaDir)) metaDir += "_";
5582
+ warnings.push({ message: `metapackage directory "${collided}" collides with a suite directory; emitting its sources in "${metaDir}" instead (import name stays "${metaImport}")` });
5563
5583
  }
5564
- files.set(`${rootMod}/__init__.py`, generateRootInitPy());
5565
- files.set(`${rootMod}/py.typed`, "");
5566
- files.set("pyproject.toml", generateRootPyproject(proj, distNames, rootMod));
5584
+ files.set(`${metaDir}/__init__.py`, generateRootInitPy());
5585
+ files.set(`${metaDir}/py.typed`, "");
5586
+ files.set("pyproject.toml", generateRootPyproject(proj, distNames, metaImport, metaDir));
5567
5587
  files.set("README.md", generateRootReadme(proj, distNames));
5568
5588
  files.set("requirements.txt", generateRequirementsTxt(pkgDirs));
5569
5589
  return {