📊
markdown preview enhancedのコードチャンクでuvを利用する
TL;DR
末尾のコード(run_with_uv.py
)を同一階層において、
```python {cmd=python args=["run_with_uv.py", "numpy"]}
で囲い、実行する
実行例
v3.13でnumpyを利用したコードを実行する
# python {cmd=python args=["run_with_uv.py", "numpy","version=3.13", "$input_file"]}
import numpy as np
print(np.array([1,1]))
Installed 1 package in 149ms
[1 1]
numpyを利用したコードを実行する
# python {cmd=python args=["run_with_uv.py", "numpy", "$input_file"]}
import numpy as np
print(np.array([1,1]))
Downloading numpy (17.5MiB)
Downloading numpy
Installed 1 package in 72ms
[1 1]
pandasを利用したコードを実行する
# python {cmd=python args=["run_with_uv.py", "pandas","version=3.13", "$input_file"]}
import pandas as pd
import numpy as np
print(np.array([1,1]))
Installed 6 packages in 136ms
[1 1]
特に何も指定しないとそのまま実行される
# python {cmd=python args=["run_with_uv.py", "$input_file"]}
print([1,1])
[1, 1]
ファイル名も指定しなくてOK
# python {cmd=python args=["run_with_uv.py"]}
print([1,1])
[1, 1]
バージョンの変更もできる-その1
# python {cmd=python args=["run_with_uv.py", "version=3.11"]}
import sys
print(sys.version)
3.11.12 (main, Apr 9 2025, 03:55:30) [Clang 14.0.3 ]
バージョンの変更もできる-その2
# python {cmd=python args=["run_with_uv.py", "v=3.13"]}
import sys
print(sys.version)
3.13.3 (main, Apr 9 2025, 03:55:34) [Clang 14.0.3 ]
やっていること
markdown preview enhancedのコードチャンクは下記のコードで実行されている。
特定のコマンドがパースされて、実行される。ホストOSのuvを利用したいと思った場合にはいくつかの方法があるが、ここではpythonのsubprocessを介して実行することにした。
依存関係に関してはpythonの実行時に渡される引数をパースして、スクリプト内に書き込み、uvのPEP723に準拠した、ファイル内での依存関係を解決して実行できる機能を利用する。
script.py
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "numpy",
# ]
# ///
import numpy as np
...
uv run
を実行して、スクリプト内の依存関係を動的に解決しつつ実行する。
def run_script(script_path):
"""Run the script with uv and return the result."""
try:
return subprocess.run(
["uv", "run", script_path], capture_output=True, text=True
)
実行例
例えば
python {cmd=python args=["run_with_uv.py", "pandas","version=3.13", "$input_file"]}
と書くことで実行するスクリプトの上部に
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "pandas",
# ]
# ///
が記載され、uv run
が実行される。
以上
付録:コード
run_with_uv.py
#!/usr/bin/env python3
# Save this as run_with_uv.py in your project directory
# Usage patterns:
# With dependency: {cmd=python args=["run_with_uv.py", "numpy", "version=3.13", "$input_file"]}
# Multiple dependencies: {cmd=python args=["run_with_uv.py", "numpy,pandas", "version=3.13", "$input_file"]}
# No version: {cmd=python args=["run_with_uv.py", "numpy", "$input_file"]}
# No deps or version: {cmd=python args=["run_with_uv.py", "$input_file"]}
# Simplest form: {cmd=python args=["run_with_uv.py"]}
import os
import re
import subprocess
import sys
import tempfile
default_python_version = "3.11" # Default Python version
def parse_arguments():
"""Parse command line arguments and return dependencies, Python version, and input file path."""
dependencies = []
python_version = None # Default Python version
input_file = None
# Parse arguments
for arg in sys.argv[1:]:
if arg.startswith("version=") or arg.startswith("v="):
# Extract version from version=X.Y format
version_match = re.search(r"[version|v]=(\d+\.\d+)", arg)
if version_match:
python_version = version_match.group(1)
elif "code_chunk" in arg:
# This is likely the input file
input_file = arg
elif not arg.startswith("version=") and not arg.startswith("v="):
# Assume this is a dependency list if it's not a version or input file
if "," in arg:
# Multiple dependencies
dependencies = [dep.strip() for dep in arg.split(",") if dep.strip()]
else:
# Single dependency
dependencies = [arg.strip()]
# If no input file was found in args, try to find it in the current directory
if input_file is None:
# Look for code_chunk files in the current directory
import glob
code_chunks = glob.glob("*_code_chunk.*")
if code_chunks:
# Sort by creation time, newest first
code_chunks.sort(key=os.path.getctime, reverse=True)
input_file = code_chunks[0]
return dependencies, python_version, input_file
def read_input_file(input_file):
"""Read content from the input file."""
if not input_file:
print(
"Error: No input file found. Make sure to include $input_file in your arguments "
"or run this in a directory with code_chunk files.",
file=sys.stderr,
)
return None
if not os.path.exists(input_file):
print(f"Error: Input file '{input_file}' not found.", file=sys.stderr)
return None
try:
with open(input_file, "r") as f:
return f.read()
except Exception as e:
print(f"Error reading input file: {e}", file=sys.stderr)
return None
def create_temp_script(original_content, dependencies, python_version):
"""Create a temporary script file with the appropriate headers."""
with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as temp_file:
temp_path = temp_file.name
# Write script headers
temp_file.write("# /// script\n")
if python_version is None:
temp_file.write(f'# requires-python = ">={default_python_version}"\n')
else:
temp_file.write(f'# requires-python = "=={python_version}.*"\n')
# Add dependencies if specified
if dependencies:
temp_file.write("# dependencies = [\n")
for dep in dependencies:
temp_file.write(f'# "{dep}",\n')
temp_file.write("# ]\n")
temp_file.write("# ///\n")
# Write the original content
temp_file.write(original_content)
return temp_path
def run_script(script_path):
"""Run the script with uv and return the result."""
try:
return subprocess.run(
["uv", "run", script_path], capture_output=True, text=True
)
except Exception as e:
print(f"Error running script: {e}", file=sys.stderr)
return None
def filter_output(output):
"""Filter installation messages from the output."""
filtered_lines = []
for line in output.splitlines():
# Skip installation messages
if not line.strip() or "Installed" in line and "packages" in line:
continue
filtered_lines.append(line)
return "\n".join(filtered_lines)
def main():
"""Main function to orchestrate the process."""
# Parse command line arguments
dependencies, python_version, input_file = parse_arguments()
# Read input file content
original_content = read_input_file(input_file)
if original_content is None:
return 1
# Create temporary script file
temp_path = create_temp_script(original_content, dependencies, python_version)
try:
# Run script
result = run_script(temp_path)
if result is None:
return 1
# Always print errors
if result.stderr:
print(result.stderr, end="", file=sys.stderr)
# Filter and print output
print(result.stdout.strip())
return result.returncode
finally:
# Clean up
if os.path.exists(temp_path):
os.remove(temp_path)
if __name__ == "__main__":
sys.exit(main())
Discussion