Tu país

Las herramientas que lo admiten usan tu país para la moneda local, el formato de los números, las unidades y el tamaño del papel. Tu elección se guarda solo en este navegador.

Escribe un nombre o un código de dos letras. Usa las flechas arriba y abajo para recorrer los países, Intro para elegir uno y Escape para cerrar.

Python  Módulo 1 – Primeros pasos: instalar, ejecutar y leer Python

Entornos virtuales con venv y pip

Crea un entorno virtual de Python con venv, instala y lista paquetes en él con python -m pip, y descubre por qué muchos Python del sistema rechazan pip install.

Lección gratis, respuestas Pro: 20 minutos
Gratis: la lección completa. Con un pase: las respuestas modelo de sus 2 preguntas de entrevista.

Qué vas a aprender

  • Crear, activar y borrar un entorno virtual con venv
  • Instalar, listar y quitar paquetes con python -m pip
  • Detectar desde un programa si se ejecuta en un entorno virtual
  • Explicar por qué muchos Python del sistema rechazan pip install
  • Reconstruir un entorno a partir de requirements.txt en lugar de moverlo

Antes de empezar

Qué vas a aprender · Antes de empezar

Qué vas a aprender

  • Crear, activar y borrar un entorno virtual con venv
  • Instalar, listar y quitar paquetes con python -m pip
  • Detectar desde un programa si se ejecuta en un entorno virtual
  • Explicar por qué muchos Python del sistema rechazan pip install
  • Reconstruir un entorno a partir de requirements.txt en lugar de moverlo

Antes de empezar

En esta página

Dos proyectos en una misma computadora pueden necesitar dos versiones del mismo paquete, uno escrito para la versión 1 de una biblioteca y otro para la versión 2. Con un único Python, un solo pip install sirve a los dos, así que una actualización para un proyecto puede romper el otro, y el Python que vino con tu sistema operativo también puede ejecutar herramientas del propio sistema. Un entorno virtual da a cada proyecto una carpeta de paquetes propia.

A continuación creas un entorno y miras dentro, ves qué cambia al activarlo, instalas un paquete en él sin descargar nada, ves por qué muchos Python del sistema rechazan un pip install sin más, y reconstruyes un entorno en lugar de moverlo. Todas las órdenes se ejecutaron con el Python 3.14.8 de este curso, y la página muestra exactamente lo que imprimieron.

Un entorno virtual es una carpeta con sus propios paquetes

Un entorno virtual es una carpeta, por convención llamada .venv y guardada en la propia carpeta del proyecto. Dentro importan tres cosas:

  • pyvenv.cfg, un pequeño archivo de texto que nombra el Python a partir del cual se creó el entorno, su Python base;
  • bin/python (Scripts\python.exe en Windows), la propia orden python del entorno;
  • lib/python3.14/site-packages (Lib\site-packages en Windows), adonde van los paquetes que instalas para este proyecto.
Una carpeta de proyecto contiene main.py y .venv, cuyo python lee pyvenv.cfg, que nombra el Python base, e importa sus propios paquetes.Carpeta de tu proyectoPython 3.14 baseel intérprete yla biblioteca estándarmain.pytu propio código.venv: el entorno virtualbin/python(Scripts\python.exeen Windows)site-packageslos paquetesde este proyectopyvenv.cfgindica elPython basese ejecuta conimporta deleeindica

Qué hay dentro de un entorno virtual

Descripción en texto del diagrama

El diagrama muestra, de arriba abajo, una carpeta de proyecto y, debajo y fuera de ella, el Python base.

  • La carpeta del proyecto contiene tu propio código, main.py, y el entorno virtual, una carpeta llamada .venv.
  • main.py se ejecuta con la propia orden python del entorno, bin/python (Scripts\python.exe en Windows).
  • Ese python lee pyvenv.cfg e importa los paquetes de la carpeta site-packages del entorno (lib/python3.14/site-packages, o Lib\site-packages en Windows), donde pip instala los paquetes de este proyecto.
  • pyvenv.cfg nombra el Python 3.14 base a partir del cual se creó el entorno. El python del entorno es un enlace a ese intérprete (en Windows, un pequeño lanzador que lo inicia) y usa su biblioteca estándar: solo los paquetes pertenecen al entorno.

Crea uno con python3 -m venv .venv en la carpeta del proyecto, o py -m venv .venv en Windows. -m venv ejecuta el módulo venv del Python que escribiste, así que el entorno se construye sobre ese Python y mantiene su versión. La orden no imprime nada cuando funciona. En Debian necesita antes un paquete más: Debian distribuye venv en un paquete propio, python3-venv, y la documentación de Ubuntu incluye venv entre lo que añade su paquete python3-full. Este programa crea un entorno y mira dentro:

Crear un entorno y mirar dentro

look_inside.py

import sys
from pathlib import Path

from session import run

# The environment's programs are in .venv/bin (.venv\Scripts on Windows).
run("python3 -m venv .venv", sys.executable, "-m", "venv", ".venv")
print("In .venv:", sorted(p.name for p in Path(".venv").iterdir()))
print("pyvenv.cfg says:")
print(Path(".venv/pyvenv.cfg").read_text(), end="")
packages = Path(".venv/lib/python3.14/site-packages")
print("site-packages holds:", sorted(p.name for p in packages.iterdir()))
print(".gitignore holds:", Path(".venv/.gitignore").read_text().splitlines()[-1])

check = "import sys; print(sys.prefix != sys.base_prefix)"
run(f".venv/bin/python -c '{check}'", ".venv/bin/python", "-c", check)
run(f"python3 -c '{check}'", sys.executable, "-c", check)

Salida

