Я хочу рассказать о том, как настраивать проект типа package на Python. Обычно это то, с чем встречаются в самом начале процесса разработки и после всех настроек благополучно забывают. Но я решил весь этот процесс описать, дабы не повторять все эти действия, каждый раз, когда нужно создать новый проект. Может, кто-то скажет, что прежде, чем понять Python, нужно пройти путь самурая и понять все тонкости его настройки самому, опираясь лишь на документацию. Возможно, они правы, но я считаю, что лучше один раз написать, чем тысячу раз вспоминать.

Краткий обзор теории построения проекта:

В целом принцип построения такой:

  1. Устанавливаем Python ( https://www.python.org/downloads/ )

  2. Создаем папку на диске и в ней файл __init__.py. Благодаря этому файлу Python понимает, что в ней лежат файлы с исходным кодом. __init__.py может быть пустым, но также в нем можно прописать ссылки на все функции, которые содержаться в файлах типа *.py, которые лежат внутри этой папки.

  3. Для того чтобы можно было удобно проверять, как работают функции в этих файлах можно создать __main__.py в этой же папке. И далее, набирая команду “python -m Имя_Папки” в командной строке, будет выполняться код из файла __main__.py

  4. Далее можно добавить тесты для созданного проекта и также необходимо настроить setup для того, чтобы собирать из проекта пакет на Python, который можно будет где угодно устанавливать и запускать.

  5. Чтобы наш пакет автоматически сохранялся на каком-нибудь локальном сервере, надо будет прописать этот сервер в файле 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

Комментарии (2)