---
title: "uv run: ejecuta scripts de Python sin gestionar dependencias"
description: "Aprende a usar uv run para ejecutar scripts de Python al instante. Sin venv ni pip install. Cubre dependencias con --with, metadatos PEP 723 y scripts con shebang."
date: 2025-07-06
categories: ["tools"]
tags: ["uv","python","scripting"]
---

import Notice from "@components/widgets/Notice.astro";
import ListCheck from "@components/widgets/ListCheck.astro";
import Accordion from "@components/widgets/Accordion.astro";
import Tabs from "@components/widgets/Tabs.astro";
import Tab from "@components/widgets/Tab.astro";

Ejecuta cualquier script de Python con dependencias en un solo comando. Sin entorno virtual, sin `pip install`, sin `requirements.txt`. `uv run` se encarga de todo: resuelve las dependencias, crea un entorno aislado, ejecuta tu script y guarda el resultado en caché para que las reejecuciones sean rápidas.

uv es un gestor de paquetes de Python escrito en Rust, con licencia Apache-2.0/MIT, creado por Astral (ahora una empresa de OpenAI). En el momento de escribir esto, la versión actual es la 0.12.x. Sigue siendo completamente de código abierto y el ritmo de publicaciones del proyecto no se ha ralentizado desde la adquisición.

