uv
uv copied to clipboard
entry points/scripts with additional "attributes" are not generated correctly (e.g. for Pyinvoke)
uv
generates console scripts incorrectly for entry_points that have dotted function paths.
For example Invoke's setup.py:
entry_points={
"console_scripts": [
"invoke = invoke.main:program.run",
"inv = invoke.main:program.run",
]
},
The generated script will look like:
#!/path/to/uv-venv/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from invoke.main import program.run
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(program.run())
And trying to run this (Python 3.12.0) generates:
uv-venv/bin/inv
File "/Users/markm/clmv2/ServiceTest/uv-venv/bin/inv", line 5
from invoke.main import program.run
^
SyntaxError: invalid syntax
The pip generated console script separates the import_name from the function_name
#!/path/to/py-venv/bin/python3.12
# -*- coding: utf-8 -*-
import re
import sys
from invoke.main import program
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(program.run())
- Steps to Reproduce:
-
uv pip install pyinvoke
-
inv
(orinvoke
)
-
- Platform: macOS 14.3 (23D56) (M1)
-
uv-version
:uv 0.1.3
I see there is a comment in the UV code asking what is the format - I think we can probably follow the rules here: https://github.com/pypa/setuptools/blob/main/docs/userguide/entry_point.rst#entry-points-syntax i.e.
Nested object
If you supply:
<name> = <package_or_module>:<object>.<attr>.<nested_attr>
this will be roughly interpreted as:
from <package_or_module> import <object> parsed_value = <object>.<attr>.<nested_attr>
Also maybe not relevant - but the script parsing regex looks different between Pip and uv:
- Pip: https://github.com/pypa/pip/blob/7f8a6844037fb7255cfd0d34ff8e8cf44f2598d4/src/pip/_vendor/distlib/util.py#L710
- uv: https://github.com/astral-sh/uv/blob/main/crates/install-wheel-rs/src/script.rs#L46C9-L46C155
I don't mind trying to fix this - but unsure whether I should add an "import_name" field to script::Script
- or provide a function to return these from the string (i.e. instead of using entrypoint.import_name
Would the preference be to extract this in the regex - or via string parsing/splitting?
Awesome, thank you! Would love help. I think it'd be reasonable to add a field to script::Script
, but I don't have a strong preference at this point.
Thanks for writing up such a clear issue.
I also ran into this when installing granian. Which also has a dot in the script but not in the module name.
Below I attached the differences between pip and uv I found, which match the problem.
[project.scripts]
granian = 'granian:cli.cli'
The script generated by pip is:
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from granian import cli
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(cli.cli())
While uv creates
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from granian import cli.cli
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(cli.cli())
I can look into this.