Архитектура и бекенд13 мин

Структурата на един проект (2): минава на моята машина, гърми след инсталиране

От · основател и главен изпълнителен директор на Hábil ·

Тестовете минават, а пакетът гърми при този, който го инсталира. PyPA иска src/, но редактируемата инсталация също чете папката ти. С шаблон.

Структурата на един проект · част 2 от 7 · Python

Сценарий: героите и историята са измислени. Техническите данни и цитатите са с източник в края.

За 1 минута

  • Ако пускаш тестовете от корена на проекта, Python може да намери папката ти преди инсталирания пакет. Файл, който така и не е влязъл в пакета, минава тестовете ти и гърми при този, който го инсталира [1][3].
  • Ръководството за пакетиране на Python (PyPA) решава това с една папка: src/. С нея кодът се изпълнява само ако е инсталиран [1].
  • Но внимавай: редактируемата инсталация (pip install -e .) също чете папката ти. С hatchling — цялата src/ (измерено) [5][6].
  • Затова шаблонът от тази част тества два пъти: бързо срещу папката ти и наистина срещу инсталираното. И идва със скрипт, който го доказва, като става червен, когато трябва.
  • За понеделник: пусни тестовете си срещу инсталирания пакет в нова среда. Ако нещо гръмне, вече знаеш какво е.

В тази статия

  1. Какво се случи в петък в 9:30?
  2. Какво иска ръководството за пакетиране?
  3. Кои са петте най-чести грешки?
  4. Упражнение: можеш ли да повториш грешката на Иван?
  5. Ключови изводи
  6. Какво следва?
  7. Шаблонът и курсът
  8. Какво прави Hábil?
  9. Източници

Какво се случи в петък в 9:30?

Всички тестове на Иван минаваха. В пакета, който инсталира София, нямаше файла, който тези тестове използваха.

Четвъртък, 18:10. Трети етаж.

След Go втората задача му се стори лесна: «Проверителят на Python да експортира отчета в CSV». Проверителят беше същата програма от предишната седмица, но на Python: четеше списък с услуги, запитваше ги и казваше кои отговарят.

Хранилището, отново наследено, изглеждаше така:

revisor/          el paquete
tests/
pyproject.toml

Иван написа функцията в нов файл, exportar.py. Остави го в корена, до всичко останало, защото там го виждаше. В revisor/cli.py добави един ред: import exportar. Пусна тестовете, както го бяха учили, от корена:

$ python -m pytest -q
.                                                                        [100%]
1 passed in 0.00s

Зелено. Качи промяната и си тръгна.

Петък, 9:30.

София от експлоатацията инсталира новата версия в своята среда с pip install . и пусна командата. Терминалът ѝ отговори това (пътищата са съкратени):

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'

Иван отвори папката си: exportar.py си беше там. Пусна тестовете пак. Пак зелено.

— На моята машина минава — написа той в канала.

Лусия, техническата ръководителка, отговори с въпрос:

— Тестовете ти провериха това, което инсталира София, или папката ти?

Иван се загледа в екрана. Никога не му беше хрумвало, че това са две различни неща.

— От корена Python вижда всичко, което има в корена — продължи Лусия. — София няма твоя корен. Тя има пакета. А в пакета exportar.py така и не е влязъл.

Какво иска ръководството за пакетиране?

Кодът, който се импортира, да живее в src/, за да се изпълнява едва след като бъде инсталиран.

Официалното ръководство за пакетиране на Python, на PyPA, сравнява двете подредби в src layout vs flat layout [1]. В плоската подредба пакетът живее в корена, до pyproject.toml. В подредбата src/ слиза едно ниво надолу.

Разликата, която има значение, е на първия ред от списъка му: със src/ трябва да инсталираш проекта, за да изпълниш кода му, а с плоската подредба — не [1]. Изглежда като недостатък. Точно това е смисълът.

Ръководството обяснява защо: Python слага текущата папка в началото на пътя си за търсене, така че ако в нея има пакет със същото име като инсталирания, печели този от папката. А това, казва то, може да остави настройката на пакетирането грешна, без никой да забележи, «which could result in files not being included in a distribution» [1]. Точно това се случи с Иван.

