Single-file Python script with embedded dependencies
Anyone who has worked on a UNIX-like system has at some point started to write a shell script, then realized that Bash is too limited for the task. I did.
Then we started to convert it to a more powerful interpreted language, like Python, only to realize that we now need to use external packages.
And we need to declare what these dependencies are, along with their version requirements.
At this point, a solution is to start a full-fledged Python project with a pyproject.toml file expressing dependencies, the script itself, everything is a directory, with maybe a nice README file.
But then we lose the simplicity of a self-contained file we can easily copy or share where it’s needed.
Fortunately, there’s now a better way, brought by PEP 723, adopted on 2024–01–08.
It allows to declare inline metadata as part of a Python script, especially dependencies requirements.
And uv supports it 💚
Along with a command option to bootstrap new scripts with a template for inline metadata.
Here’s how to generate a script whose purpose will be to generate a fresh ULID:
uv init --script gen_ulid.py
gen_ulid.py will contain:
# /// script
# requires-python = ">=3.13"
# dependencies = []
# ///
def main() -> None:
print("Hello from gen_ulid.py!")
if __name__ == "__main__":
main()
A shebang line can be added to be able to run the script like an executable : #!/usr/bin/env -S uv run --script
Dependencies can be specified: # dependencies = ["ulid-py"]
uv also supports a custom syntax to restrict packages versions to those published before a certain date, which prevents the script from breaking if future incompatible versions were distributed, without needing to use an external lock file:
# [tool.uv]
# exclude-newer = "2025-08-04T00:00:00Z"
We finally get this script content:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = ["ulid-py"]
# [tool.uv]
# exclude-newer = "2025-08-04T00:00:00Z"
# ///
import ulid
def main() -> None:
print(ulid.new())
if __name__ == "__main__":
main()
Let’s run it a first time:
$ chmod +x gen_ulid.py
$ ./gen_ulid.py
Installed 1 package in 3ms
01K244Y4RY3GZM7FH62Z9MS3EH
Then a second time, package files having been cached somewhere in ~/.cache/uv:
$ ./gen_ulid.py
01K24538K88T67T1YA3BJX9F69
Now we have the best of both worlds: a single, self-contained file, with proper dependency management.
By Thomas Martin
Follow me or comment