$ python3 -m venv .venv
In .venv: ['.gitignore', 'bin', 'include', 'lib', 'pyvenv.cfg']
pyvenv.cfg says:
home = …/bin
include-system-site-packages = false
version = 3.14.8
executable = …/bin/python3.14
command = …/bin/python3 -m venv .venv
site-packages holds: ['pip', 'pip-26.2.1.dist-info']
.gitignore holds: *
$ .venv/bin/python -c 'import sys; print(sys.prefix != sys.base_prefix)'
True
$ python3 -c 'import sys; print(sys.prefix != sys.base_prefix)'
False

Salida registrada con Python 3.14.8 en macOS 26 arm64. Para ejecutarlo tú: mise exec python@3.14.8 -- python3 look_inside.py

session.py

"""Runs a command as if you had typed it in a terminal, and prints what it printed."""
import os
import subprocess

# PIP_DISABLE_PIP_VERSION_CHECK: pip does not look online for a newer pip,
# so what these examples print does not depend on the network.
ENV = {**os.environ, "PIP_DISABLE_PIP_VERSION_CHECK": "1"}


def run(typed, *command):
    """Show the command as you would type it, run it, and print its output."""
    print("$", typed)
    result = subprocess.run(command, capture_output=True, text=True, env=ENV)
    print(result.stdout + result.stderr, end="")
    return result

Hay cuatro cosas que ver en esa salida. La biblioteca estándar no se copió: site-packages solo contiene pip, el único paquete con el que empieza un entorno nuevo, en la versión que incluye este Python, 26.2.1. pyvenv.cfg nombra la carpeta del Python base en home (la página acorta esa carpeta a …), y include-system-site-packages = false deja fuera los paquetes propios del Python base. venv escribió un .gitignore que contiene *, así que Git ignora toda la carpeta, cosa que venv hace desde Python 3.13. Y sys.prefix != sys.base_prefix es True dentro del entorno y False fuera: la documentación de venv da esta comparación como la forma de que un programa lo sepa.

¿Cómo sabe .venv/bin/python que pertenece a un entorno? Recorre un arranque y una importación:

Interactivo · paso a paso Cómo encuentra sus paquetes el python del entorno Recorre un arranque de .venv/bin/python y una importación, de pyvenv.cfg al paquete.
Cómo encuentra sus paquetes el python del entorno, paso 6 de 6. Se ve: .venv/bin/python (sys.prefix es .venv), Python 3.14 base, .venv/pyvenv.cfg, Biblioteca estándar, site-packages de .venv, site-packages base.enlaza auna carpeta más arriba.venv/bin/pythonsys.prefix es .venvPython 3.14 baseel intérprete.venv/pyvenv.cfghome = …/binBiblioteca estándarjson, pathlib y mássite-packages de .venvlos paquetes de este proyectosite-packages baseno buscado

El dibujo muestra la escena completa; sus 6 pasos aparecen abajo.

Todos los pasos en texto
  1. Inicias .venv/bin/python. En macOS y Linux, .venv/bin/python suele ser un enlace al intérprete del Python base (en Windows, un pequeño lanzador que lo inicia), así que lo que se ejecuta es el intérprete base.
  2. Encuentra pyvenv.cfg. Al arrancar, Python busca pyvenv.cfg junto a su programa y una carpeta más arriba. Encuentra .venv/pyvenv.cfg, así que sys.prefix pasa a ser .venv mientras que sys.base_prefix sigue siendo el Python base.
  3. La biblioteca estándar se comparte. json, pathlib y el resto de la biblioteca estándar siguen viniendo del Python base, y por eso crear un entorno tarda segundos y no copia ninguno de ellos.
  4. Los paquetes vienen de .venv. Los paquetes se buscan en la propia carpeta site-packages del entorno, así que se puede importar todo lo que pip instaló en este entorno.
  5. Los paquetes base se quedan fuera. pyvenv.cfg dice include-system-site-packages = false, así que no se busca en la carpeta site-packages propia del Python base. Un paquete instalado ahí es invisible aquí.
  6. Un proyecto, un conjunto de paquetes. El .venv de otro proyecto tiene su propio site-packages y el mismo Python base. Los dos comparten el intérprete y la biblioteca estándar, y nunca los paquetes del otro.

Idea clave

Un entorno virtual es una carpeta de paquetes con un pyvenv.cfg que apunta a un Python base: el intérprete y la biblioteca estándar son los del Python base, y solo los paquetes pertenecen al entorno.

Punto de control

Comprueba: qué es un entorno virtual

  1. Una pregunta Un programa imprime sys.prefix y sys.base_prefix, y las dos carpetas son distintas. ¿Qué te dice eso?

    Elige una respuesta.

    Ver la respuesta: una pregunta

    Respuesta: Se ejecuta dentro de un entorno virtual, creado a partir del Python de sys.base_prefix

    Un python que encuentra un pyvenv.cfg junto a él o una carpeta más arriba asigna a sys.prefix la carpeta del entorno y mantiene en sys.base_prefix la del Python base. La documentación de venv da sys.prefix != sys.base_prefix como la comprobación.

Activarlo pone el python del entorno el primero en PATH

Typing .venv/bin/python every time is long. Activating the environment puts its bin folder (Scripts on Windows) first on your PATH: The environment variable that lists the folders a shell searches, in order, for a command you type. A command whose folder is not on PATH answers "command not found" until the folder is added or the full path is typed., so that python, python3 and pip mean the environment’s until you type deactivate or close the terminal. Here is a bash shell before, during and after:

Qué cambian activate y deactivate Python · activate_session.py
import subprocess
import sys

from session import run

run("python3 -m venv .venv", sys.executable, "-m", "venv", ".venv")

# The commands below run in one bash shell, one after another, as if typed.
commands = [
    "command -v python3",
    "source .venv/bin/activate",
    "command -v python3",
    'echo "$PS1"',
    "deactivate",
    "command -v python3",
]
script = "".join(f"echo '$ {line}'; {line}\n" for line in commands)
shell = subprocess.run(["bash", "-c", script], capture_output=True, text=True)
print(shell.stdout + shell.stderr, end="")