Текущата папка има една тънкост: не винаги влиза. python -m pytest я добавя; само pytest — не [3][8]. Иван ползва първата форма и с нея целият му корен беше на показ.

Шаблонът от тази част, estructura-python, използва подредбата 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

Какво решава кое заминава с пакета?

Един ред от pyproject.toml.

[tool.hatch.build.targets.wheel]
packages = ["src/revisor"]

Това, което се инсталира, е «колело» (wheel) — файл .whl, сглобен от проекта. Този ред казва на hatchling, инструмента, който го сглобява, че колелото носи src/revisor и нищо друго [6]. Без него hatchling гадае: търси първо revisor/__init__.py, а после src/revisor/__init__.py [7]. Шаблонът го изписва, за да се чете.

Така всичко, което живее в src/revisor/, заминава с пакета. Това, което живее извън нея — не.

Значи src/ вече ме пази?

От корена ти — да. От редактируемата инсталация — не съвсем.

За работа е удобно да инсталираш в редактируем режим: pip install -e .. Така нищо не копираш; Python чете файловете ти там, където са, и всяка промяна в кода се вижда при следващото изпълнение [5]. pip предупреждава, че редактируемата и обикновената инсталация могат да се държат различно, според инструмента за сглобяване, и че който ползва пакета ти, «will see the behaviour of regular installs» [5].

А как е при hatchling? Документацията му казва, че при редактируема инсталация той решава кои папки да добави към пътя за търсене според избраните файлове [6]. Измерихме го: добавя цялата src/. Ако някой остави src/suelto.py извън пакета, редактируемата инсталация го намира, а колелото не го носи.

Същата страница предлага изход — опцията dev-mode-exact [6]. Измерено: с нея import suelto дава ModuleNotFoundError. Но има цена и страницата я посочва: «The dev-mode-exact mechanism is not supported by static analysis tools & IDEs» [6], така че редакторът ти спира да допълва автоматично. А и не вижда какво колелото изключва вътре в пакета (измерено). Затова шаблонът предпочита да тества колелото, защото то е това, което получава инсталиращият.

Ръководството на PyPA вече го предвижда: обичайно е редактируемата инсталация да се ползва за разработка, «and a regular installation is used for testing» [1]. Шаблонът прави точно това:

pytest                                  # rápido: lee tu carpeta src/
bash herramientas/probar-instalado.sh   # lo que ve quien instala tu paquete

Втората команда създава нова, празна среда, инсталира проекта без -e (тоест сглобява колелото и го инсталира) и пуска същите тестове. Накрая преброява резултатите, като чете отчета на pytest, а не кода му за изход. Това са последните ѝ три реда:

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

Защо тестовете живеят извън пакета?

За да не заминават с него и за да тестват пакета така, както би го видял всеки.

tests/ е до src/, не вътре в нея. pytest работи в режим importlib — този, който документацията му препоръчва за нови проекти [2]: в този режим той не слага папки от проекта в пътя за търсене, за да импортира тестовете [4]. Тества се това, което е инсталирано в средата.

А как разбираш какво е инсталирано? tests/conftest.py го казва в заглавката на всяко изпълнение (тази, която pytest -q пропуска):

revisor bajo prueba: src/revisor (la CARPETA del proyecto)

С променливата REVISOR_EXIGIR_INSTALADO=1 освен това отказва да тръгне, ако revisor идва от папката ти. Скриптът за инсталираното я включва и така не може да тества твоята src/revisor с мисълта, че е тествал колелото. Внимавай: пазачът гледа само откъде идва revisor. Ако друг модул от папката ти се промъкне в тестовете, спира го границата, за която става дума веднага след това.

Какво има в pyproject.toml?

Целият проект, в един файл.

Ръководството на PyPA описва три таблици: [build-system], която казва с какво се сглобява; [project], с данните на проекта; и [tool], за всеки инструмент [13]. Шаблонът ползва и трите, плюс четвърта: [dependency-groups], където живеят pytest, ruff и mypy с фиксирана версия. Тази група съществува от PEP 735, който я определя точно така: изисквания, записани в pyproject.toml, които не влизат в никоя дистрибуция на проекта [16]. Който инсталира revisor, не получава pytest.

