Как работать с PYPI =================== ----------- Оглавление ----------- * BuildPackage_ * DeployPackage_ * UsingPackage_ * PackageStructureInfo_ * PackageRestoring_ * Notes_ * WindowsInstallerBundling_ * PythonProtoClassesGenerating_ Сборка пакета (в wheel) ----------------------- .. _BuildPackage: Для корректной сборки пакета нужно создать в корне проекта файл ``setup.py`` со следующим содержимым: .. code:: python from setuptools import setup setup( name='vision', # Название пакета. Для установки пакета введите команду в консоли: pip install vision (по умолчанию ставится последняя версия пакета) version='1.0', # Версия пакета (в данном случае 1.0). Для установки конкретной версии введите команду в консоли: pip install vision==1.0 packages=['vision', 'vision.actions'], # Модули, которые войдут в пакет (необязательные можно при желании исключить) python_requires='~=3.6', # Требуемая версия Python - в данном случае 3.6 (но не 4, так как тильда не позволяет инкрементироватть версии) install_requires=[ 'Pillow==6.2.2', 'numpy', 'pytesseract' ], # Требуемые зависимости пакета для его корректной работы url='', # URL адрес проекта, например, на github (не обязательно) license='', # Лицензия (не обязательно) author='ROBIN Platform', # Автор пакета (используется в метаданных pip, не обязательно) author_email='', # Почтовый адрес автора пакета (в случае с официальным PyPI репозиторием - могут приходить уведомления о том, что какие-то модули устарели / содержат уязвимости, в остальных случаях используется просто для обратной связи) description='Actions for working with OCR and computer vision' # Описание пакета (не обязательно, используется в метаданных pip) ) После создания ``setup.py``, устанавливаем и обновляем все необходимые зависимости для корректной сборки пакета. :: pip install --upgrade wheel setuptools Для сборки готового пакета (wheel) запускаем следующую команду: :: python setup.py sdist bdist_wheel В результате работы этой команды должны создаться две директории - ``build`` и ``dist``. Внутри dist будет .whl файл нашего собранного пакета. Загрузка пакета в сторонний PyPI-репозиторий -------------------------------------------- .. _DeployPackage: Для загрузки пакета в репозиторий нам потребуется установить ``twine``. Выполняем следующую команду: :: pip install twine После установки twine, загружаем наш новый пакет в репозиторий следующей командой (адрес репозитория может различаться): :: twine upload --repository-url http://172.28.1.250:8083/repository/pypi-releases/simple/ dist/* Twine запросит логин и пароль от репозитория. Вводим их и пакет успешно загружается в репозиторий. Чтобы Twine не запрашивал логин и пароль в интерактивном режиме, можно загрузить пакет в репозиторий следующей командой: :: twine upload -u [login] -p [password] --repository-url http://172.28.1.250:8083/repository/pypi-releases/simple/ dist/* где [login] и [password] - логин и пароль от репозитория. Установка пакета из стороннего репозитория ------------------------------------------ .. _UsingPackage: Чтобы установить пакет (в нашем случае ``vision``) из стороннего репозитория, используем следующую команду :: pip install -i http://[login]:[password]@172.28.1.250:8083/repository/pypi-releases/simple/ --trusted-host 172.28.1.250 vision где [login] и [password] - ваши логин и пароль от репозитория. Использование установленного пакета ----------------------------------- Вариант 1. Импорт целого пакета ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: python import vision image_url = "test.jpg" print(vision.actions.read_text_from_image(image, lang='rus')) Вариант 2. Импорт отдельных методов/модулей пакета. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: python from vision.actions import read_text_from_image image_url = "test.jpg" print(read_text_from_image(image, lang='rus')) Общая информация о пакетах -------------------------- .. _PackageStructureInfo: Структура правильного PyPI-пакета должна быть следующей (упрощена для наглядности). :: | .gitignore | .gitlab-ci.yml | +---ExistsOnScreen | | setup.py | | __init__.py | | | \---ExistsOnScreen | action.py | Robin.Vision.ExistsOnScreen-Python.robin-impinfo | __init__.py | +---FindOnScreen | | setup.py | | __init__.py | | | \---FindOnScreen | action.py | Robin.Vision.FindOnScreen-Py.robin-impinfo | __init__.py | +---ReadTextFromImage | | setup.py | | __init__.py | | | \---ReadTextFromImage | action.py | Robin.Vision.ReadTextFromImage-Py.robin-impinfo | __init__.py | +---RecognizeByTemplate | | setup.py | | __init__.py | | | \---RecognizeByTemplate | action.py | Robin-Py.Vision.RecognizeByTemplate-Py.robin-impinfo | __init__.py | +---Utils | | setup.py | | __init__.py | | | \---Utils | utils.py | __init__.py | +---WaitForAction | | setup.py | | __init__.py | | | \---WaitForAction | action.py | Robin-Py.Vision.WaitForAction-Py.robin-impinfo | __init__.py | +---WaitForDisappear | | setup.py | | __init__.py | | | \---WaitForDisappear | action.py | Robin-Py.Vision.WaitForDisappear-Py.robin-impinfo | __init__.py | В директории .egg-info хранится мета-информация об установленном пакете. :: ├── vision.egg-info ├── PKG-INFO # Информация о пакете ├── SOURCES.txt # Информация о содержимом пакета - какие модули в него входят ├── dependency_links.txt # Информация о зависимостях этого пакета (если они присутствуют) └── top_level.txt # Название пакета Восстановление пакетов ====================== .. _PackageRestoring: Из репозитория -------------- Пакетный менеджер pip самостоятельно определяет и подгружает зависимости пакета (их мы указали в списке install\_requires в листинге setup.py выше). Для установки пакета со всеми его зависимостями достаточно ввести следующую команду: :: pip install -i http://[login]:[password]@172.28.1.250:8083/repository/pypi-releases/simple/ --trusted-host 172.28.1.250 vision Из локального файла (wheel) --------------------------- Установка пакета из локального файла (wheel) ``.whl`` ничем не отличается от обычной установки пакета, разве что при установке wheel нужно установить пакет ``wheel`` :: pip install wheel и в команде ``pip install`` указать путь к этому файлу :: pip install ./dist/vision-1.0-py3-none-any.whl Так, если в wheel'е уже на этапе заполнения файла setup.py были прописаны зависимости в install\_requires, то pip подгрузит их для wheel'а автоматически. Из локальной директории. ------------------------ Допустим, у нас есть wheel'ы в локальной директории ``/home/thealchemist/wheels/``, и один из них - vision. Чтобы установить конкретный пакет - vision - мы выполняем следующую команду :: pip install --no-index --find-links /home/thealchemist/wheels/ vision Ключ ``--no-index`` указывает pip не обращаться к Python Global PyPI, а устанавливать исключительно из локальной среды. Чтобы установить все wheel'ы из директории, выполняем команду :: pip install --no-index --find-links /home/thealchemist/wheels/ *.whl Важные замечания ---------------- .. _Notes: \_\_init\_\_.py и как устроен импорт подмодулей в Python ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Допустим, у нас есть следующая структура: :: ├── actions │   ├── __init__.py │   ├── exists_on_screen.py │   ├── find_on_screen.py │   ├── read_text_from_image.py │   ├── recognize_by_template.py │   ├── wait_for_action.py │   └── wait_for_disappear.py Предположим, мы хотим иметь возможность импортировать наши экшны следующим образом (просто и логически верно): .. code:: python from actions import read_text_from_image print(read_text_from_image('awesome_image.jpg')) | Дело в том, что любой ``.py`` файл в Python является модулем. | Так как у нас уже присутствует файл ``read_text_from_image.py``, | то вместо конкретной функции у нас будет импортирован целый модуль, соответственно при вызове модуля будет выброшена ошибка (модуль нельзя вызвать, это не метод). Чтобы избежать таких неприятностей, мы в \_\_init\_\_.py прописываем следующие импорты: .. code:: python # Переносы сделаны для наглядности объяснения from # Из .read_text_from_image # модуля read_text_from_image в текущей директории (точка перед названием модуля) import # мы импортируем read_text_from_image # метод/функцию/объект read_text_from_image # остальные импорты from .exist_on_screen import exists_on_screen from .recognize_by_template import recognize_by_template # и так далее Важно отметить, что из модуля мы импортируем **метод** с *таким же* названием. Поставка Python для инсталлятора [Windows] ========================================== .. _WindowsInstallerBundling: Все исполняемые среды Python любой версии можно загрузить в локальный репозиторий с https://www.python.org/ftp/python/ Добавляем Python в инсталлятор следующим образом: Скачиваем установочный exe с питоньей средой исполнения с глобального / нашего локального репозитория, и запаковываем его в Inno Setup (где в процессе установки всего потом вызываем). Для тихой установки Python вызываем инсталлятор с флагом /quiet и дополнительными параметрамии при необходимости. Список всех дополнительных параметров можно найти на https://docs.python.org/3.6/using/windows.html#installing-without-ui Для того, чтобы свежеустановленный Python заработал, надо создать для него локальную переменную среды, в которую прописать путь к ``%PYTHON_DIR%/bin/``, где ``%PYTHON_DIR%`` - место, куда распакован архив с Python. Дальше можно вызывать наш конкретный свежеустановленный Python следующей командой (предположим, что переменная среды называется ``PYLOCALENV``) :: %PYLOCALENV%/python --version Соответственно все команды вроде ``pip``, ``easy_install``, ``twine`` вызываем с контекстом %PYLOCALENV% :: %PYLOCALENV%/pip install requests Все установленные пакеты в локальный Python можно найти по пути %PYTHON_DIR%/site-packages/ (в случае портативной установки) или %APPDATA%/Roaming/Python/Python38/site-packages/ (в случае установки с инсталлятора) Генерация protobuf классов ========================== .. _PythonProtoClassesGenerating: Разберем создание протобафного класса. В качестве примера клонируем репозиторий: :: git clone http://git-server/contracts/protocols.git -b master Все команды выполняются в cmd в Windows. В каждой директории с proto файлами создаём папку python, в которой будут хранится сгенерированные классы: :: cd protocols cd proto\Base mkdir python protoc -I=. -I=.\.. -I=.\..\..\include\ --python_out=python *.proto cd ..\ExecutorAgent mkdir python protoc -I=. -I=.\.. -I=.\..\..\include\ --python_out=python *.proto cd ..\AgentOrchestrator mkdir python protoc -I=. -I=.\.. -I=.\..\..\include\ --python_out=python *.proto cd ..\OrchestratorUI mkdir python protoc -I=. -I=.\.. -I=.\..\..\include\ --python_out=python *.proto cd ..\StudioAgent mkdir python protoc -I=. -I=.\.. -I=.\..\..\include\ --python_out=python *.proto В ``.gitlab-ci.yml`` команды будут аналогичными. Пример кода для генерации классов в ``.gitlab-ci.yml``: :: cd proto\Base mkdir python protoc -I=./ -I=./../ -I=./../../include/ *.proto --python_out=./python/