Salida

$ python3 -m venv .venv
$ command -v python3
…/bin/python3
$ source .venv/bin/activate
$ command -v python3
.venv/bin/python3
$ echo "$PS1"
(.venv) 
$ deactivate
$ command -v python3
…/bin/python3

Salida registrada con Python 3.14.8 en macOS 26 arm64. Para ejecutarlo tú: mise exec python@3.14.8 -- python3 activate_session.py

Antes de activar, python3 era el Python base, el de este curso (acortado a …). Después de source .venv/bin/activate, el shell encontró primero .venv/bin/python3, y el indicador empezaba por (.venv), que es como puedes ver que hay un entorno activo. deactivate devolvió las dos cosas a su estado. La orden para activar depende de tu shell:

Shell Se activa con
bash o zsh (macOS, Linux) source .venv/bin/activate
fish source .venv/bin/activate.fish
csh o tcsh source .venv/bin/activate.csh
PowerShell (Windows) .venv\Scripts\Activate.ps1
Símbolo del sistema (Windows) .venv\Scripts\activate.bat

Si PowerShell se niega a ejecutar Activate.ps1, la documentación de venv da la orden que permite esos scripts para tu propio usuario: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser.

Activar es una comodidad, no un requisito. La ruta completa del intérprete del entorno ejecuta un programa en el entorno sin activar nada: .venv/bin/python main.py, o .venv\Scripts\python main.py en Windows. Así es como usan un entorno los editores y las tareas programadas.

Punto de control

Comprueba: activar un entorno

  1. Una pregunta ¿Qué cambia source .venv/bin/activate?

    Elige una respuesta.

    Ver la respuesta: una pregunta

    Respuesta: Pone .venv/bin la primera en PATH, así que python, python3 y pip significan los del entorno hasta que lo desactivas

    Activar es una comodidad del shell: pone la carpeta bin del entorno la primera en PATH y añade (.venv) al indicador. La ruta completa, .venv/bin/python, usa el entorno sin activarlo.

pip instala en el entorno del python que lo ejecuta

python -m pip ejecuta el pip de ese python, así que instala en el entorno de ese python. El programa de abajo primero construye un pequeño archivo de paquete, un wheel: Un paquete construido, un archivo zip con la extensión .whl, a menudo uno por plataforma. pip instala un wheel desempaquetando sus archivos, sin ejecutar el código del propio paquete. Más en “Buscar e instalar paquetes de terceros” (en inglés) hecho por make_wheel.py, para que la instalación no necesite descargar nada: --no-index --find-links wheels le dice a pip que busque solo en esa carpeta. Instalar desde PyPI: El Python Package Index, pypi.org, donde se publican la mayoría de los paquetes de Python de terceros. pip instala desde él salvo que se le diga que use otro índice o una carpeta de archivos. Más en “Buscar e instalar paquetes de terceros” (en inglés) funciona igual, con una descarga:

Instalar en el entorno, y solo ahí

install_session.py

import sys

from make_wheel import make_wheel
from session import run

make_wheel("wheels")  # a package file to install from, so no download is needed
py = ".venv/bin/python"
run("python3 -m venv .venv", sys.executable, "-m", "venv", ".venv")
run(f"{py} -m pip --version", py, "-m", "pip", "--version")
run(
    f"{py} -m pip install --no-index --find-links wheels marksheet",
    py, "-m", "pip", "install", "--no-index", "--find-links", "wheels", "marksheet",
)
run(f"{py} -m pip list", py, "-m", "pip", "list")
where = "import marksheet; print(marksheet.__file__)"
run(f"{py} -c '{where}'", py, "-c", where)
run("python3 -c 'import marksheet'", sys.executable, "-c", "import marksheet")
run(f"{py} -m pip uninstall -y marksheet", py, "-m", "pip", "uninstall", "-y", "marksheet")

Salida

$ python3 -m venv .venv
$ .venv/bin/python -m pip --version
pip 26.2.1 from .venv/lib/python3.14/site-packages/pip (python 3.14)
$ .venv/bin/python -m pip install --no-index --find-links wheels marksheet
Looking in links: wheels
Processing ./wheels/marksheet-1.0.0-py3-none-any.whl
Installing collected packages: marksheet
Successfully installed marksheet-1.0.0
$ .venv/bin/python -m pip list
Package   Version
--------- -------
marksheet 1.0.0
pip       26.2.1
$ .venv/bin/python -c 'import marksheet; print(marksheet.__file__)'
.venv/lib/python3.14/site-packages/marksheet.py
$ python3 -c 'import marksheet'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import marksheet
ModuleNotFoundError: No module named 'marksheet'
$ .venv/bin/python -m pip uninstall -y marksheet
Found existing installation: marksheet 1.0.0
Uninstalling marksheet-1.0.0:
  Successfully uninstalled marksheet-1.0.0

Salida registrada con Python 3.14.8 en macOS 26 arm64. Para ejecutarlo tú: mise exec python@3.14.8 -- python3 install_session.py

make_wheel.py

"""Builds marksheet-1.0.0-py3-none-any.whl, a tiny package, so that no download is needed.

A wheel is a zip file: the package's code, and a NAME-VERSION.dist-info folder that
says what the package is and which commands it adds.
"""
import base64
import hashlib
import zipfile
from pathlib import Path

CODE = '''def grade(mark):
    return "pass" if mark >= 35 else "fail"


def main():
    print("72 is a", grade(72))
'''


