@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 +30 -19
- package/dist/index.mjs +30 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/backend/python/packaging.ts +24 -9
- package/src/backend/python/python.ts +26 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@styx-api/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Styx compiler core: parses CLI tool descriptors (e.g. Boutiques), optimizes an IR, solves typed parameter bindings, and generates type-safe wrappers (TypeScript, Python, JSON Schema). Part of the Styx/NiWrap ecosystem.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"styx",
|
|
@@ -72,17 +72,23 @@ function licenseField(proj: ProjectMeta): string {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Per-suite `pyproject.toml`. The
|
|
76
|
-
*
|
|
77
|
-
* import
|
|
75
|
+
* Per-suite `pyproject.toml`. The suite source stays in a flat directory
|
|
76
|
+
* (`python/<pkg>/bet.py`), but setuptools' `package-dir` maps that directory onto
|
|
77
|
+
* the dotted import package `<project>.<pkg>`, so every suite nests under the
|
|
78
|
+
* metapackage's `<project>/` namespace. That restores `from <project> import
|
|
79
|
+
* <pkg>` while keeping each suite a separately-installable distribution (and
|
|
80
|
+
* leaves no top-level `<pkg>` polluting the global namespace). With no project
|
|
81
|
+
* name the import name is the bare `<pkg>` (top-level fallback). The styxdefs
|
|
78
82
|
* floor is the only runtime dependency.
|
|
79
83
|
*
|
|
80
|
-
* Precondition: `
|
|
81
|
-
*
|
|
82
|
-
* it verbatim as the directory name, so this stays consistent with that.
|
|
84
|
+
* Precondition: `importName` is a valid (possibly dotted) Python package path -
|
|
85
|
+
* the caller scrubs the project + package names into identifiers accordingly.
|
|
83
86
|
*/
|
|
84
|
-
export function generateSubPyproject(
|
|
85
|
-
|
|
87
|
+
export function generateSubPyproject(
|
|
88
|
+
proj: ProjectMeta,
|
|
89
|
+
pkg: PackageMeta,
|
|
90
|
+
importName: string,
|
|
91
|
+
): string {
|
|
86
92
|
const cb = new CodeBuilder(" ");
|
|
87
93
|
cb.line("[project]");
|
|
88
94
|
cb.line(`name = "${tomlStr(pyDistName(proj, pkg))}"`);
|
|
@@ -135,12 +141,18 @@ export function generateRootInitPy(): string {
|
|
|
135
141
|
* Root `pyproject.toml`: a metapackage depending on `styxkit[all]` (the runner
|
|
136
142
|
* stack it re-exports) plus each per-suite distribution. `packages` lists only
|
|
137
143
|
* the metapackage's own module so setuptools ships the styxkit re-export without
|
|
138
|
-
* sweeping the sibling suite directories into this distribution.
|
|
144
|
+
* sweeping the sibling suite directories into this distribution. The module is
|
|
145
|
+
* the `<project>/` namespace package the suites nest into, so installing the
|
|
146
|
+
* metapackage makes `<project>.use_docker()` reachable alongside the suites'
|
|
147
|
+
* `from <project> import <pkg>`. `moduleDir` is the on-disk source directory; it
|
|
148
|
+
* differs from the import `moduleName` only when a suite is named after the
|
|
149
|
+
* project, in which case a `package-dir` remap keeps the import name intact.
|
|
139
150
|
*/
|
|
140
151
|
export function generateRootPyproject(
|
|
141
152
|
proj: ProjectMeta,
|
|
142
153
|
distNames: string[],
|
|
143
154
|
moduleName: string,
|
|
155
|
+
moduleDir: string,
|
|
144
156
|
): string {
|
|
145
157
|
const cb = new CodeBuilder(" ");
|
|
146
158
|
cb.line("[project]");
|
|
@@ -158,6 +170,9 @@ export function generateRootPyproject(
|
|
|
158
170
|
cb.blank();
|
|
159
171
|
cb.line("[tool.setuptools]");
|
|
160
172
|
cb.line(`packages = ["${moduleName}"]`);
|
|
173
|
+
if (moduleDir !== moduleName) {
|
|
174
|
+
cb.line(`package-dir = { "${moduleName}" = "${moduleDir}" }`);
|
|
175
|
+
}
|
|
161
176
|
cb.blank();
|
|
162
177
|
cb.line("[tool.setuptools.package-data]");
|
|
163
178
|
cb.line(`"${moduleName}" = ["py.typed"]`);
|
|
@@ -547,6 +547,14 @@ export class PythonBackend implements Backend {
|
|
|
547
547
|
const pkgDirs: string[] = [];
|
|
548
548
|
const warnings: EmitWarning[] = [];
|
|
549
549
|
|
|
550
|
+
// The metapackage's importable module doubles as the namespace every suite
|
|
551
|
+
// nests under (`<project>.<suite>`), so `from <project> import <suite>` and
|
|
552
|
+
// `<project>.use_docker()` both resolve from one shared `<project>/` package.
|
|
553
|
+
// Scrub the project name to a valid, non-keyword identifier; with no project
|
|
554
|
+
// name there is no namespace and suites stay top-level (matching the bare
|
|
555
|
+
// distribution-name fallback in `pyDistName`).
|
|
556
|
+
const nsName = proj.name && proj.name.trim() ? pyScrubIdent(proj.name, PY_RESERVED) : undefined;
|
|
557
|
+
|
|
550
558
|
for (const p of packages) {
|
|
551
559
|
const pkg = p.meta ?? {};
|
|
552
560
|
// Mirror the CLI's `pkgDir` fallback so a nameless package's source dir
|
|
@@ -554,27 +562,31 @@ export class PythonBackend implements Backend {
|
|
|
554
562
|
const dir = pkg.name ?? "package";
|
|
555
563
|
pkgDirs.push(dir);
|
|
556
564
|
distNames.push(pyDistName(proj, pkg));
|
|
557
|
-
|
|
565
|
+
// Import package: `<project>.<suite>` so it shares the metapackage's
|
|
566
|
+
// namespace; setuptools maps the flat suite dir onto this dotted path.
|
|
567
|
+
const importPkg = nsName ? `${nsName}.${dir}` : dir;
|
|
568
|
+
files.set(`${dir}/pyproject.toml`, generateSubPyproject(proj, pkg, importPkg));
|
|
558
569
|
files.set(`${dir}/README.md`, generateSubReadme(proj, pkg));
|
|
559
570
|
}
|
|
560
571
|
|
|
561
|
-
// The metapackage ships
|
|
562
|
-
//
|
|
563
|
-
// the
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
572
|
+
// The metapackage ships `<project>/__init__.py` (a thin styxkit re-export so
|
|
573
|
+
// `<project>.use_docker()` works, PEP 561 typed) into the same `<project>/`
|
|
574
|
+
// package the suites nest under. Its on-disk source dir must dodge a suite
|
|
575
|
+
// named after the project (both would write `<project>/`); the import name
|
|
576
|
+
// stays `<project>` via a `package-dir` remap so the namespace is preserved.
|
|
577
|
+
const metaImport = nsName ?? "project";
|
|
578
|
+
let metaDir = metaImport;
|
|
579
|
+
if (pkgDirs.includes(metaDir)) {
|
|
580
|
+
const collided = metaDir;
|
|
581
|
+
while (pkgDirs.includes(metaDir)) metaDir += "_";
|
|
570
582
|
warnings.push({
|
|
571
|
-
message: `metapackage
|
|
583
|
+
message: `metapackage directory "${collided}" collides with a suite directory; emitting its sources in "${metaDir}" instead (import name stays "${metaImport}")`,
|
|
572
584
|
});
|
|
573
585
|
}
|
|
574
|
-
files.set(`${
|
|
575
|
-
files.set(`${
|
|
586
|
+
files.set(`${metaDir}/__init__.py`, generateRootInitPy());
|
|
587
|
+
files.set(`${metaDir}/py.typed`, "");
|
|
576
588
|
|
|
577
|
-
files.set("pyproject.toml", generateRootPyproject(proj, distNames,
|
|
589
|
+
files.set("pyproject.toml", generateRootPyproject(proj, distNames, metaImport, metaDir));
|
|
578
590
|
files.set("README.md", generateRootReadme(proj, distNames));
|
|
579
591
|
files.set("requirements.txt", generateRequirementsTxt(pkgDirs));
|
|
580
592
|
|