@styx-api/core 0.5.0 → 0.5.1

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))}"`);
@@ -4510,9 +4512,14 @@ function generateRootInitPy() {
4510
4512
  * Root `pyproject.toml`: a metapackage depending on `styxkit[all]` (the runner
4511
4513
  * stack it re-exports) plus each per-suite distribution. `packages` lists only
4512
4514
  * the metapackage's own module so setuptools ships the styxkit re-export without
4513
- * sweeping the sibling suite directories into this distribution.
4515
+ * sweeping the sibling suite directories into this distribution. The module is
4516
+ * the `<project>/` namespace package the suites nest into, so installing the
4517
+ * metapackage makes `<project>.use_docker()` reachable alongside the suites'
4518
+ * `from <project> import <pkg>`. `moduleDir` is the on-disk source directory; it
4519
+ * differs from the import `moduleName` only when a suite is named after the
4520
+ * project, in which case a `package-dir` remap keeps the import name intact.
4514
4521
  */
4515
- function generateRootPyproject(proj, distNames, moduleName) {
4522
+ function generateRootPyproject(proj, distNames, moduleName, moduleDir) {
4516
4523
  const cb = new CodeBuilder(" ");
4517
4524
  cb.line("[project]");
4518
4525
  cb.line(`name = "${tomlStr(proj.name ?? "project")}"`);
@@ -4529,6 +4536,7 @@ function generateRootPyproject(proj, distNames, moduleName) {
4529
4536
  cb.blank();
4530
4537
  cb.line("[tool.setuptools]");
4531
4538
  cb.line(`packages = ["${moduleName}"]`);
4539
+ if (moduleDir !== moduleName) cb.line(`package-dir = { "${moduleName}" = "${moduleDir}" }`);
4532
4540
  cb.blank();
4533
4541
  cb.line("[tool.setuptools.package-data]");
4534
4542
  cb.line(`"${moduleName}" = ["py.typed"]`);
@@ -5548,23 +5556,26 @@ var PythonBackend = class {
5548
5556
  const distNames = [];
5549
5557
  const pkgDirs = [];
5550
5558
  const warnings = [];
5559
+ const nsName = proj.name && proj.name.trim() ? pyScrubIdent(proj.name, PY_RESERVED) : void 0;
5551
5560
  for (const p of packages) {
5552
5561
  const pkg = p.meta ?? {};
5553
5562
  const dir = pkg.name ?? "package";
5554
5563
  pkgDirs.push(dir);
5555
5564
  distNames.push(pyDistName(proj, pkg));
5556
- files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg));
5565
+ const importPkg = nsName ? `${nsName}.${dir}` : dir;
5566
+ files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg, importPkg));
5557
5567
  files.set(`${dir}/README.md`, generateSubReadme(proj, pkg));
5558
5568
  }
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` });
5569
+ const metaImport = nsName ?? "project";
5570
+ let metaDir = metaImport;
5571
+ if (pkgDirs.includes(metaDir)) {
5572
+ const collided = metaDir;
5573
+ while (pkgDirs.includes(metaDir)) metaDir += "_";
5574
+ warnings.push({ message: `metapackage directory "${collided}" collides with a suite directory; emitting its sources in "${metaDir}" instead (import name stays "${metaImport}")` });
5564
5575
  }
5565
- files.set(`${rootMod}/__init__.py`, generateRootInitPy());
5566
- files.set(`${rootMod}/py.typed`, "");
5567
- files.set("pyproject.toml", generateRootPyproject(proj, distNames, rootMod));
5576
+ files.set(`${metaDir}/__init__.py`, generateRootInitPy());
5577
+ files.set(`${metaDir}/py.typed`, "");
5578
+ files.set("pyproject.toml", generateRootPyproject(proj, distNames, metaImport, metaDir));
5568
5579
  files.set("README.md", generateRootReadme(proj, distNames));
5569
5580
  files.set("requirements.txt", generateRequirementsTxt(pkgDirs));
5570
5581
  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))}"`);