<Notice type="info" title="¿Nuevo en uv?">
    Consulta primero [Cómo empezar con uv](https://www.bitdoze.com/uv-get-start/) para instalar uv y entender lo básico.
</Notice>

## ¿Por qué usar uv run para scripts de Python?

El flujo de trabajo tradicional en Python para ejecutar un script con dependencias externas es este:

1. Crear un entorno virtual
2. Activarlo
3. Instalar las dependencias con pip
4. Ejecutar el script
5. Desactivarlo y, opcionalmente, limpiarlo

Con `uv run`, toda esa secuencia se reduce a una línea:

```bash
uv run --with requests myscript.py
```

uv resuelve la dependencia, crea un entorno aislado (que queda en caché para reutilizarse en ejecuciones posteriores), ejecuta el script y listo. El entorno no se borra tras cada ejecución: se almacena en `~/.cache/uv`, así que las ejecuciones repetidas con las mismas dependencias son casi instantáneas. Simplemente no tienes que pensar en ello.

<YouTubeEmbed
  url="https://www.youtube.com/embed/ozy02OXTkZo"
  label="Ejecutar scripts de Python con uv: sin gestión de dependencias"
/>

## Ejecutar scripts sin dependencias (uv run)

El caso más sencillo: un script que solo usa la biblioteca estándar de Python.

```python
# test_basic.py
import os
import sys
import json

def system_info():
    info = {
        "python_version": sys.version,
        "platform": sys.platform,
        "user_home": os.path.expanduser("~"),
        "current_directory": os.getcwd()
    }
    print(json.dumps(info, indent=2))

if __name__ == "__main__":
    system_info()
```

Ejecútalo:

```bash
uv run test_basic.py
```

Salida esperada:

```json
{
  "python_version": "3.13.5 (main, Jun 11 2025, 15:30:20)",
  "platform": "linux",
  "user_home": "/home/username",
  "current_directory": "/home/username/scripts"
}
```

<Notice type="warning" title="¿Ejecutando dentro de un directorio de proyecto?">
    Si ejecutas `uv run script.py` dentro de un directorio que contiene un `pyproject.toml`, uv instalará primero las dependencias del proyecto actual, aunque tu script no las necesite. Usa `uv run --no-project script.py` para omitir la detección del proyecto y ejecutar el script de forma aislada. Nota: el flag `--no-project` debe ir **antes** del nombre del script.
</Notice>

## Ejecutar scripts con dependencias externas (uv run --with)

Aquí es donde `uv` realmente brilla. Crea un script que necesite paquetes de terceros y especifícalos en la línea de comandos:

```python
# test_api.py
import requests
import json
from datetime import datetime

def test_github_api():
    """Fetch GitHub API data for the astral-sh org"""
    try:
        response = requests.get("https://api.github.com/users/astral-sh")
        data = response.json()

        print(f"✅ API Response Status: {response.status_code}")
        print(f"🏢 Organization: {data.get('name', 'N/A')}")
        print(f"📍 Location: {data.get('location', 'N/A')}")
        print(f"👥 Public Repos: {data.get('public_repos', 0)}")
        print(f"⏰ Tested at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

    except requests.exceptions.RequestException as e:
        print(f"❌ API request failed: {e}")
    except KeyError as e:
        print(f"❌ Unexpected API response format: {e}")

if __name__ == "__main__":
    test_github_api()
```

Ejecútalo con el flag `--with`:

```bash
uv run --with requests test_api.py
```

Salida esperada (el valor de `public_repos` cambia con el tiempo; vuelve a ejecutarlo para ver el número actual):

```
✅ API Response Status: 200
🏢 Organization: Astral
📍 Location: United States of America
👥 Public Repos: 82
⏰ Tested at: 2026-08-23 10:15:42
```

Puedes especificar varias dependencias:

```bash
uv run --with requests --with pandas --with matplotlib data_analysis.py
```

O usar restricciones de versión:

```bash
uv run --with 'requests>=2.31.0,<3.0.0' --with 'pandas>=2.0.0' test_script.py
```

Si obtienes un `ModuleNotFoundError`, la causa más común es un flag `--with` que falta o una errata en el nombre del paquete.

Para un ejemplo real de monitorización de API, consulta nuestro [script comprobador masivo de URLs creado con uv](https://www.bitdoze.com/es/script-uv-comprobador-url/).

## Usar metadatos en línea en el script (PEP 723)

Para los scripts que vas a ejecutar más de una vez, incrustar la información de las dependencias directamente en el archivo es más limpio que pasar flags `--with` cada vez. El estándar PEP 723 de Python define un formato de metadatos en línea que uv lee automáticamente.

### El formato de la cabecera PEP 723

Los metadatos en línea usan un bloque de comentarios especial al principio de tu archivo Python:

```python
# /// script
# dependencies = [
#   "requests>=2.31.0",
#   "rich>=13.0.0"
# ]
# requires-python = ">=3.11"
# [tool.uv]
# exclude-newer = "2026-01-01T00:00:00Z"
# ///
```

Esto es lo que hace cada campo:

**`dependencies`**: lista todos los paquetes de Python que necesita tu script, con sintaxis estilo pip:
- `"requests>=2.31.0"`: versión mínima
- `"pandas>=2.0.0,<3.0.0"`: rango de versiones
- `"rich"`: la más reciente disponible
- `"django==4.2.7"`: versión exacta fijada

Este campo **debe** estar presente aunque tu script no tenga dependencias externas. En ese caso, usa `dependencies = []`.

**`requires-python`**: especifica el requisito de versión de Python con sintaxis PEP 440:
- `">=3.11"`: Python 3.11 o superior
- `">=3.11,<3.14"`: solo Python 3.11, 3.12 o 3.13

Sección **`[tool.uv]`**: configuración opcional específica de uv:
- **`exclude-newer`**: solo tiene en cuenta paquetes publicados antes de esta marca de tiempo (formato RFC 3339). Mejora la reproducibilidad al evitar que una publicación futura de un paquete cambie tu resolución de dependencias.

### Ejemplo completo: analizador de perfiles de GitHub

```python
# /// script
# dependencies = [
#   "requests>=2.31.0",
#   "rich>=13.0.0",
#   "click>=8.0.0"
# ]
# requires-python = ">=3.11"
# [tool.uv]
# exclude-newer = "2026-01-01T00:00:00Z"
# ///

import requests
import click
from rich.console import Console
from rich.table import Table

console = Console()

@click.command()
@click.option('--username', prompt='GitHub username', help='GitHub username to analyze')
def analyze_github_user(username):
    """Analyze a GitHub user's profile and repositories"""

    with console.status(f"[bold green]Fetching data for {username}..."):
        try:
            user_response = requests.get(f"https://api.github.com/users/{username}")
            user_response.raise_for_status()
            user_data = user_response.json()

            repos_response = requests.get(f"https://api.github.com/users/{username}/repos")
            repos_response.raise_for_status()
            repos_data = repos_response.json()

        except requests.exceptions.RequestException as e:
            console.print(f"[bold red]Error fetching data: {e}")
            return

    console.print(f"\n[bold blue]GitHub User Analysis: {username}[/bold blue]")
    console.print(f"Name: {user_data.get('name', 'N/A')}")
    console.print(f"Bio: {user_data.get('bio', 'N/A')}")
    console.print(f"Public Repos: {user_data.get('public_repos', 0)}")
    console.print(f"Followers: {user_data.get('followers', 0)}")

    table = Table(title=f"Top Repositories for {username}")
    table.add_column("Repository", style="cyan")
    table.add_column("Stars", style="magenta")
    table.add_column("Language", style="green")
    table.add_column("Description", style="yellow")

    top_repos = sorted(repos_data, key=lambda x: x.get('stargazers_count', 0), reverse=True)[:10]

    for repo in top_repos:
        table.add_row(
            repo['name'],
            str(repo.get('stargazers_count', 0)),
            repo.get('language', 'N/A'),
            (repo.get('description', 'N/A') or 'N/A')[:50] + "..."
        )

    console.print(table)

if __name__ == "__main__":
    analyze_github_user()
```

Guárdalo como `github_analyzer.py` y ejecútalo:

```bash
uv run github_analyzer.py
```

uv lee los metadatos en línea, instala `requests`, `rich` y `click` automáticamente y después ejecuta el script.

### Más ejemplos de PEP 723

<Tabs>
<Tab name="Análisis de datos">

```python
# /// script
# dependencies = [
#   "pandas>=2.0.0",
#   "matplotlib>=3.7.0"
# ]
# requires-python = ">=3.10"
# ///

import pandas as pd
import matplotlib.pyplot as plt

data = pd.read_csv('data.csv')
plt.plot(data['x'], data['y'])
plt.title("Data Analysis")
plt.show()
```

Ejecutar con: `uv run data_analysis.py`

</Tab>
<Tab name="Pruebas de API">

```python
# /// script
# dependencies = [
#   "httpx>=0.27.0",
#   "pytest>=8.0.0"
# ]
# requires-python = ">=3.10"
# [tool.uv]
# exclude-newer = "2026-01-01T00:00:00Z"
# ///

import httpx
import pytest

def test_health_endpoint():
    response = httpx.get("https://api.example.com/health")
    assert response.status_code == 200

def test_users_endpoint():
    response = httpx.get("https://api.example.com/users")
    assert response.status_code == 200
    assert len(response.json()) > 0
```

Ejecutar con: `uv run api_tests.py`

</Tab>
<Tab name="Generador de informes">

```python
# /// script
# dependencies = [
#   "pandas>=2.0.0",
#   "rich>=13.0.0"
# ]
# requires-python = ">=3.10"
# ///

import pandas as pd
from rich.console import Console
from rich.table import Table

console = Console()

df = pd.read_csv("sales.csv")
summary = df.groupby("product").agg({"amount": "sum", "quantity": "sum"}).reset_index()

table = Table(title="Sales Summary")
table.add_column("Product", style="cyan")
table.add_column("Revenue", style="green")
table.add_column("Units Sold", style="magenta")

for _, row in summary.iterrows():
    table.add_row(row["product"], f"${row['amount']:,.2f}", str(int(row["quantity"])))

console.print(table)
```

Ejecutar con: `uv run report.py`

</Tab>
</Tabs>

### Configurar un índice de paquetes privado

Los antiguos campos `index-url` y `extra-index-url` de `[tool.uv]` están obsoletos. El enfoque moderno usa tablas `[[tool.uv.index]]`:

```python
# /// script
# dependencies = ["requests>=2.31.0"]
# requires-python = ">=3.10"
# [[tool.uv.index]]
# url = "https://pypi.example.com/simple"
# ///
```

También puedes añadir índices desde la CLI al gestionar scripts:

```bash
uv add --script example.py --index "https://pypi.example.com/simple" 'requests<3'
```

Para sobrescribir el índice por defecto, usa `--default-index` en lugar del obsoleto `--index-url`.

## Ciclo de vida del script: init → add → run → lock → audit

Editar a mano el bloque `# ///` funciona, pero uv ahora tiene comandos de primera clase para gestionar los metadatos de los scripts. Es el camino más rápido de la idea al script en ejecución.

### 1. Crear un script nuevo

```bash
uv init --script healthcheck.py --python 3.12
```

Esto crea `healthcheck.py` con la cabecera PEP 723 ya incluida.

### 2. Añadir dependencias

```bash
uv add --script healthcheck.py 'httpx>=0.27' 'rich'
```

uv actualiza los metadatos en línea del archivo. Sin edición manual de TOML.

### 3. Ejecutarlo

```bash
uv run healthcheck.py https://example.com
```

### 4. Bloquear para reproducibilidad

```bash
uv lock --script healthcheck.py
```

Esto crea `healthcheck.py.lock`, un archivo de bloqueo adyacente que fija las versiones exactas de las dependencias. Comprueba que existe:

```bash
ls healthcheck.py.lock
```

### 5. Ejecutar solo con las dependencias bloqueadas

```bash
uv run --locked healthcheck.py
```

Esto falla si el archivo de bloqueo no existe o está desactualizado, que es exactamente lo que quieres en CI.

### 6. Auditar CVE conocidos

```bash
uv audit --script healthcheck.py
```

Esto comprueba tus dependencias bloqueadas contra bases de datos de vulnerabilidades conocidas. Nota: el script debe estar bloqueado primero; `uv audit` sobre un script sin bloquear dará error.

### 7. Inspeccionar el árbol de dependencias

```bash
uv tree --script healthcheck.py
```

### 8. Eliminar una dependencia

```bash
uv remove --script healthcheck.py rich
```

Este ciclo de vida (`init` -> `add` -> `run` -> `lock` -> `audit`) convierte el «editar TOML a mano» en un flujo de trabajo gestionado con reproducibilidad y auditoría de seguridad integradas.

## Crear scripts ejecutables con un shebang de uv

Para los scripts que ejecutas con frecuencia, añade un shebang para hacerlos directamente ejecutables. La línea `#!/usr/bin/env -S uv run --script` le indica al sistema que use uv para ejecutar el script. También hay un alias corto: `-s` en lugar de `--script`.

```python
#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
#   "httpx>=0.27.0",
#   "typer>=12.0.0"
# ]
# requires-python = ">=3.11"
# ///

import httpx
import typer
from typing import Optional

app = typer.Typer()

@app.command()
def check_website(
    url: str = typer.Argument(..., help="Website URL to check"),
    timeout: Optional[int] = typer.Option(10, help="Request timeout in seconds")
):
    """Check if a website is up and running"""

    if not url.startswith(('http://', 'https://')):
        url = f"https://{url}"

    try:
        with httpx.Client(timeout=timeout) as client:
            response = client.get(url)

        if response.status_code == 200:
            typer.echo(f"✅ {url} is UP (Status: {response.status_code})")
            typer.echo(f"Response time: {response.elapsed.total_seconds():.2f}s")
        else:
            typer.echo(f"⚠️  {url} returned status {response.status_code}")

    except httpx.RequestError as e:
        typer.echo(f"❌ {url} is DOWN - {e}")
    except httpx.TimeoutException:
        typer.echo(f"❌ {url} timed out after {timeout} seconds")

if __name__ == "__main__":
    app()
```

Hazlo ejecutable y ejecútalo:

```bash
chmod +x website_checker.py
./website_checker.py github.com
```

## Gestionar las versiones de Python en los scripts

Puedes especificar qué versión de Python usar en cualquier ejecución de un script:

```bash
# Run with Python 3.12
uv run --python 3.12 test_script.py

# Run with Python 3.13
uv run --python 3.13 test_script.py
```

Si la versión solicitada no está instalada, uv la descarga automáticamente. No hace falta instalación manual. ¿Necesitas instalar Python en tu sistema primero? Consulta nuestra guía sobre [cómo instalar y gestionar versiones de Python](https://www.bitdoze.com/install-upgrade-python-mac/).

También puedes fijar la versión en los metadatos en línea:

```python
# /// script
# requires-python = ">=3.11"
# dependencies = ["aiohttp>=3.9"]
# ///

import asyncio
import aiohttp

async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
        "https://httpbin.org/delay/1",
        "https://httpbin.org/delay/2",
        "https://httpbin.org/delay/3"
    ]

    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)

    print(f"Fetched {len(results)} URLs successfully!")

if __name__ == "__main__":
    asyncio.run(main())
```

Nota: `asyncio` forma parte de la biblioteca estándar de Python — no es un paquete de PyPI y no debe aparecer en `dependencies`.

## Variables de entorno y secretos (archivos .env)

Los scripts que llaman a API suelen necesitar secretos. En lugar de escribirlos directamente en el código, usa `--env-file`:

```bash
uv run --env-file .env api_probe.py
```

Tu archivo `.env`:

```
API_KEY=sk-abc123
API_BASE_URL=https://api.example.com
```

Tu script los lee con `os.environ`:

```python
import os

api_key = os.environ["API_KEY"]
base_url = os.environ.get("API_BASE_URL", "https://api.default.com")
```

Para desactivar la carga de `.env` (por ejemplo, en CI, donde defines las variables de entorno de otra forma), pasa `--no-env-file`.

<Notice type="warning" title="No subas nunca los archivos .env">
    Añade `.env` a tu `.gitignore` de inmediato. Los secretos en el control de versiones son una brecha de seguridad esperando a ocurrir.
</Notice>

## Stdin y scripts remotos

Puedes canalizar scripts directamente a uv por stdin:

```bash
echo 'print("hello from stdin")' | uv run -
```

O usar la sintaxis heredoc:

```bash
uv run - <<'EOF'
import sys
print(f"Running on Python {sys.version}")
EOF
```

`uv run` también acepta una URL HTTP(S): descarga y ejecuta el script:

```bash
uv run https://example.com/scripts/healthcheck.py
```

<Notice type="error" title="Advertencia de seguridad para scripts remotos">
    Ejecutar un script desde una URL descarga y ejecuta código arbitrario. uv instalará automáticamente las dependencias que el script declare en su cabecera PEP 723. Ejecuta solo scripts remotos de fuentes en las que confíes. Esta función también está deshabilitada en algunos entornos corporativos y de CI.
</Notice>

## Web scraping y conversión a Markdown

Un caso de uso práctico: extraer una página web y convertirla a Markdown limpio. Esta es una versión reducida — para scraping de nivel producción a escala, considera la [plataforma de web scraping de Bright Data](https://go.bitdoze.com/brightdata). Más información sobre [web scraping con BrightData MCP](https://www.bitdoze.com/es/guia-brightdata-mcp/).

```python
# /// script
# dependencies = [
#   "requests>=2.31.0",
#   "beautifulsoup4>=4.13.0",
#   "markdownify>=0.14.0",
#   "typer>=12.0.0",
#   "rich>=13.0.0"
# ]
# requires-python = ">=3.10"
# ///

import requests
import typer
from bs4 import BeautifulSoup
from markdownify import markdownify as md
from rich.console import Console
from rich.panel import Panel
import re

console = Console()
app = typer.Typer()

def clean_markdown(text: str) -> str:
    text = re.sub(r'\n\s*\n\s*\n', '\n\n', text)
    return text.strip()

@app.command()
def scrape(
    url: str = typer.Argument(..., help="URL to scrape"),
    output: str = typer.Option(None, "--output", "-o", help="Output file"),
    selector: str = typer.Option(None, "--selector", "-s", help="CSS selector"),
):
    """Scrape a webpage and convert to markdown."""
    if not url.startswith(('http://', 'https://')):
        url = f"https://{url}"

    headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
    }

    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()

    soup = BeautifulSoup(response.content, 'html.parser')

    for tag in soup(["script", "style", "nav", "footer", "header", "aside"]):
        tag.decompose()

    if selector:
        content = soup.select_one(selector)
    else:
        content = soup.find('article') or soup.find('main') or soup.find('body')

    if not content:
        console.print("[bold red]No content found")
        return

    markdown = clean_markdown(md(str(content), heading_style='ATX', bullets='*'))

    if output:
        with open(output, 'w') as f:
            f.write(markdown)
        console.print(f"[bold green]✅ Saved to {output}")
    else:
        preview = markdown[:2000] + "..." if len(markdown) > 2000 else markdown
        console.print(Panel(preview, title="Markdown Output", border_style="green"))

if __name__ == "__main__":
    app()
```

Uso:

```bash
# Scrape and preview
uv run web_scraper.py https://example.com

# Save to file
uv run web_scraper.py https://blog.example.com --output article.md

# Extract specific content via CSS selector
uv run web_scraper.py https://docs.example.com --selector "article"
```

## Buenas prácticas para los scripts de uv

<ListCheck>
<ul>
<li>Usa metadatos en línea (PEP 723) para cualquier script que ejecutes más de una vez</li>
<li>Especifica siempre `requires-python` para evitar sorpresas en distintos sistemas</li>
<li>Usa restricciones de versión: fija las versiones mayores para evitar cambios que rompan el código</li>
<li>Usa `uv lock --script` para ejecuciones reproducibles, sobre todo en CI</li>
<li>Revisa el bloque `# ///` antes de ejecutar scripts compartidos por otros (ver la sección de seguridad más abajo)</li>
<li>Gestiona los errores correctamente: las peticiones de red, la E/S de archivos y las llamadas a API fallan</li>
<li>Usa `--env-file` para los secretos en lugar de incrustar claves de API en tus scripts</li>
</ul>
</ListCheck>

## Consideraciones de seguridad para scripts PEP 723

Los scripts PEP 723 instalan y ejecutan automáticamente las dependencias declaradas en el bloque `# ///`. Es cómodo, pero también es un vector para código malicioso, sobre todo porque muchas herramientas de análisis de composición de software (SCA) todavía no escanean los metadatos en línea.

<Notice type="warning" title="Revisa antes de ejecutar">
    Antes de ejecutar un script PEP 723 de una fuente no confiable (un gist de GitHub, una entrada de blog, el fragmento de un colega), lee el bloque `# /// script`. Comprueba que los nombres de las dependencias no sean typosquatting (por ejemplo, `requets` en lugar de `requests`). Para los scripts que mantienes, usa `uv lock --script` y `uv audit --script` para detectar CVE conocidos.
</Notice>

## Solución de problemas con uv run

<Accordion label="ModuleNotFoundError" group="faq" expanded="true">

La causa más común: un flag `--with` que falta o una lista `dependencies` ausente o vacía en los metadatos en línea.

**Solución:** añade el flag `--with` en la línea de comandos, o añade el paquete al array `dependencies` de tu bloque `# /// script`. Recuerda que `dependencies` debe estar presente aunque esté vacío (`dependencies = []`).

</Accordion>

<Accordion label="El script se ejecuta con las dependencias del proyecto" group="faq">

Si ejecutas `uv run script.py` dentro de un directorio con un `pyproject.toml`, uv instala primero las dependencias del proyecto actual. Normalmente no es lo que quieres para scripts independientes.

**Solución:** añade `--no-project` antes del nombre del script:

```bash
uv run --no-project script.py
```

Nota: los scripts con metadatos PEP 723 en línea ignoran automáticamente el proyecto que los rodea; `--no-project` solo es necesario para scripts sin metadatos en línea.

</Accordion>

<Accordion label="Autenticación en un índice privado" group="faq">

Si tus dependencias provienen de un PyPI privado (Azure Artifactory, AWS CodeArtifact, etc.), configura el índice en tus metadatos en línea:

```python
# [[tool.uv.index]]
# url = "https://pkgs.dev.azure.com/myorg/_packaging/myfeed/pypi/simple/"
```

O pásalo por la CLI: `uv run --index "https://..." script.py`. Para las credenciales, usa `uv auth login` o define la variable de entorno `UV_INDEX`.

</Accordion>

<Accordion label="La caché crece demasiado" group="faq">

uv guarda en caché los entornos y los wheels en `~/.cache/uv`. Con el tiempo puede crecer, sobre todo si ejecutas scripts con conjuntos de dependencias muy distintos.

**Comprobar el tamaño:**

```bash
uv cache size --human
```

**Limpieza:**

```bash
uv cache clean       # remove all cached packages
uv cache prune       # remove unused cache entries
```

</Accordion>

<Accordion label="Ejecuciones reproducibles en CI" group="faq">

Para una resolución de dependencias determinista en CI, bloquea primero el script y ejecútalo después con `--locked`:

```bash
uv lock --script my_script.py
uv run --locked my_script.py
```

`--locked` falla si el archivo de bloqueo no existe o no coincide con los metadatos en línea. Usa `--frozen` para omitir la resolución por completo y usar lo que haya en el archivo de bloqueo (más rápido, pero no se actualizará si los metadatos cambiaron).

</Accordion>

## Beneficios de rendimiento

Astral afirma que uv es entre 10 y 100 veces más rápido que las herramientas tradicionales de gestión de paquetes de Python. La mejora exacta depende del escenario (instalaciones en frío frente a reejecuciones en caché, número de dependencias, latencia de red), pero la diferencia se nota en la práctica, sobre todo en pipelines de CI y en el desarrollo iterativo.

Donde más se nota:
- **Pipelines de CI/CD**: compilaciones más rápidas significan ciclos de feedback más rápidos
- **Flujos de desarrollo**: ejecuta scripts sin esperar a que se configure el entorno
- **Experimentación en ciencia de datos**: cambia rápido entre conjuntos de bibliotecas
- **Administración de sistemas**: usa conjuntos de herramientas distintos por script sin conflictos

## Casos de uso comunes

### Pruebas y monitorización de API

```bash
# Quick API health check
uv run --with requests api_health_check.py

# Test API with different HTTP methods
uv run --with httpx --with typer api_tester.py --method POST --url https://api.example.com
```

Consulta nuestro [script comprobador masivo de URLs con uv](https://www.bitdoze.com/es/script-uv-comprobador-url/) para un ejemplo completo de monitorización.

### Procesamiento y análisis de datos

```bash
# Process CSV files
uv run --with pandas --with matplotlib data_processor.py input.csv

# Web scraping and content conversion
uv run --with beautifulsoup4 --with requests --with markdownify web_scraper.py https://example.com
```

### Administración de sistemas

```bash
# Server monitoring
uv run --with psutil --with click server_monitor.py

# Log analysis
uv run --with click --with rich log_analyzer.py /var/log/app.log
```

### Prototipado y experimentación

```bash
# Test new libraries quickly
uv run --with fastapi --with uvicorn prototype_api.py

# Content extraction and conversion
uv run --with beautifulsoup4 --with markdownify --with typer content_converter.py
```

### Ejecutar herramientas CLI empaquetadas con uvx

Si necesitas ejecutar una herramienta CLI empaquetada (no un archivo de script), usa `uvx` en lugar de `uv run`:

```bash
uvx ruff check .
uvx httpie GET https://api.example.com
uvx black --check src/
```

`uvx` es la forma abreviada de `uv tool run`: instala la herramienta en un entorno aislado y la ejecuta. Úsalo para herramientas como `ruff`, `black`, `httpie`, `mypy`, etc. Usa `uv run` para tus propios archivos de script.

## Conclusión

`uv run` elimina la fricción de la gestión de dependencias de Python para los scripts. Tanto si estás probando API, procesando datos, haciendo scraping de páginas web o prototipando, escribes el script y lo ejecutas: uv se encarga del resto.

<ListCheck>
<ul>
<li>Un solo comando para ejecutar cualquier script con dependencias: sin venv, sin pip</li>
<li>Los metadatos en línea PEP 723 mantienen las dependencias dentro del archivo del script</li>
<li>`uv init --script` + `uv add --script` + `uv lock --script` para un ciclo de vida gestionado</li>
<li>`--env-file` para los secretos, `--locked` para la reproducibilidad en CI, `uv audit` para la seguridad</li>
<li>Entre 10 y 100 veces más rápido que las herramientas tradicionales (benchmarks de Astral)</li>
</ul>
</ListCheck>

Cuando tu script crezca hasta convertirse en algo más grande, explora los [mejores frameworks web de Python](https://www.bitdoze.com/best-python-web-frameworks/) para crear aplicaciones completas. ¿Listo para contenedorizar? Aprende a [ejecutar aplicaciones Python en Docker](https://www.bitdoze.com/docker-run-python/). Y cuando estés listo para desplegar, [despliega tus proyectos uv con Dokploy y Railpack](https://www.bitdoze.com/dokploy-python-railpack-uv/).

## Artículos relacionados

- [Cómo empezar con uv](https://www.bitdoze.com/uv-get-start/): instala uv y configura tu primer proyecto
- [Texto a voz con uv](https://www.bitdoze.com/es/script-uv-texto-a-voz/): crea un script de generación de audio
- [Despliega un proyecto Python con uv y Dokploy](https://www.bitdoze.com/dokploy-python-railpack-uv/): despliegue en producción con Git y Railpack
- [Script comprobador masivo de URLs con uv](https://www.bitdoze.com/es/script-uv-comprobador-url/): ejemplo real de monitorización de API