pythonpyproject.tomluv

uv run fails with "failed to spawn"


I have a module with a pretty regular layout, a main function in a sacrypt directory inside a __init__.py file. Then, pyproject.toml looks like this;

[project]
name = "sacrypt"
version = "0.1.0"
description = "Cryptanalysis examples for SAC"
readme = "README.md"
requires-python = ">=3.13"

[project.scripts]
sacrypt = "sacrypt:main"

uv run sacrypt, however, fails with:

error: Failed to spawn: `sacrypt`
  Caused by: No such file or directory (os error 2)

I have tried changing it to a different name, as well as moving sacrypt to src/sacrypt to no avail. This is by the book following tutorials as well as the documentation. Any idea what is going on here or where I should poke to find the error? Running uv with any amount of -v is not giving any more information. Also, this is uv version 0.6.9.

Update: As instructed in the comment, I have added a build system this way

[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

And then issued uv build. Still an error, this time different:

Traceback (most recent call last):
  File "/home/jmerelo/Tutoriales/cemed-green-software/code/Python/.venv/bin/sacrypt", line 4, in <module>
    from sacrypt import main
ModuleNotFoundError: No module named 'sacrypt'

Running it from the command line, or running tests, works fine.


Solution

  • It is actually easy.

    Quick Answer

    So we have to add first the [build-system] block for building - so give enough info for build-ing - which is necessary before a script can be called, and under [project.scripts] we have to use the syntax: <mycommandname> = "<module_or_file>:<invoked/entry_function_name>". And call this defined command by: uv run <mycommandname>.

    [build-system]
    requires = ["setuptools>=42", "wheel"]
    build-backend = "setuptools.build_meta"
    
    [project.scripts]
    sacrypt = "main:main"
    

    Detailed Answer

    Starting with:

    # start a project:
    uv init sacrypt
    

    Which generates the project structure:

    sacrypt
    ├── README.md
    ├── main.py
    └── pyproject.toml
    

    We modify nano pyproject.toml:

    cd sacrypt
    nano pyproject.toml
    
    [project]
    name = "sacrypt"
    version = "0.1.0"
    description = "Add your description here"
    readme = "README.md"
    requires-python = ">=3.12"
    dependencies = []
    

    to:

    [project]
    name = "sacrypt"
    version = "0.1.0"
    description = "Cryptanalysis examples for SAC"
    readme = "README.md"
    requires-python = ">=3.13"
    dependencies = []
    

    Then you added:

    [project.scripts]
    sacrypt = "sacrypt:main"
    

    Which could not be run by:

    $ uv run sacrypt
    

    but rather invoking:

    error: Failed to spawn: `sacrypt`
      Caused by: No such file or directory (os error 2)
    

    Somehow it couldn't build the package.

    It seems one has to add the build information, like mentioned in this answer: https://stackoverflow.com/a/73066937/9690090 :

    [build-system]
    requires = ["setuptools>=42", "wheel"]
    build-backend = "setuptools.build_meta"
    

    This info for building seems to be necessary.

    The other point is - currently the only source file is main.py. The command must be of the form `commandname = "file_module_name:entry_function_name"

    [project.scripts]
    sacrypt = "sacrypt:main"
    

    In our case, we have main.py and a function def main(): print("Hello from sacrypt!"). So the correct version is:

    [project.scripts]
    sacrypt = "main:main"
    

    So the entire pyproject.toml is now:

    [project]
    name = "sacrypt"
    version = "0.1.0"
    description = "Add your description here"
    readme = "README.md"
    requires-python = ">=3.12"
    dependencies = []
    
    [build-system]
    requires = ["setuptools>=42", "wheel"]
    build-backend = "setuptools.build_meta"
    
    [project.scripts]
    sacrypt = "main:main"
    

    And then you can from inside the sacrypt project folder run:

    uv run sacrypt
    

    And then you see:

    % uv run sacrypt
    Using CPython 3.12.9
    Creating virtual environment at: .venv
          Built sacrypt @ file:///Users/josephus/projects/python/sacrypt
    Installed 1 package in 1ms
    Hello from sacrypt!
    

    And the folder structure is now:

    sacrypt
    ├── README.md
    ├── __pycache__
    │   └── main.cpython-312.pyc
    ├── main.py
    ├── pyproject.toml
    ├── sacrypt.egg-info
    │   ├── PKG-INFO
    │   ├── SOURCES.txt
    │   ├── dependency_links.txt
    │   ├── entry_points.txt
    │   └── top_level.txt
    └── uv.lock
    

    I have found this issue in astral-sh's github which discusses the possibility to introduce such scripts - but it seems this is then solved.

    Alternative Versions of [build-system]

    are listed here: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts

    using hatchling

    [build-system]
    requires = ["hatchling"]
    build-backend = "hatchling.build"
    

    using setuptools

    [build-system]
    requires = ["setuptools >= 61.0"]
    build-backend = "setuptools.build_meta"
    

    using Flit

    [build-system]
    requires = ["flit_core >= 3.4"]
    build-backend = "flit_core.buildapi"
    

    or using PDM

    [build-system]
    requires = ["pdm-backend"]
    build-backend = "pdm.backend"
    

    They discuss in this issue that a

    [build-system]                ## doesn't work!!
    requires = ["uv"]             ## doesn't work!!
    build-backend = "uv.api"      ## doesn't work!! as of 20250322
    

    would be nice - but this is not yet done!

    I've tried them now - and can say in my macos Python 3.12, only setuptools and PDM work. all the other suggested ones didn't work (yet).