def make_wheel(folder):
    info = "marksheet-1.0.0.dist-info"
    files = {
        "marksheet.py": CODE,
        f"{info}/METADATA": "Metadata-Version: 2.4\nName: marksheet\nVersion: 1.0.0\n",
        f"{info}/WHEEL": "Wheel-Version: 1.0\nGenerator: make_wheel.py\nRoot-Is-Purelib: true\nTag: py3-none-any\n",
        f"{info}/entry_points.txt": "[console_scripts]\nmarksheet = marksheet:main\n",
    }
    record = []
    for name, text in files.items():
        digest = base64.urlsafe_b64encode(hashlib.sha256(text.encode()).digest())
        record.append(f"{name},sha256={digest.rstrip(b'=').decode()},{len(text.encode())}")
    files[f"{info}/RECORD"] = "\n".join(record + [f"{info}/RECORD,,"]) + "\n"
    Path(folder).mkdir(exist_ok=True)
    wheel = Path(folder, "marksheet-1.0.0-py3-none-any.whl")
    with zipfile.ZipFile(wheel, "w") as archive:
        for name, text in files.items():
            archive.writestr(name, text)
    return wheel

pip --version nombra la carpeta desde la que se ejecuta su pip, el site-packages del entorno, y el paquete también acabó ahí, como .venv/lib/python3.14/site-packages/marksheet.py. Así que el python del entorno lo importa, y el python3 base se detiene con ModuleNotFoundError: ese aislamiento es para lo que sirve un entorno virtual. Con el entorno activo, un python -m pip sin más significa lo mismo:

Tarea Orden
Instalar un paquete python -m pip install requests
Instalar un rango de versiones python -m pip install "requests>=2,<3"
Actualizar un paquete python -m pip install --upgrade requests
Listar lo que hay instalado python -m pip list
Quitar un paquete python -m pip uninstall requests
Guardar las versiones exactas python -m pip freeze > requirements.txt
Instalar desde ese archivo python -m pip install -r requirements.txt

Pon entre comillas un rango de versiones, como en la segunda fila: sin las comillas, el shell lee < y > como redirecciones. Como mostró la lección del REPL, python -m pip también asegura que se ejecuta el pip del intérprete correcto, mientras que un pip sin más puede pertenecer a otro Python de tu PATH.

Seguridad

A package you install runs its code on your computer whenever your program uses it. Check the spelling of the name and the project’s page on pypi.org before you install: with typosquatting: Registering a package name one slip away from a popular one, such as a letter dropped, two letters swapped or a separator removed, so that a typo installs someone else's code., a name one slip away from a popular package, a typo installs someone else’s code.

Punto de control

Comprueba: instalar en un entorno

  1. Pregunta 1 de 2 Sin ningún entorno activo, ejecutas .venv/bin/python -m pip install rich. ¿Dónde se instala rich?

    Elige una respuesta.

    Ver la respuesta: pregunta 1 de 2

    Respuesta: En el site-packages del entorno, .venv/lib/python3.14/site-packages

    El python que escribes decide dónde instala pip. .venv/bin/python -m pip instala en .venv, esté activado o no, igual que hizo install_session.py con marksheet.

  2. Pregunta 2 de 2 Después de instalar marksheet en .venv, ejecutas python3 -c "import marksheet" con el python3 base y sin ningún entorno activo. ¿Qué pasa?

    Elige una respuesta.

    Ver la respuesta: pregunta 2 de 2

    Respuesta: ModuleNotFoundError, porque el Python base no busca en el site-packages de .venv

    Cada python busca en su propio site-packages. El python3 base no sabe nada de .venv, así que la importación falla con ModuleNotFoundError, justo como imprimió install_session.py.

Muchos Python del sistema rechazan pip install fuera de un entorno

Ejecuta python3 -m pip install requests con el Python de Debian 12 o posterior, o con el de Homebrew en un Mac, fuera de cualquier entorno, y pip se detiene con error: externally-managed-environment y This environment is externally managed. Ese Python pertenece al gestor de paquetes del sistema, que instala él mismo sus paquetes; un pip install podría sustituir un paquete que necesita una herramienta del sistema. La regla es un estándar de empaquetado, de la PEP 668. Recorre la comprobación que hace pip 26.2.1 antes de instalar:

Interactivo · paso a paso ¿pip instala o se niega? Recorre las dos preguntas que se hace pip antes de instalar, para un Python del sistema y para un entorno virtual.
¿pip instala o se niega?, paso 6 de 6. Se ve: pip install requests, ¿En un entorno? (prefix ≠ base_prefix), Instala el paquete, ¿Encuentra el archivo de marca? (aquí no hay archivo), Se niega con un error. Resaltado: ¿Encuentra el archivo de marca?.yesnoyesno hay archivopip install requestscon el python3 del sistema¿En un entorno?prefix ≠ base_prefixInstala el paquete¿Encuentra el archivo de marca?aquí no hay archivoSe niega con un errorno hay archivo

El dibujo muestra la escena completa; sus 6 pasos aparecen abajo.

Todos los pasos en texto
  1. Fuera de cualquier entorno. Ejecutas python3 -m pip install requests con el Python de Debian 12 o de Homebrew, sin ningún entorno virtual activo.
  2. Primera pregunta. pip pregunta si se ejecuta dentro de un entorno virtual, comparando sys.prefix con sys.base_prefix. Aquí son la misma carpeta, así que la respuesta es no.
  3. Segunda pregunta. Busca un archivo llamado EXTERNALLY-MANAGED en la carpeta de la biblioteca estándar. El gestor de paquetes del sistema puso uno ahí, para decir que él mismo instala los paquetes de este Python.
  4. pip se niega. pip se detiene con error: externally-managed-environment y muestra el mensaje de ese archivo, que normalmente dice que crees un entorno virtual. No se instaló ni se cambió nada.
  5. Dentro de un entorno virtual. Ejecuta la misma orden con .venv/bin/python y la primera respuesta es sí. pip instala en el entorno y nunca busca el archivo, y por eso un entorno siempre funciona.
  6. Un Python sin el archivo. Un Python sin ese archivo, como el de este curso instalado con mise, deja que pip instale, pero en el único Python base que comparten todos los proyectos. Un entorno sigue siendo mejor sitio.

