For the longest time, I have been frustrated with Python because I couldn’t use it for one-off scripts. I had to first ensure it was running in an environment where it could find the right Python version and the dependencies installed. That is now a thing of the past.
uv
If you are not a Pythonista (or one possibly living under a rock), uv is an extremely fast Python package and project manager, written in Rust.
uv also provides this nifty tool called uvx (kinda like npx from the Node/NPM ecosystem for Javascript/Typescript packages) which can be used to invoke a Python tool inside a package. uvx takes care of creating a (cached) disposable virtual environment, setting up the right Python version and installing all the dependencies before running.
For example
$ uvx ruff --version Installed 1 package in 5ms ruff 0.12.0PEP 723 is a Python Enhancement Proposal that specifies a metadata format that can be embedded in single-file Python scripts to assist launchers, IDEs and other external tools which may need to interact with such scripts.
Here is the example directly lifted from the proposal:
# /// script # requires-python = ">=3.11" # dependencies = [ # "requests<3", # "rich", # ] # /// import requests from rich.pretty import pprint resp = requests.get("https://peps.python.org/api/peps.json") data = resp.json() pprint([(k, v["title"]) for k, v in data.items()][:10])Using uv and PEP 723 metadata to run a Python script
Combining uv and the PEP-723 metadata inside a Python script, we can run the script in the previous section as follows:
$ uv run pep.py Installed 9 packages in 24ms [ │ ('1', 'PEP Purpose and Guidelines'), │ ('2', 'Procedure for Adding New Modules'), │ ('3', 'Guidelines for Handling Bug Reports'), │ ('4', 'Deprecation of Standard Modules'), │ ('5', 'Guidelines for Language Evolution'), │ ('6', 'Bug Fix Releases'), │ ('7', 'Style Guide for C Code'), │ ('8', 'Style Guide for Python Code'), │ ('9', 'Sample Plaintext PEP Template'), │ ('10', 'Voting Guidelines') ]We can combine things we covered in the previous sections to create a simple executable script that can extract YouTube transcripts.
First we create a Python script with a shebang and inline metadata.
#!/usr/bin/env -S uv run --script # /// script # requires-python = ">=3.8" # dependencies = [ # "youtube-transcript-api", # ] # /// import sys import re from youtube_transcript_api import YouTubeTranscriptApi from youtube_transcript_api.formatters import TextFormatter if len(sys.argv) < 2: print('Usage: provide YouTube URL or video_id as argument', file=sys.stderr) sys.exit(1) url_or_id = sys.argv[1] # Extract video ID from URL if it's a full URL video_id_match = re.search(r'(?:v=|/)([a-zA-Z0-9_-]{11})', url_or_id) if video_id_match: video_id = video_id_match.group(1) else: # Assume it's already a video ID video_id = url_or_id try: ytt_api = YouTubeTranscriptApi() transcript = ytt_api.fetch(video_id) formatter = TextFormatter() print(formatter.format_transcript(transcript)) except Exception as e: print(f'Error: {e}', file=sys.stderr) sys.exit(1)Note the shebang line: #!/usr/bin/env -S uv run --script. It is important to specify uv run with the --script flag when used on the shebang line.
We save this script as ytt and then make it executable with chmod +x ytt.
We can now run the script like:
$ ./ytt https://www.youtube.com/watch?v=zgSQr0d5EVg Installed 7 packages in 10ms hey it's Matt here and today I'm going to show you how to use UV not only to install packages in your python projects but to manage entire projects to create virtual environments and even how to manage entire versions of python with UV uh and hopefully you'll understand by any of this video UV is a dropin …<snipped text>…Fun times
This opens up a lot of possibilities for running Python code more seamlessly. Before this I used to prefer Go for one-off scripts because it was easy to create a self-contained binary executable. But now that I could use uv, I coded up a quick MCP server in Python for extracting YouTube transcripts. Check it out on Github at cottongeeks/ytt-mcp.