Skip to content

Commit a977bbb

Browse files
authored
install and test poetry (#9)
1 parent 695a8a1 commit a977bbb

File tree

5 files changed

+107
-26
lines changed

5 files changed

+107
-26
lines changed

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ FROM buildpack-deps:bookworm AS base
33
ENV CACHEBUST=2024-09-06
44
RUN apt update
55

6-
# Rust envvars
76
ENV RUSTUP_HOME=/usr/local/rustup \
87
CARGO_HOME=/usr/local/cargo \
98
RUST_VERSION=1.81.0 \
109
VIRTUAL_ENV=/var/local/python-venv
11-
ENV PATH=/usr/local/cargo/bin:$VIRTUAL_ENV/bin:$PATH
10+
ENV PATH=/usr/local/cargo/bin:$VIRTUAL_ENV/bin:/root/.local/bin:$PATH
1211

1312
# == node ======================
1413
FROM base AS node
@@ -33,6 +32,8 @@ RUN --mount=type=cache,target=/var/cache/apt,id=framework-runtime-python \
3332
python3-wheel \
3433
python3-dev \
3534
python3-venv \
35+
pipx \
36+
&& pipx install poetry \
3637
&& python3 -m venv $VIRTUAL_ENV
3738

3839
# == R ===========================

bin/test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ import { dirname } from "node:path";
55
import { run as runTests } from "node:test";
66
import { spec } from "node:test/reporters";
77

8+
import { parseArgs } from "node:util";
9+
const { values: { "only": argOnly } } = parseArgs({
10+
options: {
11+
"only": { type: "boolean" },
12+
}
13+
});
14+
815
export async function buildTestImage() {
916
console.log("building image...");
1017
let stdio = new StringStream();
@@ -34,7 +41,7 @@ const files = await glob(["tests/**/*.test.ts"], {
3441
});
3542

3643
await buildTestImage();
37-
runTests({ files, concurrency: true })
44+
runTests({ files, concurrency: true, only: argOnly })
3845
.on("test:fail", () => {
3946
process.exitCode = 1;
4047
})

tests/dataloader-languages.test.ts

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
11
import { test, describe } from "node:test";
2+
import os from "node:os";
23
import assert from "node:assert/strict";
34
import {
45
assertSemver,
56
binaryOnPathTest,
67
binaryVersionTest,
78
runCommandInContainer,
89
} from "./index.ts";
10+
import { cp, mkdtemp, rm } from "node:fs/promises";
11+
import { join } from "node:path";
912

1013
describe("Dataloader languages", () => {
11-
describe("Python", () => {
12-
binaryVersionTest({
13-
binary: "python3",
14-
semver: "^3.11",
15-
prefix: "Python",
16-
});
17-
18-
binaryOnPathTest({ binary: "pip" });
19-
20-
test(`A Python virtual environment is activated`, async () => {
21-
// should not throw
22-
await runCommandInContainer(["pip", "install", "requests"]);
23-
});
24-
});
25-
2614
describe("JavaScript", () => {
2715
binaryVersionTest({ binary: "node", semver: "^20.17" });
2816
binaryVersionTest({ binary: "npm", semver: "^10.5" });
@@ -57,6 +45,63 @@ describe("Dataloader languages", () => {
5745
});
5846
});
5947

48+
describe("Python", () => {
49+
binaryVersionTest({
50+
binary: "python3",
51+
semver: "^3.11",
52+
prefix: "Python",
53+
});
54+
55+
binaryVersionTest({
56+
binary: "pip",
57+
semver: "^23.0.1",
58+
extract: /^pip ([^ ]+) /,
59+
});
60+
binaryVersionTest({ binary: "pipx", semver: "^1.1.0" });
61+
binaryVersionTest({
62+
binary: "poetry",
63+
semver: "^1.8.3",
64+
prefix: "Poetry (version ",
65+
suffix: ")",
66+
});
67+
68+
test(`A Python virtual environment is activated`, async () => {
69+
// should not throw
70+
await runCommandInContainer(["pip", "install", "pip-install-test"]);
71+
});
72+
73+
test(`Poetry can install dependencies in the virtualenv`, async () => {
74+
let testDir = await mkdtemp(join(os.tmpdir(), "poetry-test-"));
75+
try {
76+
// This will install dependencies using Poetry, and then try to run `ls`
77+
// in the installed dependency's package. If the package is not
78+
// installed here, the `ls` command will exit non-zero and
79+
// `runCommandInContainer` will throw.
80+
await cp(
81+
"./tests/fixtures/poetry-test/pyproject.toml",
82+
`${testDir}/pyproject.toml`,
83+
);
84+
let res = await runCommandInContainer(
85+
[
86+
"sh",
87+
"-c",
88+
"poetry install; ls $(poetry env info --path)/lib/python3.11/site-packages/pip_install_test/__init__.py",
89+
],
90+
{
91+
workingDir: "/poetry-test",
92+
mounts: [{ host: testDir, container: "/poetry-test" }],
93+
},
94+
);
95+
} finally {
96+
try {
97+
await rm(testDir, { recursive: true });
98+
} catch {
99+
/* ok */
100+
}
101+
}
102+
});
103+
});
104+
60105
binaryVersionTest({
61106
binary: "Rscript",
62107
semver: "^4.4.1",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[tool.poetry]
2+
package-mode = false
3+
4+
[tool.poetry.dependencies]
5+
python = "^3.11"
6+
pip-install-test = "^0.5"

tests/index.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { test, before } from "node:test";
2+
import { resolve } from "node:path";
23
import assert from "node:assert";
34
import Dockerode from "dockerode";
45
import { Stream } from "node:stream";
@@ -13,6 +14,7 @@ export interface AssertBinaryVersionOptions {
1314
semver: string;
1415
extract?: RegExp;
1516
prefix?: string;
17+
suffix?: string;
1618
expectStderr?: RegExp;
1719
}
1820

@@ -22,12 +24,16 @@ export async function binaryVersionTest({
2224
semver,
2325
extract,
2426
prefix,
27+
suffix,
2528
expectStderr = /^$/,
2629
}: AssertBinaryVersionOptions) {
2730
await test(`${name} ${semver} is available`, async () => {
2831
const res = await runCommandInContainer([binary, "--version"]);
29-
assert.ok(res.stderr.match(expectStderr), `Expected stderr to match, got: ${res.stderr}`);
30-
assertSemver(res.stdout, semver, { extract, prefix });
32+
assert.ok(
33+
res.stderr.match(expectStderr),
34+
`Expected stderr to match, got: ${res.stderr}`,
35+
);
36+
assertSemver(res.stdout, semver, { extract, prefix, suffix });
3137
});
3238
}
3339

@@ -54,7 +60,7 @@ export function assertSemver(
5460
prefix,
5561
suffix,
5662
extract,
57-
}: { prefix?: string; suffix?: string; extract?: RegExp } = {}
63+
}: { prefix?: string; suffix?: string; extract?: RegExp } = {},
5864
) {
5965
actual = actual.trim();
6066
if (prefix && actual.startsWith(prefix)) actual = actual.slice(prefix.length);
@@ -70,7 +76,7 @@ export function assertSemver(
7076
actual = actual.trim();
7177
assert.ok(
7278
semverSatisfies(actual, expected),
73-
`Expected semver match for ${expected}, got ${actual}`
79+
`Expected semver match for ${expected}, got ${JSON.stringify(actual)}`,
7480
);
7581
}
7682

@@ -82,12 +88,26 @@ function ensureDocker() {
8288
before(ensureDocker);
8389

8490
export async function runCommandInContainer(
85-
command: string[]
91+
command: string[],
92+
{
93+
mounts = [],
94+
workingDir = "/",
95+
}: {
96+
mounts?: { host: string; container: string}[];
97+
workingDir?: string;
98+
} = {},
8699
): Promise<{ stdout: string; stderr: string }> {
87100
const docker = ensureDocker();
88101
const container = await docker.createContainer({
102+
WorkingDir: workingDir,
89103
Image: IMAGE_TAG,
90104
Cmd: command,
105+
HostConfig: {
106+
Binds: mounts.map(
107+
({ host, container }) =>
108+
`${resolve(host)}:${container}`,
109+
),
110+
},
91111
});
92112
const stdout = new StringStream();
93113
const stderr = new StringStream();
@@ -100,9 +120,11 @@ export async function runCommandInContainer(
100120
await container.start();
101121
const wait = (await container.wait()) as { StatusCode: number };
102122
if (wait.StatusCode !== 0) {
103-
throw new Error(`Command failed with status code ${wait.StatusCode}\n` +
104-
`stdout:\n${stdout.string}\n\n` +
105-
`stderr:\n${stderr.string}`);
123+
throw new Error(
124+
`Command failed with status code ${wait.StatusCode}\n` +
125+
`stdout:\n${stdout.string}\n\n` +
126+
`stderr:\n${stderr.string}`,
127+
);
106128
}
107129
return { stdout: stdout.string, stderr: stderr.string };
108130
}

0 commit comments

Comments
 (0)