Un programa puede hacer la misma comprobación que hace pip. El Python 3.14.8 de este curso lo instaló un gestor de versiones, mise, no el gestor de paquetes de un sistema, así que todas las respuestas son False: nada lo marca como gestionado.

¿Se negaría pip a instalar aquí? Python · managed.py
import sys
import sysconfig
from pathlib import Path

in_venv = sys.prefix != sys.base_prefix
marker = Path(sysconfig.get_path("stdlib"), "EXTERNALLY-MANAGED")

print("Python", sys.version.split()[0])
print("Inside a virtual environment:", in_venv)
print("Marked as externally managed:", marker.exists())
print("A plain pip install would be refused:", marker.exists() and not in_venv)

Salida

Python 3.14.8
Inside a virtual environment: False
Marked as externally managed: False
A plain pip install would be refused: False

Salida registrada con Python 3.14.8 en macOS 26 arm64. Para ejecutarlo tú: mise exec python@3.14.8 -- python3 managed.py

Ejecuta el mismo archivo con el python3 de Debian 12 o de Homebrew y la tercera línea dice True, igual que la cuarta, salvo que lo ejecutes desde un entorno virtual, donde pip siempre puede instalar. Cuando te encuentres con el error:

  • Para todo lo que importa tu propio código, crea un entorno virtual, como en esta lección.
  • Para una aplicación de línea de órdenes que quieres tener en todas partes, instálala con pipx, la herramienta de la lección siguiente, que da a cada aplicación un entorno propio.
  • Para una biblioteca que empaqueta el propio sistema, instala el paquete del sistema con su gestor de paquetes, como sugiere el mensaje del Python de Debian (apt install python3-xyz).

Atención

No uses sudo pip install, y mantente lejos de la opción --break-system-packages de pip, que, como dice su documentación, permite que pip modifique una instalación gestionada externamente. Las dos cosas cambian archivos de los que dependen las herramientas del propio sistema operativo.

Punto de control

Comprueba: Python del sistema que rechazan pip

  1. Una pregunta En Debian 12, python3 -m pip install requests para tu propio script se detiene con error: externally-managed-environment. ¿Qué haces?

    Elige una respuesta.

    Ver la respuesta: una pregunta

    Respuesta: Crear un entorno virtual para el script e instalar requests en él

    La marca dice que el gestor de paquetes del sistema es el dueño de este Python. Para el código que escribes, la respuesta es un entorno virtual; las aplicaciones de línea de órdenes van a pipx, y las bibliotecas del sistema vienen del gestor de paquetes.

Borra y reconstruye un entorno, nunca lo muevas

No hay nada tuyo en .venv, así que borrarlo es seguro: elimina la carpeta (rm -rf .venv en macOS y Linux, Remove-Item -Recurse .venv en PowerShell, o con tu gestor de archivos). Antes, anota las versiones exactas en un archivo de requisitos: Un archivo de texto, normalmente requirements.txt, con un requisito por línea, que instala pip install -r. pip freeze escribe uno que enumera cada paquete instalado con su versión exacta. Más en “Buscar e instalar paquetes de terceros” (en inglés) con pip freeze, para que un entorno nuevo pueda volver a tener los mismos paquetes.

Lo que rompe las cosas es moverlo. Cada orden que instala pip, como marksheet abajo, es un pequeño script cuya primera línea, #!, contiene la ruta completa del python del entorno, así que después de moverlo esa ruta ya no existe. El programa muestra el ciclo completo, de congelar a reconstruir:

Congelar, mover, romper y reconstruir Python · move_and_rebuild.py
import os
import shutil
import subprocess
import sys
from pathlib import Path

from make_wheel import make_wheel
from session import run


def first_line(script):
    """Where the #! line of a script that pip installed points, and whether that python exists."""
    python = script.read_text().splitlines()[0].removeprefix("#!")
    print(f"  {script} starts with #! and the full path of {os.path.relpath(python)};",
          "it exists:", Path(python).exists())


make_wheel("wheels")
py = ".venv/bin/python"
find = ["--no-index", "--find-links", "wheels"]
run("python3 -m venv .venv", sys.executable, "-m", "venv", ".venv")
run(f"{py} -m pip install --no-index --find-links wheels marksheet", py, "-m", "pip", "install", *find, "marksheet")
frozen = run(f"{py} -m pip freeze", py, "-m", "pip", "freeze").stdout
Path("requirements.txt").write_text(frozen)  # what pip freeze > requirements.txt does
first_line(Path(".venv/bin/marksheet"))

print("$ mv .venv moved")
shutil.move(".venv", "moved")
first_line(Path("moved/bin/marksheet"))
print("$ moved/bin/marksheet")
try:
    subprocess.run(["moved/bin/marksheet"], check=True)
except FileNotFoundError as error:  # the python its #! line names is gone
    print("  it does not start:", error.strerror)

print("$ rm -rf moved")
shutil.rmtree("moved")
run("python3 -m venv .venv", sys.executable, "-m", "venv", ".venv")
run(f"{py} -m pip install --no-index --find-links wheels -r requirements.txt",
    py, "-m", "pip", "install", *find, "-r", "requirements.txt")
run(".venv/bin/marksheet", ".venv/bin/marksheet")

Salida

$ python3 -m venv .venv
$ .venv/bin/python -m pip install --no-index --find-links wheels marksheet
Looking in links: wheels
Processing ./wheels/marksheet-1.0.0-py3-none-any.whl
Installing collected packages: marksheet
Successfully installed marksheet-1.0.0
$ .venv/bin/python -m pip freeze
marksheet==1.0.0
  .venv/bin/marksheet starts with #! and the full path of .venv/bin/python; it exists: True
