Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
38 changes: 38 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,43 @@ 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.get("reference")
if not source:
self._io.write_error_line(
f" - File script <c2>{name}</c2> is missing"
" a \"reference\" field"
)
continue
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
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_dir_ref_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "file-scripts-dir-ref-project"
version = "1.0.0"
description = "A project with a file script referencing a directory."
authors = ["Test Author <test@example.com>"]

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

[tool.poetry.scripts]
dir-script = { reference = "bin/some-directory", type = "file" }
console-entry = "file_scripts_dir_ref_project:main"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
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_missing_ref_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "file-scripts-missing-ref-project"
version = "1.0.0"
description = "A project with a file script referencing a non-existent file."
authors = ["Test Author <test@example.com>"]

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

[tool.poetry.scripts]
missing-script = { reference = "bin/does-not-exist.sh", type = "file" }
console-entry = "file_scripts_missing_ref_project:main"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
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_no_ref_field_project/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "file-scripts-no-ref-field-project"
version = "1.0.0"
description = "A project with a file script missing the reference field."
authors = ["Test Author <test@example.com>"]

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

[tool.poetry.scripts]
no-ref-script = { type = "file" }
console-entry = "file_scripts_no_ref_field_project:main"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
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"
120 changes: 120 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,123 @@
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}"
)


def test_builder_skips_missing_file_script(
fixture_dir: FixtureDirGetter,
tmp_path: Path,
) -> None:
from cleo.io.buffered_io import BufferedIO

poetry = Factory().create_poetry(fixture_dir("file_scripts_missing_ref_project"))
env_manager = EnvManager(poetry)
venv_path = tmp_path / "venv"
env_manager.build_venv(venv_path)
tmp_venv = VirtualEnv(venv_path)

io = BufferedIO()
builder = EditableBuilder(poetry, tmp_venv, io)
builder.build()

# The file script for the missing reference must not be created
script_path = tmp_venv._bin_dir.joinpath("missing-script")
assert not script_path.exists()

# The error message should be logged
error_output = io.fetch_error()
assert "missing-script" in error_output
assert "does not exist" in error_output


def test_builder_skips_directory_file_script(
fixture_dir: FixtureDirGetter,
tmp_path: Path,
) -> None:
from cleo.io.buffered_io import BufferedIO

poetry = Factory().create_poetry(fixture_dir("file_scripts_dir_ref_project"))
env_manager = EnvManager(poetry)
venv_path = tmp_path / "venv"
env_manager.build_venv(venv_path)
tmp_venv = VirtualEnv(venv_path)

io = BufferedIO()
builder = EditableBuilder(poetry, tmp_venv, io)
builder.build()

# The file script for the directory reference must not be created
script_path = tmp_venv._bin_dir.joinpath("dir-script")
assert not script_path.exists()

# The error message should be logged
error_output = io.fetch_error()
assert "dir-script" in error_output
assert "is not a file" in error_output

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View check run for this annotation

Cirrus CI / Tests / FreeBSD (Python 3.11) / pytest

tests/masonry/builders/test_editable_builder.py#L516

tests.masonry.builders.test_editable_builder.test_builder_skips_directory_file_script
Raw output
fixture_dir = <function fixture_dir.<locals>._fixture_dir at 0xe6992a367a0>
tmp_path = PosixPath('/tmp/pytest-of-root/pytest-0/popen-gw1/test_builder_skips_directory_f0')

    def test_builder_skips_directory_file_script(
        fixture_dir: FixtureDirGetter,
        tmp_path: Path,
    ) -> None:
        from cleo.io.buffered_io import BufferedIO
    
        poetry = Factory().create_poetry(fixture_dir("file_scripts_dir_ref_project"))
        env_manager = EnvManager(poetry)
        venv_path = tmp_path / "venv"
        env_manager.build_venv(venv_path)
        tmp_venv = VirtualEnv(venv_path)
    
        io = BufferedIO()
        builder = EditableBuilder(poetry, tmp_venv, io)
        builder.build()
    
        # The file script for the directory reference must not be created
        script_path = tmp_venv._bin_dir.joinpath("dir-script")
        assert not script_path.exists()
    
        # The error message should be logged
        error_output = io.fetch_error()
        assert "dir-script" in error_output
