Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/poetry/masonry/builders/editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import hashlib
import json
import os
import shutil

from base64 import urlsafe_b64encode
from pathlib import Path
Expand Down Expand Up @@ -211,6 +212,37 @@ def _add_scripts(self) -> list[Path]:

added.append(cmd_script)

# Handle file scripts (type = "file" in [tool.poetry.scripts])
for name, specification in self._poetry.local_config.get(
"scripts", {}
).items():
if isinstance(specification, dict) and specification.get("type") == "file":
source = specification["reference"]
source_path = self._path / source

if not source_path.exists():
self._io.write_error_line(
f" - File script <c2>{name}</c2> references"
f" <b>{source}</b> which does not exist"
)
continue

if not source_path.is_file():
self._io.write_error_line(
f" - File script <c2>{name}</c2> references"
f" <b>{source}</b> which is not a file"
)
continue

target = scripts_path.joinpath(name)
self._debug(
f" - Adding the <c2>{name}</c2> file script"
f" to <b>{scripts_path}</b>"
)
shutil.copy2(source_path, target)
target.chmod(0o755)
added.append(target)

return added

def _add_dist_info(self, added_files: list[Path]) -> None:
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/file_scripts_project/bin/my-script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
echo "Hello from file script"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def main():
print("Hello from console entry point")
16 changes: 16 additions & 0 deletions tests/fixtures/file_scripts_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "file-scripts-project"
version = "1.0.0"
description = "A project with file scripts."
authors = ["Test Author <test@example.com>"]

[tool.poetry.dependencies]
python = "^3.7"

[tool.poetry.scripts]
my-script = { reference = "bin/my-script.sh", type = "file" }
console-entry = "file_scripts_project:main"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
40 changes: 40 additions & 0 deletions tests/masonry/builders/test_editable_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,43 @@ def test_builder_catches_bad_scripts_too_many_colon(
assert "foo::bar" in msg
# and some hint about what is wrong
assert "Too many" in msg


@pytest.fixture()
def file_scripts_poetry(fixture_dir: FixtureDirGetter) -> Poetry:
poetry = Factory().create_poetry(fixture_dir("file_scripts_project"))
return poetry


def test_builder_installs_file_scripts(
file_scripts_poetry: Poetry,
tmp_path: Path,
) -> None:
env_manager = EnvManager(file_scripts_poetry)
venv_path = tmp_path / "venv"
env_manager.build_venv(venv_path)
tmp_venv = VirtualEnv(venv_path)

builder = EditableBuilder(file_scripts_poetry, tmp_venv, NullIO())
builder.build()

# The file script should be copied to the venv bin directory
script_path = tmp_venv._bin_dir.joinpath("my-script")
assert script_path.exists(), (
f"File script 'my-script' was not copied to {tmp_venv._bin_dir}"
)

# Check script content matches the source
source_content = (
file_scripts_poetry.file.path.parent / "bin" / "my-script.sh"
).read_text(encoding="utf-8")
assert script_path.read_text(encoding="utf-8") == source_content

# Check the file is executable
assert os.access(script_path, os.X_OK)

# The console entry point should also be installed
console_script = tmp_venv._bin_dir.joinpath("console-entry")
assert console_script.exists(), (
f"Console script 'console-entry' was not installed to {tmp_venv._bin_dir}"
)