$ mv .venv moved
  moved/bin/marksheet starts with #! and the full path of .venv/bin/python; it exists: False
$ moved/bin/marksheet
  it does not start: No such file or directory
$ rm -rf moved
$ python3 -m venv .venv
$ .venv/bin/python -m pip install --no-index --find-links wheels -r requirements.txt
Looking in links: wheels
Processing ./wheels/marksheet-1.0.0-py3-none-any.whl (from -r requirements.txt (line 1))
Installing collected packages: marksheet
Successfully installed marksheet-1.0.0
$ .venv/bin/marksheet
72 is a pass

Salida registrada con Python 3.14.8 en macOS 26 arm64. Para ejecutarlo tú: mise exec python@3.14.8 -- python3 move_and_rebuild.py

La orden movida seguía nombrando .venv/bin/python, que ya no existía, así que no arrancó (la página escribe esa ruta completa relativa a la carpeta del ejemplo). El entorno reconstruido a partir de requirements.txt vuelve a funcionar, con el mismo marksheet 1.0.0. La documentación de venv dice lo mismo: los entornos no se pueden mover ni copiar, así que se vuelven a crear en el nuevo lugar.

Subir o copiar .venv

Meter .venv en Git, o copiarlo a otra computadora, distribuye una carpeta llena de rutas que solo tienen sentido en la computadora que la creó. Sube requirements.txt (o el pyproject.toml del proyecto, que presenta la lección siguiente) y deja que cada persona construya su propio entorno a partir de él.

Generador de .gitignore (en inglés) Crea un .gitignore para un proyecto de Python que deje .venv y __pycache__ fuera de Git.

Punto de control

Comprueba: reconstruir en lugar de mover

  1. Una pregunta Mueves la carpeta de un proyecto, con su .venv dentro, a otro lugar del disco. ¿Qué deja de funcionar?

    Elige una respuesta.

    Ver la respuesta: una pregunta

    Respuesta: Las órdenes que instaló pip, porque su primera línea nombra la ruta antigua del python del entorno

    Las órdenes instaladas contienen la ruta completa del python del entorno en su línea #!, así que las órdenes de un entorno movido ya no arrancan. Bórralo y reconstrúyelo a partir de requirements.txt en el nuevo lugar.

Practica: comprobar un entorno

Dos funciones cortas usan las ideas de esta lección: una dice si un programa se ejecuta en un entorno virtual, y la otra lee las versiones que anotó pip freeze. Los dos pasos vienen con código inicial, una primera pista y pruebas de ejemplo que puedes ejecutar aquí en la página, así que hazlos en el orden que quieras:

Laboratorio de código · 2 pasos

Comprobar un entorno

Averigua si un Python se ejecuta en un entorno virtual, y después lee las versiones exactas que anotó pip freeze.

  1. Averiguar si un Python se ejecuta en un entorno virtual Por hacer
  2. Leer las versiones que anotó pip freeze Por hacer

Ejercicio · Fácil · Python

Averiguar si un Python se ejecuta en un entorno virtual

Escribe in_virtualenv(sys_module). Recibe un objeto con los atributos prefix y base_prefix, como el propio módulo sys, y devuelve True cuando el Python que describe se ejecuta dentro de un entorno virtual y False cuando es un Python base.

  • Dentro de un entorno, prefix es la carpeta del entorno y base_prefix la del Python base, así que son distintas.
  • En un Python base son la misma carpeta.
  • Las pruebas pasan objetos falsos además del sys real, así que lee los atributos de sys_module, no los de sys.

Código inicial · venvcheck.py

def in_virtualenv(sys_module):
    """Return True if the Python that sys_module describes runs in a virtual environment."""
    return None
Las pruebas de ejemplo · test_venvcheck.py
import sys
from types import SimpleNamespace

from venvcheck import in_virtualenv


def test_inside_an_environment():
    """a prefix that differs from base_prefix means a virtual environment"""
    fake = SimpleNamespace(prefix="/home/asha/marks/.venv", base_prefix="/usr")
    assert in_virtualenv(fake) is True


def test_base_python():
    """the same prefix and base_prefix means the base Python itself"""
    fake = SimpleNamespace(prefix="/usr", base_prefix="/usr")
    assert in_virtualenv(fake) is False


def test_windows_paths():
    """works the same with Windows paths"""
    fake = SimpleNamespace(prefix="C:\\marks\\.venv", base_prefix="C:\\Python314")
    assert in_virtualenv(fake) is True


def test_this_python():
    """agrees with the sys module of the Python that runs the tests"""
    assert in_virtualenv(sys) == (sys.prefix != sys.base_prefix)
Una pista

Toda la comprobación es una comparación de sys_module.prefix con sys_module.base_prefix. La comparación ya da True o False, así que devuélvela directamente.

Las pruebas de ejemplo se ejecutan en este dispositivo, en tu navegador (Pyodide): no se envía nada a mysmartcopilot.com. La primera ejecución descarga Python (unos 13.5 MB), que se guarda para las siguientes. Una comprobación en tu navegador es una orientación para ti, no la prueba de que el código sea correcto con cualquier entrada.

Ejercicio · Fácil · Python

Leer las versiones que anotó pip freeze