>       assert "is not a file" in error_output
E       AssertionError: assert 'is not a file' in '  - File script dir-script references bin/some-directory which does not exist\n'

/tmp/cirrus-ci-build/tests/masonry/builders/test_editable_builder.py:516: AssertionError

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.11) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.13) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.11) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.10) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.14) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.10) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.13) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.12) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.12) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'

Check failure on line 516 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.14) / pytest

test_builder_skips_directory_file_script AssertionError: assert 'is not a file' in ' - File script dir-script references bin/some-directory which does not exist\n'


def test_builder_skips_file_script_missing_reference_field(
fixture_dir: FixtureDirGetter,
tmp_path: Path,
) -> None:
from cleo.io.buffered_io import BufferedIO

poetry = Factory().create_poetry(

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View check run for this annotation

Cirrus CI / Tests / FreeBSD (Python 3.11) / pytest

tests/masonry/builders/test_editable_builder.py#L525

tests.masonry.builders.test_editable_builder.test_builder_skips_file_script_missing_reference_field
Raw output
fixture_dir = <function fixture_dir.<locals>._fixture_dir at 0x38b47ec463e0>
tmp_path = PosixPath('/tmp/pytest-of-root/pytest-0/popen-gw0/test_builder_skips_file_script0')

    def test_builder_skips_file_script_missing_reference_field(
        fixture_dir: FixtureDirGetter,
        tmp_path: Path,
    ) -> None:
        from cleo.io.buffered_io import BufferedIO
    
>       poetry = Factory().create_poetry(
            fixture_dir("file_scripts_no_ref_field_project")
        )

/tmp/cirrus-ci-build/tests/masonry/builders/test_editable_builder.py:525: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/tmp/cirrus-ci-build/src/poetry/factory.py:80: in create_poetry
    base_poetry = super().create_poetry(cwd=cwd, with_groups=with_groups)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <poetry.factory.Factory object at 0x38b47de84d10>
cwd = PosixPath('/tmp/cirrus-ci-build/tests/fixtures/file_scripts_no_ref_field_project')
with_groups = True

    def create_poetry(
        self, cwd: Path | None = None, with_groups: bool = True
    ) -> Poetry:
        from poetry.core.poetry import Poetry
        from poetry.core.pyproject.toml import PyProjectTOML
    
        poetry_file = self.locate(cwd)
        pyproject = PyProjectTOML(path=poetry_file)
    
        # Checking validity
        check_result = self.validate(pyproject.data)
        if check_result["errors"]:
            message = ""
            for error in check_result["errors"]:
                message += f"  - {error}\n"
    
>           raise RuntimeError("The Poetry configuration is invalid:\n" + message)
E           RuntimeError: The Poetry configuration is invalid:
E             - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

/.cache/pypoetry/virtualenvs/poetry-XZqP7kBn-py3.11/lib/python3.11/site-packages/poetry/core/factory.py:58: RuntimeError

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.11) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.13) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.11) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.10) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.14) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.10) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.13) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / macOS aarch64 (Python 3.12) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.12) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)

Check failure on line 525 in tests/masonry/builders/test_editable_builder.py

View workflow job for this annotation

GitHub Actions / Ubuntu (Python 3.14) / pytest

test_builder_skips_file_script_missing_reference_field RuntimeError: The Poetry configuration is invalid: - tool.poetry.scripts.no-ref-script must be valid exactly by one definition (0 matches found)
fixture_dir("file_scripts_no_ref_field_project")
)
env_manager = EnvManager(poetry)
venv_path = tmp_path / "venv"
env_manager.build_venv(venv_path)
tmp_venv = VirtualEnv(venv_path)

io = BufferedIO()
builder = EditableBuilder(poetry, tmp_venv, io)
builder.build()

# The file script with missing reference field must not be created
script_path = tmp_venv._bin_dir.joinpath("no-ref-script")
assert not script_path.exists()

# The error message should be logged
error_output = io.fetch_error()
assert "no-ref-script" in error_output
assert "reference" in error_output
Loading