Един измерен детайл: pip install --group dev изисква pip 25.1 или по-нов [17], а току-що създадена среда в Ubuntu 24.04, основата на Linux Mint 22 [10], идва с pip 24.0 [11]. Отговорът е no such option: --group. Затова README-то иска python -m pip install --upgrade pip преди всичко друго.

А къде отива CSV-то на Иван?

В пакета, до другите формати на отчета.

В шаблона отчетът вече си има дом: reporte.py, който днес превръща резултатите в таблица или в JSON. Функция a_csv отива там, до a_json, а cli.py добавя csv към опциите на --formato. Никакъв файл в корена. Никаква нова папка.

В понеделник Иван премести функцията си в пакета и пусна тестовете срещу инсталираното. Зелено. Този път — наистина зелено.

Как да съм сигурен, че границата съществува?

Защото един скрипт я нарушава нарочно и изисква инсталираното да не вижда онова, което е останало отвън.

herramientas/probar-frontera.sh работи върху временно копие на проекта. Засажда src/suelto.py — модул, който живее в src/, но извън пакета — и тест, който го импортира. После пуска целия набор от тестове два пъти, в две нови среди със същия Python, същото копие и същата версия на pytest. Между двете се сменя само една опция на pip, -e:

  1. Контрола, с pip install -e. Python чете папката, така че всички тестове трябва да минат, включително засаденият. Ако не минат, експериментът е зле сглобен и скриптът спира в червено.
  2. Нарушение, с pip install. Инсталира се колелото. Трябва да падне само един тест — засаденият — и то с точното съобщение.

Това е истинският изход:

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

Защо контролата? Защото експеримент, който очаква само червено, става «зелен» и когато се провали по грешната причина: зле написан тест, счупена среда. Проверихме го: с нарочно счупен засаден тест или с контрола, инсталирана без -e, скриптът спира на «експериментът е зле сглобен».

А ако някой промени реда packages така, че колелото да вземе цялата src/, нарушението минава и скриптът отговаря ❌ PASÓ: lo instalado vio src/suelto.py, que no está en el paquete. La frontera no existe. И това проверихме.

CI в GitHub е написан да пуска всичко с Python 3.12 и 3.14, при всяка промяна, ръчно и всеки понеделник: ruff, строг mypy, тестовете срещу инсталираното и границата. Ако публичното хранилище остане 60 дни без активност, GitHub изключва понеделнишкото изпълнение и трябва да се включи отново [23].

Кои са петте най-чести грешки?

Първите три идват от това, че не знаеш коя папка вижда Python; последните две — от това, че проектът е описан наполовина.

1. Да тестваш папката и да мислиш, че си тествал пакета

Плоската подредба оставя корена на показ, а коренът не е това, което се инсталира.

Това е грешката на Иван. Ръководството на PyPA обяснява, че src/ помага да се избегне случайното ползване на копието в разработка [1]. pytest го казва направо: «it is strongly suggested to use a src layout», особено с режима му по подразбиране [2]. А документацията на uv казва, че uv init предпочита да сложи кода в src/<nombre>/ [18]; проверихме го с uv 0.12.23.

Изходът не е само да смениш папката: трябва да тестваш и инсталираното, както прави шаблонът.

2. pip install в системния Python

Операционната система вече управлява този Python и от PEP 668 насам ти го казва.

PEP 668 предлага по подразбиране инструменти като pip да не инсталират и да не махат пакети в Python, който системата е отбелязала като управляван от нея, и да насочват опитващия се към виртуална среда [9]. В Ubuntu 24.04, без среда, отговорът е този (измерено):

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.

PEP-ът изисква опцията за принудително инсталиране «should carry some connotation that its use is risky» [9]. Затова се казва --break-system-packages. Всичко отива в .venv, както в шаблона.

3. Файл с името на стандартен модул

Един забравен random.py закрива random на Python.

Официалният самоучител на Python (tutorial) предупреждава: папката на изпълнявания скрипт отива в началото на пътя за търсене, така че файловете ѝ се зареждат вместо модулите от библиотеката със същото име, а това «is an error unless the replacement is intended» [19]. Python 3.13 добави съобщение, което го посочва [12]. Измерено с random.py, който импортира 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)