Escribe pinned_versions(text). text es lo que imprimió pip freeze (el contenido de un requirements.txt), y la función devuelve un dict que asocia el nombre de cada paquete con su versión exacta.

  • Una línea como marksheet==1.0.0 da {"marksheet": "1.0.0"}.
  • Las líneas vacías y los comentarios (las líneas que empiezan por #) se saltan, igual que los espacios alrededor de una línea.
  • Un paquete instalado directamente desde un archivo aparece como marksheet @ file:///…/marksheet-1.0.0-py3-none-any.whl#sha256=…, sin ==: sáltate también esa línea, porque anota un archivo, no una versión.
  • Un texto vacío da un dict vacío.

Código inicial · frozen.py

def pinned_versions(text):
    """Return {name: version} for every name==version line of pip freeze's output."""
    versions = {}
    return versions
Las pruebas de ejemplo · test_frozen.py
from frozen import pinned_versions


def test_two_pins():
    """reads every name==version line"""
    text = "marksheet==1.0.0\ntablekit==2.3.1\n"
    assert pinned_versions(text) == {"marksheet": "1.0.0", "tablekit": "2.3.1"}


def test_blank_lines_and_comments():
    """skips blank lines and comments"""
    text = "# written by pip freeze\n\nmarksheet==1.0.0\n"
    assert pinned_versions(text) == {"marksheet": "1.0.0"}


def test_spaces_around_a_line():
    """ignores spaces around a line"""
    assert pinned_versions("  marksheet==1.0.0  \n") == {"marksheet": "1.0.0"}


def test_a_file_instead_of_a_version():
    """skips a line that records a file instead of a version"""
    text = "marksheet @ file:///home/asha/wheels/marksheet-1.0.0-py3-none-any.whl\ntablekit==2.3.1\n"
    assert pinned_versions(text) == {"tablekit": "2.3.1"}


def test_empty_text():
    """gives an empty dict for empty text"""
    assert pinned_versions("") == {}
Una pista

Recorre text.splitlines(), aplica strip() a cada línea y usa continue cuando está vacía, empieza por # o no tiene ==. Después, line.split("==", 1) da el nombre y la versión.

Las pruebas de ejemplo se ejecutan en este dispositivo, en tu navegador (Pyodide): no se envía nada a mysmartcopilot.com. La primera ejecución descarga Python (unos 13.5 MB), que se guarda para las siguientes. Una comprobación en tu navegador es una orientación para ti, no la prueba de que el código sea correcto con cualquier entrada.

Cuando pasen los dos pasos, prueba las preguntas de abajo. Eligen la idea de esta lección que menos dominas, y la explicación de una opción equivocada dice qué se le escapa:

Práctica que se adapta a ti

Practica esta lección

11 preguntas sobre 5 ideas de esta lección. Cada respuesta se explica al momento, y lo que aciertas se guarda en este navegador.

  1. Pregunta 1 de 11 ¿Qué pone python3 -m venv .venv en la carpeta nueva?

    Elige una respuesta.

    Ver la respuesta: pregunta 1 de 11

    Respuesta: Un pyvenv.cfg, una orden python que inicia el intérprete base y una carpeta site-packages con pip dentro

    Un entorno nuevo contiene pyvenv.cfg, una orden python (un enlace al intérprete base en macOS y Linux, un lanzador en Windows), los scripts de activación y una carpeta site-packages que empieza solo con pip.

  2. Pregunta 2 de 11 Borras .venv/pyvenv.cfg y ejecutas .venv/bin/python -c "import sys; print(sys.prefix != sys.base_prefix)". ¿Qué imprime?

    Elige una respuesta.

    Una pista

    ¿Qué archivo le dice a un python que arranca que pertenece a un entorno?

    Ver la respuesta: pregunta 2 de 11

    Respuesta: False

    Un python se convierte en el python de un entorno al encontrar pyvenv.cfg junto a su programa o una carpeta más arriba. Sin el archivo, .venv/bin/python arranca como el Python base sin más, y sys.prefix es igual a sys.base_prefix.

  3. Pregunta 3 de 11 Una tarea programada ejecuta /home/asha/report/.venv/bin/python report.py y nunca activa nada. ¿Recibe report.py los paquetes del entorno?

    Elige una respuesta.

    Ver la respuesta: pregunta 3 de 11

    Respuesta: Sí, porque la ruta completa nombra el python del entorno

    Llamar al intérprete del entorno por su ruta completa es la forma fiable de que los editores y las tareas usen un entorno: sin activar nada y sin depender de PATH.

  4. Pregunta 4 de 11 ¿Qué hace deactivate?

    Elige una respuesta.

    Ver la respuesta: pregunta 4 de 11

    Respuesta: Devuelve PATH y el indicador a su estado, así que python vuelve a significar el Python base

    deactivate deshace la activación en el shell actual: PATH y el indicador vuelven a lo que eran, y el propio entorno no cambia.

  5. Pregunta 5 de 11 ¿Por qué python -m pip install … es más seguro que un pip install … sin más?

    Elige una respuesta.

    Ver la respuesta: pregunta 5 de 11

    Respuesta: Ejecuta el pip de ese python, así que el paquete va adonde ese python lo busca

    python -m pip ata pip a un intérprete. Con varios Python instalados, un pip sin más puede instalar en otro distinto del python que va a ejecutar tu programa.

  6. Pregunta 6 de 11 Con .venv activo, quieres ver lo que hay instalado en él y después volver a quitar marksheet. ¿Qué dos órdenes lo hacen?

    Elige una respuesta.

    Ver la respuesta: pregunta 6 de 11

    Respuesta: python -m pip list, y después python -m pip uninstall marksheet

    python -m pip list muestra los paquetes del entorno cuyo python lo ejecuta, y python -m pip uninstall vuelve a quitar uno. install_session.py ejecutó las dos, con -y en uninstall para que no esperara un sí.

  7. Pregunta 7 de 11 ¿Por qué python -m pip install requests>=2,<3 sale mal en un shell, mientras que python -m pip install "requests>=2,<3" funciona?

    Elige una respuesta.

    Ver la respuesta: pregunta 7 de 11

    Respuesta: Sin comillas, el shell lee > y < como redirecciones, así que pip nunca ve el requisito completo

    El shell lee > y < como redirecciones antes de que se ejecute ningún programa. Poner entre comillas el requisito completo se lo pasa a pip como un solo argumento.

  8. Pregunta 8 de 11 ¿Cómo sabe pip que un Python está gestionado externamente?

    Elige una respuesta.

    Ver la respuesta: pregunta 8 de 11

    Respuesta: Por un archivo llamado EXTERNALLY-MANAGED en la carpeta de la biblioteca estándar de ese Python

    El estándar de empaquetado pone un archivo llamado EXTERNALLY-MANAGED en la carpeta de la biblioteca estándar. Fuera de un entorno, pip lo encuentra y se niega, mostrando el mensaje que contiene el archivo.

  9. Pregunta 9 de 11 En Debian 12 creas un entorno con el python3 -m venv .venv del sistema y después ejecutas .venv/bin/python -m pip install requests. ¿Qué pasa?

    Elige una respuesta.

    Ver la respuesta: pregunta 9 de 11

    Respuesta: pip instala requests en .venv, porque dentro de un entorno no busca el archivo de marca

    Gestionado externamente significa “no cambies este Python con pip”. Un entorno construido a partir de él tiene su propio site-packages, y pip instala ahí sin quejarse.

  10. Pregunta 10 de 11 Ordena los pasos para sustituir el entorno de un proyecto por uno nuevo con los mismos paquetes.

    Da a cada elemento su posición, desde 1 (el primero).

    Ver la respuesta: pregunta 10 de 11

    Respuesta:

    1. .venv/bin/python -m pip freeze > requirements.txt
    2. rm -rf .venv
    3. python3 -m venv .venv
    4. .venv/bin/python -m pip install -r requirements.txt

    Anota primero las versiones exactas, con el propio python del entorno, mientras el entorno antiguo todavía existe; después bórralo, crea uno nuevo e instala desde el archivo. Congelar después de borrar no anotaría nada, y congelar con el python3 base anotaría en su lugar los paquetes del Python base.

  11. Pregunta 11 de 11 ¿Qué archivo subes para que un compañero pueda construir el mismo entorno?

    Elige una respuesta.

    Ver la respuesta: pregunta 11 de 11

    Respuesta: requirements.txt, con las versiones exactas que escribió pip freeze

    Sube la lista de paquetes, no la carpeta: requirements.txt (o pyproject.toml) es corto, no tiene rutas y reconstruye el entorno en cualquier sitio.

Preguntas de entrevista

Calentamiento (júnior): ¿qué es un entorno virtual, y por qué usar uno para cada proyecto?

Un entorno virtual es una carpeta, normalmente .venv dentro del proyecto, con su propia orden python y su propia carpeta site-packages, creada por python3 -m venv .venv. Su pyvenv.cfg apunta al Python base, cuyo intérprete y biblioteca estándar usa, así que solo los paquetes son suyos. Un entorno por proyecto significa que cada proyecto tiene exactamente las versiones que necesita: uno puede usar la versión 1 de una biblioteca mientras otro usa la 2, una actualización en uno no puede romper el otro, y el propio Python del sistema queda intacto. El entorno se puede borrar y reconstruir a partir de requirements.txt en cualquier momento, y por eso nunca se sube al repositorio: se sube la lista de paquetes, no la carpeta.

Dos preguntas más difíciles parten de esta lección: un servidor que rechaza pip install, y una tarea programada que no encuentra un paquete que tu terminal sí encuentra. Piensa tu primer paso antes de abrir cada una:

Las respuestas modelo se abren con cualquier pase: 79 ₹ por 1 semana (2,99 US$), un solo pago que nunca se renueva.

  1. De júnior a nivel medio

    En un servidor nuevo con Debian 12, python3 -m pip install falla con error: externally-managed-environment. Un compañero sugiere sudo pip install --break-system-packages. ¿Qué haces en su lugar, para una aplicación web que ejecuta el servidor y para una herramienta de línea de órdenes que usas allí, y por qué?

    Respuesta modelo, Pro. Las respuestas modelo se abren con cualquier pase; los precios están en el aviso de esta página. Primer paso y preguntas de seguimiento (en inglés)

  2. De júnior a nivel medio

    pip install requests funcionó en tu terminal, y tu script se ejecuta ahí. El mismo script, iniciado por una tarea programada cada noche, se detiene con ModuleNotFoundError: No module named 'requests'. Explica por qué y arréglalo para que no pueda volver a pasar.

    Respuesta modelo, Pro. Primer paso y preguntas de seguimiento (en inglés)

Para recordar

  • Un entorno virtual es una carpeta (.venv) con su propio python y su propio site-packages, construida sobre un Python base cuyo intérprete y biblioteca estándar comparte; créalo con python3 -m venv .venv, o py -m venv .venv en Windows.
  • Activar pone la carpeta bin del entorno la primera en PATH; .venv/bin/python ejecuta un programa en el entorno sin activar nada.
  • python -m pip instala en el entorno del python que lo ejecuta, y solo ahí, y por eso el Python base no puede importar el paquete.
  • error: externally-managed-environment significa que el Python pertenece al sistema: usa un entorno virtual o pipx, nunca sudo pip ni --break-system-packages.
  • Anota las versiones exactas con pip freeze > requirements.txt, y reconstruye un entorno a partir de ese archivo en lugar de moverlo o copiarlo, porque los scripts instalados contienen su ruta completa.
  • sys.prefix != sys.base_prefix le dice a un programa que se ejecuta dentro de un entorno virtual.

Referencias

Herramientas relacionadas

Informar de un problema con esta lección (en inglés)

Respuestas rápidas y búsqueda de herramientas

Escribe para buscar herramientas o para obtener una respuesta rápida (en inglés), por ejemplo 18% of 2500. Usa las flechas arriba y abajo para recorrer los resultados, Intro para elegir y Escape para cerrar.