@@ -4509,9 +4511,14 @@ function generateRootInitPy() {
4509
4511
  * Root `pyproject.toml`: a metapackage depending on `styxkit[all]` (the runner
4510
4512
  * stack it re-exports) plus each per-suite distribution. `packages` lists only
4511
4513
  * the metapackage's own module so setuptools ships the styxkit re-export without
4512
- * sweeping the sibling suite directories into this distribution.
4514
+ * sweeping the sibling suite directories into this distribution. The module is
4515
+ * the `<project>/` namespace package the suites nest into, so installing the
4516
+ * metapackage makes `<project>.use_docker()` reachable alongside the suites'
4517
+ * `from <project> import <pkg>`. `moduleDir` is the on-disk source directory; it
4518
+ * differs from the import `moduleName` only when a suite is named after the
4519
+ * project, in which case a `package-dir` remap keeps the import name intact.
4513
4520
  */
4514
- function generateRootPyproject(proj, distNames, moduleName) {
4521
+ function generateRootPyproject(proj, distNames, moduleName, moduleDir) {
4515
4522
  const cb = new CodeBuilder(" ");
4516
4523
  cb.line("[project]");
4517
4524
  cb.line(`name = "${tomlStr(proj.name ?? "project")}"`);
@@ -4528,6 +4535,7 @@ function generateRootPyproject(proj, distNames, moduleName) {
4528
4535
  cb.blank();
4529
4536
  cb.line("[tool.setuptools]");
4530
4537
  cb.line(`packages = ["${moduleName}"]`);
4538
+ if (moduleDir !== moduleName) cb.line(`package-dir = { "${moduleName}" = "${moduleDir}" }`);
4531
4539
  cb.blank();
4532
4540
  cb.line("[tool.setuptools.package-data]");
4533
4541
  cb.line(`"${moduleName}" = ["py.typed"]`);
@@ -5547,23 +5555,26 @@ var PythonBackend = class {
5547
5555
  const distNames = [];
5548
5556
  const pkgDirs = [];
5549
5557
  const warnings = [];
5558
+ const nsName = proj.name && proj.name.trim() ? pyScrubIdent(proj.name, PY_RESERVED) : void 0;
5550
5559
  for (const p of packages) {
5551
5560
  const pkg = p.meta ?? {};
5552
5561
  const dir = pkg.name ?? "package";
5553
5562
  pkgDirs.push(dir);
5554
5563
  distNames.push(pyDistName(proj, pkg));
5555
- files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg));
5564
+ const importPkg = nsName ? `${nsName}.${dir}` : dir;
5565
+ files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg, importPkg));
5556
5566
  files.set(`${dir}/README.md`, generateSubReadme(proj, pkg));
5557
5567
  }
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` });
5568
+ const metaImport = nsName ?? "project";
5569
+ let metaDir = metaImport;
5570
+ if (pkgDirs.includes(metaDir)) {
5571
+ const collided = metaDir;
5572
+ while (pkgDirs.includes(metaDir)) metaDir += "_";
5573
+ warnings.push({ message: `metapackage directory "${collided}" collides with a suite directory; emitting its sources in "${metaDir}" instead (import name stays "${metaImport}")` });
5563
5574
  }
5564
- files.set(`${rootMod}/__init__.py`, generateRootInitPy());
5565
- files.set(`${rootMod}/py.typed`, "");
5566
- files.set("pyproject.toml", generateRootPyproject(proj, distNames, rootMod));
5575
+ files.set(`${metaDir}/__init__.py`, generateRootInitPy());
5576
+ files.set(`${metaDir}/py.typed`, "");
5577
+ files.set("pyproject.toml", generateRootPyproject(proj, distNames, metaImport, metaDir));
5567
5578
  files.set("README.md", generateRootReadme(proj, distNames));
5568
5579
  files.set("requirements.txt", generateRequirementsTxt(pkgDirs));
5569
5580
  return {