admin管理员组

文章数量:1401667

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.

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.

Share Improve this question edited Mar 22 at 11:17 jjmerelo asked Mar 22 at 10:55 jjmerelojjmerelo 23.6k8 gold badges42 silver badges95 bronze badges 5
  • 1 From the linked page: "Using the entry point tables requires a build system to be defined." – InSync Commented Mar 22 at 11:00
  • @InSync is there a simple way to do that? – jjmerelo Commented Mar 22 at 11:13
  • @InSync my impression is that it's not really calling the sacript/__init__:main but a main function in the .venv/bin/sacrypt script created by uv build. Is that correct? – jjmerelo Commented Mar 22 at 11:20
  • Your understanding is correct as far as I could tell. Is uv init --app --package simple enough? – InSync Commented Mar 22 at 11:27
  • @InSync once you know it, totally. But I didn't start there, and arriving to that was complicated. I've updated my answer with this, please check if it's correct. – jjmerelo Commented Mar 22 at 11:59
Add a comment  | 

2 Answers 2

Reset to default 0

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/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./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).

The documentation, as indicated in this comment, requires to set up a build system. It does not indicate why or how to do that, or the prerequisites to do it. So here's a bit of context

pyproject.toml and the tools that use it

Although pyproject.toml is a standard, it pretty much defines a standard way of adding metadata and some dependencies to a Python project. It does not define some other things, as case in point, how to define scripts that are going to be run with tool run or anything else related to that. So, for instance, poetry will use [tool.poetry.scripts].

More importantly, it does not define what to do with that. So if something works with tool.poetry.scripts (say, key with script name and value that directly points to a function in a module), that does not imply that if project.scripts follows the same syntax, it will work the same way.

Let's get down to project.scripts

Although it uses the keyword scripts, it's better defined in the documentation as an entrypoint, that is, something like an API. So you should not understand the key as a symbolic name that will point to a Python function as above. It's a real entrypoint, something you will use to run, and that, theoretically, it belongs to your project, not to uv as a task manager. That should maybe be clear enough from the fact that it does not have the uv. prefix, but let's hammer it home. What you put in that section is

script you should have in your virtual environment = modulename:function you will call there.

Note: to be honest, I'm not entirely clear here if what's in there is the same script name or the module name... which in this case are the same. But bear with me here.

But the thing is, you need to have a script in your virtual environment. Which takes me to the next section

uv is a dependency manager... and build tool.

It's used also to build a wheel or something that is otherwise going to be distributed. It's got its build subcommand and all, which does not seem to have a sensible default, so you have to define it explicitly. This seems to work

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

This, as indicated again in this comment, can also be substituted by:

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

Again, this is standard for pyproject.toml, although it seems to use it in a different way, defining a part of poetry itself as build system. But this is required to actually build a distribution or a wheel, not for running a script.

And build tools have to be used to build. And they might be a bit picky about where to find what they are looking for; the modulename/__init__.py layout is no longer adequate. They will look by default in the src directory, so that needs to be set up; you can put your former modulename directory inside that directory, even if that's not (apparently) the most usual directory layout.

Once you do all that, you're done and you can use the script. So let's recapitulate.

Summary

In order to make a script in the project.scripts section in pyproject.toml to work with uv, you need to

  1. Use src/packagename as the directory for your package, instead of packagename
  2. Set up a build-system as above, so that you're able to define how to create a script
  3. Create a project.scripts section in pyproject.toml with packagename = "packagename/function". These first three steps will be done for you if, as indicated by InSync in this comment, you run uv init --app --package.
  4. Run uv build
  5. After that, you can run uv run packagename and it should work

本文标签: pythonuv run fails with quotfailed to spawnquotStack Overflow