La estructura de un proyecto (2): pasa en mi máquina, falla instalado
Por Dorian Chávez · fundador y CEO de Hábil ·
Tus pruebas pasan y a quien instala tu paquete le truena. La guía de PyPA pide src/, pero la instalación editable también lee tu carpeta. Con plantilla.
La estructura de un proyecto · entrega 2 de 7 · Python
Escenario: los personajes y la historia son inventados. Los datos técnicos y las citas llevan su fuente al final.
En 1 minuto
- Si corres las pruebas desde la raíz del proyecto, Python puede encontrar tu carpeta antes que el paquete instalado. Un archivo que nunca entró al paquete pasa tus pruebas y le truena a quien lo instala [1][3].
- La guía de empaquetado de Python (PyPA) lo resuelve con una carpeta:
src/. Con ella, el código solo corre si se instala [1]. - Pero ojo: la instalación editable (
pip install -e .) también lee tu carpeta. Con hatchling,src/completa (medido) [5][6]. - Por eso la plantilla de esta entrega prueba dos veces: rápido contra tu carpeta, y en serio contra lo instalado. Y trae un guion que lo demuestra poniéndose rojo cuando debe.
- Para el lunes: corre tus pruebas contra el paquete instalado en un entorno nuevo. Si algo truena, ya sabes qué es.
En este artículo
- ¿Qué pasó el viernes a las 9:30?
- ¿Qué pide la guía de empaquetado?
- ¿Cuáles son los cinco errores más comunes?
- Ejercicio: ¿puedes repetir el error de Iván?
- Para llevar
- ¿Qué sigue?
- La plantilla y el curso
- ¿Qué hace Hábil?
- Referencias
¿Qué pasó el viernes a las 9:30?
Las pruebas de Iván pasaban todas. El paquete que instaló Sofía no tenía el archivo que esas pruebas usaban.
Jueves, 18:10. Piso tres.
Después de Go, el segundo encargo le pareció fácil: «Que el revisor de Python exporte el reporte a CSV». El revisor era el mismo programa de la semana anterior, pero en Python: leía una lista de servicios, los consultaba y decía cuáles respondían.
El repositorio, heredado otra vez, tenía este aspecto:
revisor/ el paquete
tests/
pyproject.tomlIván escribió la función en un archivo nuevo, exportar.py. Lo dejó en la raíz, junto a todo, porque ahí lo veía. En revisor/cli.py agregó una línea: import exportar. Corrió las pruebas como le habían enseñado, desde la raíz:
$ python -m pytest -q
. [100%]
1 passed in 0.00sVerde. Subió el cambio y se fue a casa.
Viernes, 9:30.
Sofía, de operación, instaló la versión nueva en su entorno con pip install . y corrió el comando. La terminal le contestó esto (las rutas, recortadas):
Traceback (most recent call last):
File ".../bin/revisor", line 3, in <module>
from revisor.cli import main
File ".../site-packages/revisor/cli.py", line 1, in <module>
import exportar
ModuleNotFoundError: No module named 'exportar'Iván abrió su carpeta: exportar.py estaba ahí. Volvió a correr las pruebas. Verde otra vez.
—En mi máquina pasa —escribió en el canal.
Lucía, la líder técnica, contestó con una pregunta:
—¿Tus pruebas probaron lo que Sofía instaló, o tu carpeta?
Iván se quedó viendo la pantalla. Nunca se le había ocurrido que fueran dos cosas distintas.
—Desde la raíz, Python ve todo lo que hay en la raíz —siguió Lucía—. Sofía no tiene tu raíz. Tiene el paquete. Y en el paquete, exportar.py nunca entró.
¿Qué pide la guía de empaquetado?
Que el código importable viva en src/, para que solo corra después de instalarse.
La guía oficial de empaquetado de Python, de la PyPA, compara los dos diseños en src layout vs flat layout [1]. En el diseño plano, el paquete vive en la raíz, junto a pyproject.toml. En el diseño src/, baja un nivel.
La diferencia que importa está en la primera línea de su lista: con src/ hay que instalar el proyecto para correr su código, y con el diseño plano no [1]. Parece una desventaja. Es el punto.
La guía explica por qué: Python pone la carpeta actual al principio de su ruta de búsqueda, así que si en ella hay un paquete con el mismo nombre que el instalado, gana el de la carpeta. Y eso, dice, puede llevar a que la configuración del empaquetado quede mal sin que nadie lo note, «which could result in files not being included in a distribution» [1]. Es lo que le pasó a Iván.
Lo de la carpeta actual tiene un matiz: no siempre entra. python -m pytest sí la agrega; pytest a secas, no [3][8]. Iván usó la primera forma, y con ella su raíz completa quedó a la vista.
La plantilla de esta entrega, estructura-python, usa el diseño src/:
estructura-python/
├── pyproject.toml el proyecto en un solo archivo
├── servicios.toml el ejemplo
├── src/
│ └── revisor/ EL PAQUETE: lo único que viaja al instalar
│ ├── __init__.py · __main__.py
│ ├── cli.py opciones y código de salida
│ ├── servicio.py el vocabulario. Sin red ni archivos
│ ├── config.py lee servicios.toml
│ ├── revisar.py consulta muchos servicios a la vez
│ ├── reporte.py tabla y JSON
│ └── py.typed
├── tests/ las pruebas: no viajan
│ ├── conftest.py
│ └── test_*.py
├── herramientas/
│ ├── _comun.sh
│ ├── probar-instalado.sh
│ └── probar-frontera.sh
├── .github/workflows/verificar.yml
└── README.md · LICENSE · LICENSE-CONTENIDO.md¿Qué decide qué viaja?
Una línea de pyproject.toml.
[tool.hatch.build.targets.wheel]
packages = ["src/revisor"]Lo que se instala es una rueda, un archivo .whl que se arma a partir del proyecto. Esa línea le dice a hatchling, la herramienta que la construye, que la rueda lleva src/revisor y nada más [6]. Sin ella, hatchling adivina: busca primero revisor/__init__.py y luego src/revisor/__init__.py [7]. La plantilla lo escribe para que se lea.
Así, todo lo que viva dentro de src/revisor/ viaja. Lo que viva fuera, no.
¿Entonces src/ ya me protege?
De tu raíz, sí. De la instalación editable, no del todo.
Para trabajar, lo cómodo es instalar en modo editable: pip install -e .. Así no copias nada; Python lee tus archivos donde están y cada cambio en el código se nota en la siguiente ejecución [5]. pip advierte que la editable y la normal pueden portarse distinto, según la herramienta de construcción, y que quien use tu paquete «will see the behaviour of regular installs» [5].
¿Y cuál es la de hatchling? Su documentación dice que, para una instalación editable, decide qué carpetas agregar a la ruta de búsqueda según los archivos seleccionados [6]. Lo medimos: agrega src/ completa. Si alguien deja un src/suelto.py fuera del paquete, la instalación editable lo encuentra y la rueda no lo lleva.
La misma página ofrece una salida, la opción dev-mode-exact [6]. Medido: con ella, import suelto da ModuleNotFoundError. Pero tiene costo, y la página lo avisa: «The dev-mode-exact mechanism is not supported by static analysis tools & IDEs» [6], así que tu editor deja de autocompletar. Y tampoco ve lo que la rueda excluye dentro del paquete (medido). Por eso la plantilla prefiere probar la rueda, que es lo que recibe quien instala.
La guía de PyPA ya lo anticipa: lo típico es usar la instalación editable para desarrollar «and a regular installation is used for testing» [1]. La plantilla hace exactamente eso:
pytest # rápido: lee tu carpeta src/
bash herramientas/probar-instalado.sh # lo que ve quien instala tu paqueteEl segundo comando crea un entorno nuevo y vacío, instala el proyecto sin -e (o sea, construye la rueda y la instala) y corre las mismas pruebas. Al final cuenta los resultados leyendo el informe de pytest, no su código de salida. Éstas son sus últimas tres líneas:
revisor bajo prueba: …/site-packages/revisor (el paquete INSTALADO)
pruebas: 62 · pasaron 62 · fallaron 0 · con error 0 · saltadas 0
✅ lo instalado pasa sus pruebas¿Por qué las pruebas viven fuera del paquete?
Para que no viajen, y para que prueben el paquete como lo vería cualquiera.
tests/ está junto a src/, no dentro. pytest corre en modo importlib, el que su documentación recomienda para proyectos nuevos [2]: en ese modo no mete carpetas del proyecto en la ruta de búsqueda para importar las pruebas [4]. Lo que se prueba es lo que esté instalado en el entorno.
¿Y cómo sabes qué está instalado? tests/conftest.py lo dice en el encabezado de cada corrida (el que pytest -q omite):
revisor bajo prueba: src/revisor (la CARPETA del proyecto)Con la variable REVISOR_EXIGIR_INSTALADO=1, además, se niega a correr si el revisor viene de tu carpeta. El guion de lo instalado la activa, y así no puede probar tu src/revisor creyendo que probó la rueda. Ojo: el guardia solo mira de dónde viene revisor. Si otro módulo de tu carpeta se cuela en las pruebas, lo ataja la frontera, que viene enseguida.
¿Qué hay en pyproject.toml?
Todo el proyecto, en un solo archivo.
La guía de PyPA describe tres tablas: [build-system], que dice con qué se construye; [project], con los datos del proyecto; y [tool], para cada herramienta [13]. La plantilla usa las tres, más una cuarta: [dependency-groups], donde viven pytest, ruff y mypy con su versión fijada. Ese grupo existe desde el PEP 735, que lo define justo así: requisitos que se guardan en pyproject.toml y que no se incluyen en ninguna distribución del proyecto [16]. Quien instala el revisor no recibe pytest.
Un detalle medido: pip install --group dev necesita pip 25.1 o más nuevo [17], y un entorno recién creado en Ubuntu 24.04, la base de Linux Mint 22 [10], trae pip 24.0 [11]. La respuesta es no such option: --group. Por eso el README pide python -m pip install --upgrade pip antes que nada.
¿Y dónde va el CSV de Iván?
En el paquete, junto a los otros formatos del reporte.
En la plantilla, el reporte ya tiene casa: reporte.py, que hoy convierte resultados en tabla o en JSON. Una función a_csv va ahí, junto a a_json, y cli.py suma csv a las opciones de --formato. Ningún archivo en la raíz. Ninguna carpeta nueva.
El lunes, Iván movió su función adentro del paquete y corrió las pruebas contra lo instalado. Verde. Esta vez, verde de verdad.
¿Cómo sé que la frontera existe?
Porque un guion la rompe a propósito y exige que lo instalado no vea lo que quedó fuera.
herramientas/probar-frontera.sh trabaja en una copia temporal del proyecto. Siembra src/suelto.py, un módulo que vive en src/ pero fuera del paquete, y una prueba que lo importa. Luego corre la suite completa dos veces, en dos entornos nuevos con el mismo Python, la misma copia y la misma versión de pytest. Entre las dos solo cambia una bandera de pip, -e:
- Control, con
pip install -e. Python lee la carpeta, así que todas las pruebas tienen que pasar, la sembrada incluida. Si no pasan, el experimento está mal montado y el guion se detiene en rojo. - Violación, con
pip install. Se instala la rueda. Tiene que fallar una sola prueba, la sembrada, y con el mensaje exacto.
Ésta es la salida real:
1/2 control: pip install -e (la carpeta) · src/suelto.py está fuera del paquete
revisor bajo prueba: src/revisor (la CARPETA del proyecto)
✅ 63 de 63 pasaron: desde la carpeta, suelto.py se ve
2/2 violación: pip install (la rueda) · el mismo archivo, la misma prueba
revisor bajo prueba: …/site-packages/revisor (el paquete INSTALADO)
✗ test_suelto_viaja_con_el_paquete: ModuleNotFoundError: No module named 'suelto'
✅ 62 de 63 pasaron; falló solo la que usa suelto.py, por la razón exacta
✅ frontera: se sostiene¿Por qué el control? Porque un experimento que solo espera un rojo se pone «verde» también cuando falla por la razón equivocada: una prueba mal escrita, un entorno roto. Lo comprobamos: con la prueba sembrada rota a propósito, o con el control instalado sin -e, el guion se para en «el experimento está mal montado».
Y si alguien cambia la línea de packages para que la rueda meta todo src/, la violación pasa y el guion contesta ❌ PASÓ: lo instalado vio src/suelto.py, que no está en el paquete. La frontera no existe. También lo comprobamos.
El CI de GitHub está escrito para correr todo con Python 3.12 y 3.14, en cada cambio, a mano y cada lunes: ruff, mypy estricto, las pruebas contra lo instalado y la frontera. Si el repositorio público pasa 60 días sin actividad, GitHub apaga la corrida de los lunes y hay que encenderla otra vez [23].
¿Cuáles son los cinco errores más comunes?
Los tres primeros salen de no saber qué carpeta ve Python; los dos últimos, de describir el proyecto a medias.
1. Probar la carpeta y creer que probaste el paquete
El diseño plano deja la raíz a la vista, y la raíz no es lo que se instala.
Es el error de Iván. La guía de PyPA explica que src/ ayuda a evitar el uso accidental de la copia en desarrollo [1]. pytest lo pide de frente: «it is strongly suggested to use a src layout», sobre todo con su modo por omisión [2]. Y la documentación de uv dice que uv init prefiere poner el código en src/<nombre>/ [18]; lo comprobamos con uv 0.12.23.
La salida no es solo cambiar de carpeta: es probar también lo instalado, como hace la plantilla.
2. pip install en el Python del sistema
El sistema operativo ya administra ese Python, y desde el PEP 668 te lo dice.
El PEP 668 propone que, por omisión, las herramientas como pip no instalen ni quiten paquetes en un Python que el sistema marca como administrado por él, y que guíen a quien lo intente hacia un entorno virtual [9]. En Ubuntu 24.04, sin entorno, esto es lo que contesta (medido):
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.El PEP pide que la bandera para forzarlo «should carry some connotation that its use is risky» [9]. Por eso se llama --break-system-packages. Todo va dentro de un .venv, como en la plantilla.
3. Un archivo con el nombre de un módulo estándar
Un random.py suelto tapa al random de Python.
El tutorial oficial lo advierte: la carpeta del guion que se ejecuta va al principio de la ruta de búsqueda, así que sus archivos se cargan en lugar de los módulos de la biblioteca con el mismo nombre, y eso «is an error unless the replacement is intended» [19]. Python 3.13 agregó un mensaje que lo señala [12]. Medido con un random.py que importa random:
Python 3.12:
AttributeError: partially initialized module 'random' has no attribute 'randint' (most likely due to a circular import)
Python 3.14:
AttributeError: module 'random' has no attribute 'randint' (consider renaming '…/random.py' since it has the same name as the standard library module named 'random' and prevents importing that standard library module)En 3.12 el mensaje habla de una importación circular y no de tu archivo; el 3.12.3 de Ubuntu 24.04, el de Mint, dice lo mismo (medido). Dentro de un paquete, corriéndolo como paquete (python -m revisor.cli o el comando instalado), el choque no ocurre: revisor/random.py se llama revisor.random. Pero si corres el archivo como guion, python src/revisor/cli.py, vuelve (medido en 3.12 y 3.14).
4. Dos archivos de prueba con el mismo nombre
En el modo por omisión de pytest, tests/config/test_formato.py y tests/reporte/test_formato.py chocan.
La documentación de pytest lo explica: si las pruebas no están organizadas como paquetes, cada archivo necesita un nombre único, o pytest marca un error [4]. Medido con pytest 9.1.1:
import file mismatch:
imported module 'test_formato' has this __file__ attribute:
…/tests/config/test_formato.py
which is not the same as the test file we want to collect:
…/tests/reporte/test_formato.py
HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modulesEl arreglo de siempre era agregar __init__.py a tests/, pero eso hace que pytest ponga la raíz del repositorio en la ruta de búsqueda, y en un diseño plano tu paquete vuelve a importarse desde la carpeta [2]. El modo importlib no tiene ese problema [2]: con él, los dos archivos pasan (medido).
5. setup.py o requirements.txt como única descripción del proyecto
pip no lee un requirements.txt al instalar tu paquete.
Para proyectos nuevos, la guía de PyPA pide la tabla [project] de pyproject.toml, y conservar setup.py solo si hace falta configurar algo con código, como compilar extensiones en C [13]. Y un requirements.txt no se analiza al instalar: solo se usa cuando alguien corre pip install -r [14]. Si tus dependencias viven solo ahí, quien instale tu paquete no las recibe.
¿Quieres que cada máquina instale exactamente las mismas versiones? Para eso existe ahora un formato estándar de archivo de bloqueo, pylock.toml, del PEP 751 [15]. Es otro archivo, para otro problema.
| Error | Lo que lo delata | Fuente |
|---|---|---|
| 1. Probar la carpeta | Verde en tu máquina, ModuleNotFoundError instalado | [1][2] |
| 2. pip en el Python del sistema | externally-managed-environment | [9] |
3. Un random.py suelto | has no attribute en un módulo que sí lo tiene | [12][19] |
| 4. Pruebas con el mismo nombre | import file mismatch | [2][4] |
5. requirements.txt como única descripción | Instalado, le faltan dependencias | [13][14] |
Ejercicio: ¿puedes repetir el error de Iván?
Vas a meter un archivo fuera del paquete, ver cómo tu carpeta te dice que todo está bien, y ver cómo lo instalado te dice que no.
Necesitas Python 3.12 o más nuevo (python3 --version), Git y bash, unos 20 minutos. Linux Mint 22 trae 3.12; si python3 -m venv se queja de que falta ensurepip, corre sudo apt install python3.12-venv y repite la orden (la lección 1 del curso lo explica). En macOS, el python3 del sistema es el de Apple, «usually older» [24] (aquí, 3.9.6): instala uno reciente desde python.org. En Windows, usa WSL.
Paso 1. Clona, prepara el entorno y comprueba que todo está en verde.
git clone https://github.com/HabilMX/estructura-python
cd estructura-python
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --group dev -e .
pytest -q
bash herramientas/probar-instalado.shLo que debes ver: 62 passed y, al final, ✅ lo instalado pasa sus pruebas.
Paso 2. Predice antes de tocar. Vas a crear src/exportar.py, fuera del paquete, y una prueba que lo importa. Anota: ¿qué dirá pytest? ¿Y el guion de lo instalado?
Paso 3. Repite el error.
cat > src/exportar.py <<'EOF'
def a_csv(filas: list[tuple[str, str]]) -> str:
return "\n".join(",".join(fila) for fila in filas)
EOF
cat > tests/test_exportar.py <<'EOF'
from exportar import a_csv
def test_a_csv() -> None:
assert a_csv([("pagos", "ARRIBA")]) == "pagos,ARRIBA"
EOF
pytest -q
bash herramientas/probar-instalado.shpytest dice 63 passed: tu carpeta asegura que todo está bien. El guion dice otra cosa (lo que importa; abajo siguen la salida de pytest y un ❌):
pruebas: 63 · pasaron 62 · fallaron 0 · con error 1 · saltadas 0
✗ tests.test_exportar: ModuleNotFoundError: No module named 'exportar'Ése es el viernes de Sofía, en tu máquina.
Paso 4. Arréglalo como Iván: mete el archivo al paquete. Copia solo la línea de sed de tu sistema: el de macOS pide un argumento vacío después de -i.
mv src/exportar.py src/revisor/exportar.py
# En Linux:
sed -i 's/^from exportar import/from revisor.exportar import/' tests/test_exportar.py
# En macOS:
sed -i '' 's/^from exportar import/from revisor.exportar import/' tests/test_exportar.py
bash herramientas/probar-instalado.shAhora sale pruebas: 63 · pasaron 63 y ✅ lo instalado pasa sus pruebas.
Paso 5. Intenta engañar al guardia. Pídele a pytest que exija lo instalado, desde tu entorno editable:
REVISOR_EXIGIR_INSTALADO=1 pytestERROR: REVISOR_EXIGIR_INSTALADO=1, pero revisor viene de la carpeta src/revisor. Estas pruebas probarían tu carpeta, no lo que se instala. Usa: bash herramientas/probar-instalado.shSe negó a darte un verde. Ése es el control trabajando.
Paso 6. Regresa todo.
git checkout -- . && git clean -fd
bash herramientas/probar-frontera.shOjo: git clean -fd también borra los archivos nuevos que hayas creado ahí, como tus notas.
Cómo sabes que lo hiciste bien: corriste siete veces y viste tres desenlaces. Verde, cinco veces: las dos del paso 1, pytest en el paso 3, el paso 4 y la frontera del paso 6. Rojo, una: lo instalado en el paso 3, con No module named 'exportar'. Y la negativa del guardia en el paso 5. Si en el paso 3 el guion sale verde, revisa que exportar.py haya quedado en src/ y no en src/revisor/. Si en el paso 4 sigue rojo, revisa que la primera línea de tests/test_exportar.py diga from revisor.exportar import a_csv.
Para ir más lejos (opcional): agrega a_csv a reporte.py, suma csv a las opciones de --formato en cli.py y escribe su prueba. Antes de correrla, predice qué contará probar-instalado.sh.
Para llevar
Tres preguntas para hacerle a tu repositorio esta semana.
- ¿Desde dónde corren tus pruebas? Si la respuesta es «desde la raíz, con
python -m pytest», ¿qué archivos de la raíz alcanza a ver Python que no van en el paquete? - ¿Alguna vez corriste tus pruebas contra el paquete instalado, en un entorno nuevo y sin
-e? Hazlo una vez. Si truena, encontraste un error antes que tus usuarios. - ¿Tu proyecto se describe en
pyproject.toml, o todavía hay que leer unsetup.pyy unrequirements.txtpara saber qué necesita?
¿Qué sigue?
En Go, la frontera la vigila la herramienta. En Python, la vigila la instalación. En Rust, la vigila el compilador, y es más estricta de lo que esperas.
Dos semanas después, a Iván le tocó un programa en Rust. Toda la lógica vivía en un main.rs de 900 líneas. Quiso escribir una prueba de integración, de las que viven fuera del código, en tests/.
Cargo no encontró nada que importar. El programa funcionaba, pero desde fuera nadie podía tocarlo.
¿Cómo se prueba una función que nadie puede importar? En la entrega 3: un archivo llamado lib.rs, y por qué el compilador te está dando una pista, no un castigo.
La plantilla y el curso
- Plantilla: github.com/HabilMX/estructura-python. Se usa como plantilla de GitHub o se clona [22]. El README explica cada carpeta y cuándo agregar cada cosa. El código es MIT; el texto y las explicaciones son CC BY-SA 4.0.
- Versiones: pide Python 3.12 o más nuevo y se probó hasta la 3.14, en macOS y en Linux: con 3.14.9, la más reciente de la 3.14 el 22 de octubre [21], con 3.12.15 y con 3.12.3, la de Ubuntu 24.04 [11]. La 3.15 todavía no. La 3.14 tiene soporte hasta octubre de 2030; la 3.12 ya solo recibe parches de seguridad, hasta octubre de 2028 [20]. Solo usa la biblioteca estándar; pytest 9.1.1, ruff 0.17.1 y mypy 2.4.0 van fijados en el grupo de desarrollo.
- Curso de Python de Hábil: si
pyproject.toml,python -mo la instalación editable son nuevos para ti, la lección 7 los cuenta desde cero: Curso de Python · Lección 7, Un paquete con línea de comandos.
Escribo desde lo que vemos todos los días en Hábil, donde construimos sistemas para banca, seguros y empresas en México. Si en tu trabajo o en tu escuela se topan con un repositorio que nadie sabe dónde tocar, platiquemos: habil.mx
¿Qué hace Hábil?
Enseñamos a programar con lo mismo que usamos para construir: código probado, explicado y abierto.
Esta serie nace del mismo lugar que nuestros cursos. El Curso de Python va de cero a un primer programa completo, y lo publicamos abierto, con licencia Creative Commons; su lección 7 cuenta el paquete con línea de comandos, pyproject.toml y la instalación editable que usa la plantilla de este artículo. Y en el catálogo hay más cursos de programación, gratuitos y con código probado y explicado paso a paso.
Mira el Curso de Python y el catálogo de Cursos de programación.
Si tu equipo hereda un sistema que nadie sabe dónde tocar, o necesita construir uno que se pueda seguir manteniendo, también lo hacemos: mira Construir su sistema a la medida. Pide tu primera conversación, sin costo. En ella revisamos si conviene configurar lo que ya tienen, usar un producto o construir, y qué recibirían en cada caso. Escríbenos por WhatsApp.
Fuentes consultadas el 22 de octubre de 2026. Las citas en inglés se reproducen tal cual.
Referencias
- Python Packaging Authority, «src layout vs flat layout», Python Packaging User Guide. https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/
- pytest, «Good Integration Practices», docs.pytest.org (pytest 9.1). https://docs.pytest.org/en/stable/explanation/goodpractices.html
- pytest, «How to invoke pytest»: «Calling pytest through python -m pytest», docs.pytest.org. https://docs.pytest.org/en/stable/how-to/usage.html
- pytest, «pytest import mechanisms and sys.path/PYTHONPATH», docs.pytest.org. https://docs.pytest.org/en/stable/explanation/pythonpath.html
- pip, «Local project installs»: «Editable installs», pip.pypa.io. https://pip.pypa.io/en/stable/topics/local-project-installs/
- Hatch, «Build configuration»: «Packages» y «Dev mode», hatch.pypa.io. https://hatch.pypa.io/latest/config/build/
- Hatch, «Wheel builder»: «Default file selection», hatch.pypa.io. https://hatch.pypa.io/latest/plugins/builder/wheel/
- Python, «Command line and environment»: opción
-m, docs.python.org. https://docs.python.org/3/using/cmdline.html - G. Thomas, M. Klose, F. Laíns, D. Stufft, T. Chung, S. Rivera, E. Hashman y P. Gedam, «PEP 668 – Marking Python base environments as “externally managed”», 18 de mayo de 2021. https://peps.python.org/pep-0668/
- Linux Mint, «Linux Mint 22.3 Release Notes» («Linux Mint 22.x is based on Ubuntu 24.04»), linuxmint.com. https://www.linuxmint.com/rel_zena.php
- Launchpad (Canonical), paquetes publicados en Ubuntu 24.04 «noble»:
python3-defaults3.12.3 ypython-pip24.0. https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=python3-defaults&exact_match=true · https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=python-pip&exact_match=true - Python, «What's New In Python 3.13»: «Improved error messages», docs.python.org. https://docs.python.org/3/whatsnew/3.13.html
- Python Packaging Authority, «Writing your pyproject.toml», Python Packaging User Guide. https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
- Python Packaging Authority, «install_requires vs requirements files», Python Packaging User Guide. https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/
- Brett Cannon, «PEP 751 – A file format to record Python dependencies for installation reproducibility», 24 de julio de 2024 (aceptado el 31 de marzo de 2025). https://peps.python.org/pep-0751/
- Stephen Rosen, «PEP 735 – Dependency Groups in pyproject.toml», 20 de noviembre de 2023 (aceptado el 10 de octubre de 2024). https://peps.python.org/pep-0735/
- pip, «Changelog», versión 25.1 (26 de abril de 2025), pip.pypa.io. https://pip.pypa.io/en/stable/news/
- Astral, «Creating projects» (uv), docs.astral.sh. https://docs.astral.sh/uv/concepts/projects/init/
- Python, «The Python Tutorial», 6.1.2 «The Module Search Path», docs.python.org. https://docs.python.org/3/tutorial/modules.html
- Python Developer's Guide, «Status of Python versions». https://devguide.python.org/versions/
- Python Software Foundation, «Python 3.14.9», 21 de octubre de 2026. https://www.python.org/downloads/release/python-3149/
- GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
- GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
- Python, «Using Python on macOS» (§5.1), docs.python.org. https://docs.python.org/3/using/mac.html · Instaladores: https://www.python.org/downloads/macos/
¿Su operación tiene estos desafíos?
¿Prefiere correo? Escríbanos a hola@habil.mx