Я хочу рассказать о том, как настраивать проект типа package на Python. Обычно это то, с чем встречаются в самом начале процесса разработки и после всех настроек благополучно забывают. Но я решил весь этот процесс описать, дабы не повторять все эти действия, каждый раз, когда нужно создать новый проект. Может, кто-то скажет, что прежде, чем понять Python, нужно пройти путь самурая и понять все тонкости его настройки самому, опираясь лишь на документацию. Возможно, они правы, но я считаю, что лучше один раз написать, чем тысячу раз вспоминать.
Краткий обзор теории построения проекта:
В целом принцип построения такой:
Устанавливаем Python ( https://www.python.org/downloads/ )
Создаем папку на диске и в ней файл __init__.py. Благодаря этому файлу Python понимает, что в ней лежат файлы с исходным кодом. __init__.py может быть пустым, но также в нем можно прописать ссылки на все функции, которые содержаться в файлах типа *.py, которые лежат внутри этой папки.
Для того чтобы можно было удобно проверять, как работают функции в этих файлах можно создать __main__.py в этой же папке. И далее, набирая команду “python -m Имя_Папки” в командной строке, будет выполняться код из файла __main__.py
Далее можно добавить тесты для созданного проекта и также необходимо настроить setup для того, чтобы собирать из проекта пакет на Python, который можно будет где угодно устанавливать и запускать.
Чтобы наш пакет автоматически сохранялся на каком-нибудь локальном сервере, надо будет прописать этот сервер в файле c:\\Users\{CurrentUser}\pip\pip.ini . Если этого не сделать, то пакет будет сохраняться на диске, откуда его можно будет скопировать куда-то руками.
Важные нюансы. На что обрать внимание.
На первый взгляд всё довольно просто, но есть нюансы. Самый простой из них это __init__.py. В папках с кодом на Python надо не забывать его добавлять. Далее – это файл setup.py. От его корректной настройки зависит, то, как будет собираться пакет и будет ли он вообще собираться. Также стоит знать о таком файле как MANIFEST.in. Говорят, что сейчас он не нужен, но без него у меня всё работало через раз. Ну и как вишенка на торте – это дополнительный аргумент для pytests “–no-cov”. С помощью которого можно запускать тесты в режиме отладки, если у нас помимо тестов прикручен анализ покрытия кода этими тестами. Есть ещё одна важная деталь для понимания – все библиотеки необходимые для работы разрабатываемого продукта рекомендуется устанавливать в локально окружение – venv, чтобы они были видны только этому продукту и существовали только внутри него, точнее, его локального виртуального окружения venv (Virtual Environment).
Мой путь. От теории к практике.
Вооружившись этими знаниями, я решил создать простой проект на Python и потратил на это уйму времени, так как в теории всё быстро и понятно, а на практике всё немного сложнее. Для начала пришлось повозиться с настройками setup.py. Я хотел, чтобы сторонние библиотеки хранились в отдельном файле и оказалось, что это не так просто сделать, так как в документации такой подход не рассматривается. Потом были танцы с бубном вокруг локального хранилища разработанных пакетов. Хранить свои пакеты глобально (https://pypi.org/) у меня не было желания, несмотря на рекомендации делать именно так в документации к питону (https://packaging.python.org/tutorials/packaging-projects/). Если не вдаваться в детали, то для того, чтобы это сделать, надо развернуть локально простенький веб сервер и прописать к нему путь в pip.ini. Ещё были сюрпризы от IntelliJ IDEA, в ней нужно в паре мест что-то подкрутить и подправить, чтобы проект начал нормально работать. Возможно, я немного сгустил краски и в целом создавать свой проект на Python легко и просто, но на всякий случай, я всё же решил написать подробную инструкцию с приложенным к ним видео, в которой описал свои шаги для создания проекта типа package на Python. У меня получилось 86 шагов, которые я разделил на 3 части. Причем в это описание не вошла история о том, как создавать свой локальный сервер для хранения пакетов. Я решил, что это больше относится к DevOps и шаги могут немного отличаться, в зависимости от того куда и как выкладывать эти пакеты. Скачать уже созданный мной тестовый проект можно тут - https://github.com/AlexKorole/mypython
Пошаговая инструкция.
Итак, сами шаги:
1. Создание python проекта типа package (видео тут: https://youtu.be/enahePzYT6E)
1.1. https://www.python.org/downloads/
1.2. create directory "mypython"
1.3. create directory "src" in "mypython"
1.4. create directory "myPyLib" in "src"
1.5. create file "hello.py" in directory "myPyLib"
1.6. add code in "hello.py":
def SayHello(name):
print("Hello ", name)
1.7. create file "__init__.py" in directory "myPyLib"
1.8. add code in "__init__.py":
from .hello import SayHello
1.9. create file "__main__.py" in directory "myPyLib"
1.10. add code in "__main__.py"
from .hello import SayHello
SayHello("dfg")
1.11. cmd
1.12. cd C:\work\Python\mypython\src
1.13. python -m myPyLib
1.14. cd..
1.15. py -m pip install --upgrade pip
1.16. py -m pip install --user virtualenv
1.17. py -m venv myvenv
1.18. .\myvenv\Scripts\activate
1.19. create file "setup.py" in directory "C:\work\Python\mypython"
1.20. add code in "setup.py":
from setuptools import setup, find_packages
with open("requirements/req.txt") as f:
required = f.read().splitlines()
setup(
version = "0.0.1",
packages=find_packages(where='src', exclude=["tests*"]),
install_requires=required,
name="myPkg",
author="I am",
author_email="",
description="library for static functions",
classifiers=["Programming Language :: Python :: 3"],
python_requires=">=3.7",
package_dir={"": "src"},
include_package_data = True
)
1.21. create file "pyproject.toml" in directory "C:\work\Python\mypython"
1.22. add in "pyproject.toml":
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
1.23. create directory "requirements" in "C:\work\Python\mypython"
1.24. create file "req.txt" in directory "requirements"
1.25. add in "req.txt":
pandas==1.1.4
1.26. create file "MANIFEST.in" in directory "C:\work\Python\mypython"
1.27. add in "MANIFEST.in"
include requirements/req.txt
1.28. in cmd: "pip install pandas"
1.29. modify "hello.py"
import numpy as np
import pandas as pd
def SayHello(name):
print("Hello ", name)
def CheckPandas():
s = pd.Series([1, 3, 5, np.nan, 6, 8])
print(s)
1.30. modify "__init__.py":
from .hello import SayHello, CheckPandas
1.31. modify "__main__.py"
from .hello import SayHello, CheckPandas
SayHello("dfg")
CheckPandas()
1.32. in cmd: "cd src"
1.33. "python -m myPyLib"
1.34. cd..
1.35. .\myvenv\Scripts\deactivate
1.36. py -m pip install --upgrade build
1.37. py -m build
1.38. cd C:\work\Python\mypython\dist
1.39. py -m venv mytestvenv
1.40. .\mytestvenv\Scripts\activate
1.41. pip install myPkg-0.0.1-py3-none-any.whl
1.42. py
1.43. from myPyLib import SayHello, CheckPandas
1.44. SayHello("aa")
1.45. CheckPandas()
1.46. Ctrl+Z
2. Добавляем к python проекту тесты, покрытие и проверку чистоты кода (видео тут: https://youtu.be/fhNIcTTKA6E)
2.1. .\mytestvenv\Scripts\deactivate
2.2. cd C:\work\Python\mypython
2.3. .\myvenv\Scripts\activate
2.4. in cmd "pip install tox"
2.5. create file "tox.ini" in "C:\work\Python\mypython"
2.6. add text in "tox.ini" file
[tox]
envlist = py38
skipsdist=true
[testenv]
commands = pytest
whitelist_externals = *
2.7. create folder "tests" in "C:\work\Python\mypython"
2.8. add file empty filr "__init__.py" in "C:\work\Python\mypython"
2.9. add file "test_hello.py" in "C:\work\Python\mypython\tests"
2.10. add code in "test_hello.py"
from src.myPyLib import SayHello
def test_SayHello():
assert SayHello("bb") == None
2.11. in cmd "tox"
2.12. pip install flake8
2.13. add to "tox.ini"
[flake8]
application_import_names = myPyLib, tests
max-line-length = 120
extend-ignore = E203,W503,E231,I201
exclude = myvenv, .tox, build, dist
2.14. in cmd "flake8"
2.15. pip install pytest-cov
2.16. pip install pytest-xdist
2.17. add to "tox.ini"
[pytest]
addopts = --cov=src.myPyLib --cov-append --cov-report term-missing
3. Открываем python проект в Intellij IDEA (видео тут: https://youtu.be/lQXzBsGWiIo)
3.1. in IntellijIdea - File - Settings - Plugins - Python (install)
3.2. in IntellijIdea - File - Open - directory "C:\work\Python\mypython"
3.3. Event Log - Python framework is detected - Configure
3.4. File - Project Structure - Project - Project SDK - Python 3.8
3.5. File - Project Structure - Modules
3.6. for Test set "tests" directory
3.7. for Source set "src\myPyLib" directory
3.8. Add Configuration -> Python
3.9. Name "run"
3.10. Script path: "C:\work\Python\mypython\src\myPyLib"
3.11. Use SDK of module
3.12. in "C:\work\Python\mypython\src\myPyLib\__main__.py" change:
from src.myPyLib import SayHello, CheckPandas
SayHello("dfg")
CheckPandas()
3.13. In IntellijIdea click Run "run"
3.14. Add Configuration -> PythonTests
3.15. select pytests
3.16. Additional Arguments: --no-cov // for debug
3.17. set "C:\work\Python\mypython\tests"
3.18. Use SDK of module
3.19. Run PythonTests
3.20. File - Settings - Tools - Python Integrated Tools - Testing - Default test runner: pytest
3.21. Run by click
3.22. Edit Configuration - Templates - Python tests - pytest - Additional Arguments: --no-cov
3.23. Run by click
3.24. tox.ini comment "addopts = --cov=src.myPyLib --cov-append --cov-report term-missing"
3.25. pip.ini https://mothergeo-py.readthedocs.io/en/latest/development/how-to/alternate-pypi.html