В 3.12 съобщението говори за кръгово импортиране, а не за твоя файл; 3.12.3 от Ubuntu 24.04, този в Mint, казва същото (измерено). Вътре в пакет, ако го пускаш като пакет (python -m revisor.cli или инсталираната команда), сблъсъкът не се случва: revisor/random.py се казва revisor.random. Но ако пуснеш файла като скрипт, python src/revisor/cli.py, проблемът се връща (измерено с 3.12 и 3.14).

4. Два тестови файла с едно и също име

В режима по подразбиране на pytest tests/config/test_formato.py и tests/reporte/test_formato.py се сблъскват.

Документацията на pytest го обяснява: ако тестовете не са организирани като пакети, всеки файл трябва да има уникално име, иначе pytest отчита грешка [4]. Измерено с 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 modules

Обичайното решение беше да се добави __init__.py в tests/, но тогава pytest слага корена на хранилището в пътя за търсене и при плоска подредба пакетът ти пак се импортира от папката [2]. Режимът importlib няма този проблем [2]: с него двата файла минават (измерено).

5. setup.py или requirements.txt като единствено описание на проекта

pip не чете requirements.txt, когато инсталира пакета ти.

За нови проекти ръководството на PyPA иска таблицата [project] от pyproject.toml и setup.py да остане само ако нещо трябва да се настрои с код, например компилиране на разширения на C [13]. А requirements.txt не се анализира при инсталиране: ползва се само когато някой пусне pip install -r [14]. Ако зависимостите ти живеят само там, който инсталира пакета ти, не ги получава.

Искаш всяка машина да инсталира точно същите версии? За това вече има стандартен формат на заключващ файл, pylock.toml, от PEP 751 [15]. Това е друг файл, за друг проблем.

5. setup.py или requirements.txt като единствено описание на проекта
ГрешкаКакво я издаваИзточник
1. Тестване на папкатаЗелено на твоята машина, ModuleNotFoundError след инсталиране[1][2]
2. pip в системния Pythonexternally-managed-environment[9]
3. Забравен random.pyhas no attribute в модул, който го има[12][19]
4. Тестове с едно и също имеimport file mismatch[2][4]
5. requirements.txt като единствено описаниеСлед инсталиране липсват зависимости[13][14]

Упражнение: можеш ли да повториш грешката на Иван?

Ще сложиш файл извън пакета, ще видиш как папката ти казва, че всичко е наред, и как инсталираното ти казва, че не е.

Трябват ти Python 3.12 или по-нов (python3 --version), Git и bash, около 20 минути. Linux Mint 22 идва с 3.12; ако python3 -m venv се оплаче, че липсва ensurepip, пусни sudo apt install python3.12-venv и повтори командата (урок 1 от курса го обяснява). В macOS системният python3 е този на Apple, «usually older» [24] (тук 3.9.6): инсталирай нов от python.org. В Windows ползвай WSL.

Стъпка 1. Клонирай, подготви средата и провери, че всичко е зелено.

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.sh

Какво трябва да видиш: 62 passed и накрая ✅ lo instalado pasa sus pruebas.

Стъпка 2. Предскажи, преди да пипнеш. Ще създадеш src/exportar.py, извън пакета, и тест, който го импортира. Запиши: какво ще каже pytest? А скриптът за инсталираното?

Стъпка 3. Повтори грешката.

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.sh

pytest казва 63 passed: папката ти те уверява, че всичко е наред. Скриптът казва друго (важното; отдолу следват изходът на pytest и един ❌):

pruebas: 63 · pasaron 62 · fallaron 0 · con error 1 · saltadas 0
   ✗ tests.test_exportar: ModuleNotFoundError: No module named 'exportar'

Това е петъкът на София — на твоята машина.

Стъпка 4. Поправи го като Иван: сложи файла в пакета. Копирай само реда със sed за твоята система: този в macOS иска празен аргумент след -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.sh

Сега излиза pruebas: 63 · pasaron 63 и ✅ lo instalado pasa sus pruebas.

