Skip to content

Commit 5d9a99f

Browse files
authored
fix(nx-python): handle "from" for packages in pyproject.toml during the build process (#184)
* fix(nx-python): handle "from" for packages Packages in the pyproject.yml can have a "from" property, which is used to accomodate the "src" layout instead of the "flat" or "adhoc" layout. This change attempts to add prepend any specified "from" directory to the package name. * test(nx-python): cover the new branch statement for handling packages with "from"
1 parent d7347a7 commit 5d9a99f

File tree

3 files changed

+128
-1
lines changed

3 files changed

+128
-1
lines changed

packages/nx-python/src/executors/build/executor.spec.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,132 @@ describe('Build Executor', () => {
882882
expect(output.success).toBe(true);
883883
});
884884

885+
it('should build python project with local dependencies that specify a "from" directory', async () => {
886+
fsMock({
887+
'apps/app/.venv/pyvenv.cfg': 'fake',
888+
'apps/app/app/index.py': 'print("Hello from app")',
889+
'apps/app/poetry.lock': dedent`
890+
[[package]]
891+
name = "dep1"
892+
version = "1.0.0"
893+
description = "Dep1"
894+
category = "main"
895+
optional = false
896+
python-versions = "^3.8"
897+
develop = false
898+
899+
[package.source]
900+
type = "directory"
901+
url = "../../libs/dep1"
902+
`,
903+
'apps/app/pyproject.toml': dedent`
904+
[tool.poetry]
905+
name = "app"
906+
version = "1.0.0"
907+
[[tool.poetry.packages]]
908+
include = "app"
909+
910+
[tool.poetry.dependencies]
911+
python = "^3.8"
912+
dep1 = { path = "../../libs/dep1" }
913+
`,
914+
915+
'libs/dep1/src/dep1/index.py': 'print("Hello from dep1")',
916+
'libs/dep1/pyproject.toml': dedent`
917+
[tool.poetry]
918+
name = "dep1"
919+
version = "1.0.0"
920+
921+
[[tool.poetry.packages]]
922+
include = "dep1"
923+
from = "src"
924+
925+
[tool.poetry.dependencies]
926+
python = "^3.8"
927+
`,
928+
});
929+
930+
spawnSyncMock.mockImplementation((_, args, opts) => {
931+
if (args[0] == 'build') {
932+
spawnBuildMockImpl(opts);
933+
} else if (args[0] == 'export' && opts.cwd === 'apps/app') {
934+
writeFileSync(
935+
join(buildPath, 'requirements.txt'),
936+
dedent`
937+
dep1 @ file://${process.cwd()}/libs/dep1
938+
`
939+
);
940+
}
941+
return { status: 0 };
942+
});
943+
944+
const options: BuildExecutorSchema = {
945+
ignorePaths: ['.venv', '.tox', 'tests/'],
946+
silent: false,
947+
outputPath: 'dist/apps/app',
948+
keepBuildFolder: true,
949+
devDependencies: false,
950+
lockedVersions: true,
951+
bundleLocalDependencies: true,
952+
};
953+
954+
const output = await executor(options, {
955+
cwd: '',
956+
root: '.',
957+
isVerbose: false,
958+
projectName: 'app',
959+
workspace: {
960+
version: 2,
961+
npmScope: 'nxlv',
962+
projects: {
963+
app: {
964+
root: 'apps/app',
965+
targets: {},
966+
},
967+
dep1: {
968+
root: 'libs/dep1',
969+
targets: {},
970+
},
971+
},
972+
},
973+
});
974+
975+
expect(checkPoetryExecutableMock).toHaveBeenCalled();
976+
expect(activateVenvMock).toHaveBeenCalledWith('.');
977+
expect(existsSync(buildPath)).toBeTruthy();
978+
expect(existsSync(`${buildPath}/app`)).toBeTruthy();
979+
expect(existsSync(`${buildPath}/dep1`)).toBeTruthy();
980+
expect(existsSync(`${buildPath}/dist/app.fake`)).toBeTruthy();
981+
expect(spawnSyncMock).toHaveBeenCalledWith('poetry', ['build'], {
982+
cwd: buildPath,
983+
shell: false,
984+
stdio: 'inherit',
985+
});
986+
987+
const projectTomlData = parse(
988+
readFileSync(`${buildPath}/pyproject.toml`).toString('utf-8')
989+
) as PyprojectToml;
990+
991+
expect(projectTomlData.tool.poetry.packages).toStrictEqual([
992+
{
993+
include: 'app',
994+
},
995+
{
996+
include: 'dep1',
997+
},
998+
]);
999+
1000+
expect(projectTomlData.tool.poetry.dependencies).toStrictEqual({
1001+
python: '^3.8',
1002+
});
1003+
1004+
expect(projectTomlData.tool.poetry.group.dev.dependencies).toStrictEqual(
1005+
{}
1006+
);
1007+
1008+
expect(output.success).toBe(true);
1009+
});
1010+
8851011
it('should build python project with local dependencies and extras', async () => {
8861012
fsMock({
8871013
'apps/app/.venv/pyvenv.cfg': 'fake',

packages/nx-python/src/executors/build/resolvers/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function includeDependencyPackage(
99
buildTomlData: PyprojectToml
1010
) {
1111
for (const pkg of tomlData.tool.poetry.packages) {
12-
const pkgFolder = join(root, pkg.include);
12+
const pkgFolder = join(root, pkg.from ?? '', pkg.include);
1313
const buildPackageFolder = join(buildFolderPath, pkg.include);
1414

1515
copySync(pkgFolder, buildPackageFolder);

packages/nx-python/src/graph/dependency-graph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export type PyprojectToml = {
4848
version: string;
4949
packages?: Array<{
5050
include: string;
51+
from?: string;
5152
}>;
5253
dependencies?: PyprojectTomlDependencies;
5354
group?: {

0 commit comments

Comments
 (0)