Стъпка 5. Опитай да излъжеш пазача. Поискай от pytest да изисква инсталираното, от редактируемата ти среда:

REVISOR_EXIGIR_INSTALADO=1 pytest
ERROR: 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.sh

Отказа да ти даде зелено. Това е контролата в действие.

Стъпка 6. Върни всичко.

git checkout -- . && git clean -fd
bash herramientas/probar-frontera.sh

Внимавай: git clean -fd изтрива и новите файлове, които си създал там, например бележките си.

Как разбираш, че си се справил: пусна седем пъти и видя три резултата. Зелено, пет пъти: двата в стъпка 1, pytest в стъпка 3, стъпка 4 и границата в стъпка 6. Червено, веднъж: инсталираното в стъпка 3, с No module named 'exportar'. И отказът на пазача в стъпка 5. Ако в стъпка 3 скриптът излезе зелен, провери дали exportar.py е останал в src/, а не в src/revisor/. Ако в стъпка 4 остане червен, провери дали първият ред на tests/test_exportar.py гласи from revisor.exportar import a_csv.

За по-нататък (по желание): добави a_csv в reporte.py, добави csv към опциите на --formato в cli.py и напиши теста му. Преди да го пуснеш, предскажи какво ще преброи probar-instalado.sh.

Ключови изводи

Три въпроса, които да зададеш на своето хранилище тази седмица.

  1. Откъде се пускат тестовете ти? Ако отговорът е «от корена, с python -m pytest», кои файлове от корена вижда Python, които не влизат в пакета?
  2. Пускал ли си някога тестовете си срещу инсталирания пакет, в нова среда и без -e? Направи го веднъж. Ако гръмне, намерил си грешка преди потребителите си.
  3. Описан ли е проектът ти в pyproject.toml, или още трябва да четеш setup.py и requirements.txt, за да разбереш от какво има нужда?

Какво следва?

В Go границата я пази инструментът. В Python — инсталацията. В Rust — компилаторът, и той е по-строг, отколкото очакваш.

Две седмици по-късно на Иван му се падна програма на Rust. Цялата логика живееше в main.rs от 900 реда. Искаше да напише интеграционен тест — от тези, които живеят извън кода, в tests/.

Cargo не намери нищо за импортиране. Програмата работеше, но отвън никой не можеше да я докосне.

Как се тества функция, която никой не може да импортира? В част 3: файл на име lib.rs и защо компилаторът ти дава подсказка, а не наказание.

Шаблонът и курсът

  • Шаблон: github.com/HabilMX/estructura-python. Използва се като шаблон в GitHub или се клонира [22]. README-то обяснява всяка папка и кога да добавиш всяко нещо. Кодът е под MIT; текстът и обясненията — под CC BY-SA 4.0.
  • Версии: изисква Python 3.12 или по-нов и е тестван до 3.14, в macOS и в Linux: с 3.14.9, най-новата от 3.14 към 22 октомври [21], с 3.12.15 и с 3.12.3, тази в Ubuntu 24.04 [11]. С 3.15 още не. 3.14 има поддръжка до октомври 2030 г.; 3.12 вече получава само корекции по сигурността, до октомври 2028 г. [20]. Ползва само стандартната библиотека; pytest 9.1.1, ruff 0.17.1 и mypy 2.4.0 са фиксирани в групата за разработка.
  • Курсът по Python на Hábil: ако pyproject.toml, python -m или редактируемата инсталация са нови за теб, урок 7 ги обяснява от нулата: Курс по Python · Урок 7, Пакет с команден ред.

Пиша от това, което виждаме всеки ден в Hábil, където изграждаме системи за банки, застраховане и компании в Мексико. Ако в работата или в университета ти и колегите ти попаднете на хранилище, за което никой не знае откъде да го хване, да поговорим: habil.mx

Какво прави Hábil?

Учим на програмиране със същото, с което изграждаме: тестван, обяснен и отворен код.

Тази поредица идва от същото място като нашите курсове. Курсът по Python води от нулата до първа цялостна програма и го публикуваме отворено, с лиценз Creative Commons; урок 7 обяснява пакета с команден ред, pyproject.toml и редактируемата инсталация, които използва шаблонът от тази статия. А в каталога има още курсове по програмиране, безплатни и с тестван и обяснен стъпка по стъпка код.

Разгледай Курса по Python и каталога Курсове по програмиране.

Ако екипът ти наследи система, за която никой не знае откъде да я хване, или трябва да изгради такава, която да може да се поддържа и занапред, правим и това: разгледай Изграждане на система по поръчка. Поискай първи разговор, без заплащане. В него преглеждаме дали си струва да се настрои това, което екипът ти вече има, да се ползва продукт или да се изгради, и какво би получил екипът във всеки случай. Пиши ни в WhatsApp.

Източниците са проверени на 22 октомври 2026 г. Цитатите на английски са възпроизведени дословно.

Източници

  1. Python Packaging Authority, «src layout vs flat layout», Python Packaging User Guide. https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/
  2. pytest, «Good Integration Practices», docs.pytest.org (pytest 9.1). https://docs.pytest.org/en/stable/explanation/goodpractices.html
  3. pytest, «How to invoke pytest»: «Calling pytest through python -m pytest», docs.pytest.org. https://docs.pytest.org/en/stable/how-to/usage.html
  4. pytest, «pytest import mechanisms and sys.path/PYTHONPATH», docs.pytest.org. https://docs.pytest.org/en/stable/explanation/pythonpath.html
  5. pip, «Local project installs»: «Editable installs», pip.pypa.io. https://pip.pypa.io/en/stable/topics/local-project-installs/
  6. Hatch, «Build configuration»: «Packages» и «Dev mode», hatch.pypa.io. https://hatch.pypa.io/latest/config/build/
  7. Hatch, «Wheel builder»: «Default file selection», hatch.pypa.io. https://hatch.pypa.io/latest/plugins/builder/wheel/
  8. Python, «Command line and environment»: опцията -m, docs.python.org. https://docs.python.org/3/using/cmdline.html
  9. G. Thomas, M. Klose, F. Laíns, D. Stufft, T. Chung, S. Rivera, E. Hashman и P. Gedam, «PEP 668 – Marking Python base environments as “externally managed”», 18 май 2021 г. https://peps.python.org/pep-0668/
  10. 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
  11. Launchpad (Canonical), пакети, публикувани в Ubuntu 24.04 «noble»: python3-defaults 3.12.3 и python-pip 24.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
  12. Python, «What's New In Python 3.13»: «Improved error messages», docs.python.org. https://docs.python.org/3/whatsnew/3.13.html
  13. Python Packaging Authority, «Writing your pyproject.toml», Python Packaging User Guide. https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
  14. Python Packaging Authority, «install_requires vs requirements files», Python Packaging User Guide. https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/
  15. Brett Cannon, «PEP 751 – A file format to record Python dependencies for installation reproducibility», 24 юли 2024 г. (приет на 31 март 2025 г.). https://peps.python.org/pep-0751/
  16. Stephen Rosen, «PEP 735 – Dependency Groups in pyproject.toml», 20 ноември 2023 г. (приет на 10 октомври 2024 г.). https://peps.python.org/pep-0735/
  17. pip, «Changelog», версия 25.1 (26 април 2025 г.), pip.pypa.io. https://pip.pypa.io/en/stable/news/
  18. Astral, «Creating projects» (uv), docs.astral.sh. https://docs.astral.sh/uv/concepts/projects/init/
  19. Python, «The Python Tutorial», 6.1.2 «The Module Search Path», docs.python.org. https://docs.python.org/3/tutorial/modules.html
  20. Python Developer's Guide, «Status of Python versions». https://devguide.python.org/versions/
  21. Python Software Foundation, «Python 3.14.9», 21 октомври 2026 г. https://www.python.org/downloads/release/python-3149/
  22. GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
  23. GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
  24. Python, «Using Python on macOS» (§5.1), docs.python.org. https://docs.python.org/3/using/mac.html · Инсталатори: https://www.python.org/downloads/macos/

Вашата дейност среща ли тези предизвикателства?

Да обсъдим вашия проект

Предпочитате имейл? Пишете ни на hola@habil.mx