Skip to content

Instantly share code, notes, and snippets.

@s3rgeym
Last active February 10, 2022 10:01
Show Gist options
  • Save s3rgeym/49346497b176a5c12c1f43ad30102eef to your computer and use it in GitHub Desktop.
Save s3rgeym/49346497b176a5c12c1f43ad30102eef to your computer and use it in GitHub Desktop.
Разработка на Python и все, что с ней связано

Терминология

Различие между модулем, пакетом, библиотекой и фреймворком

Модуль – это единственный файл с функциями и классами.

Пакет – это два и более модулей. В python файловая структура пакета выглядит так:

package/
  __init__.py
  foo.py
  bar.py

Библиотека – набор пакетов.

Фреймворк – набор библиотек.

Работа с pip

# Установить пакет
pip install <package>

# Установить пакет для текущего пользователя
# Для работы скриптов требует строки в .zshrc «export PATH=$HOME/.local/bin:$PATH»
pip install --user <package>

# Удалить пакет
pip uninstall <package>

# Удалить все пакеты
pip freeze | xargs pip uninstall -y

# Обновить пакет
pip install -U <package>

# Сохранить пользовательские пакеты вместе с их версиями в файл
pip freeze > requirements.txt

# Установить список пакетов из файла
pip install -r requirements.txt

Pyenv

Это скрипт на баше, который позволяет управлять версиями Python.

Проблема: в дистрибутивах Linux часто по-умолчанию установлена довольно старая версия интерпретатора. Ее замена может повляить на работоспособность системы. Если нужна специфическая версия Python, то ее можно поискать в репозиториях, либо собрать самому. Вместо команды python придется использовать python3.6, вместо pip – pip3 и т.д.

Перед началом установки следует установить все необходимые зависимости, следуя этой инструкции.

# Лучше всего ставить pyenv по-старинке, так как при установке через пакетные менеджеры не работает обновление
curl https://pyenv.run | bash

# Добавляем в конец файла эти строки
$ nano .bashrc
...
export PATH=$HOME/.pyenv/bin:$PATH
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

# Для zsh редактируем переменную
$ nano .zshrc
plugins=(
  ...
  pyenv
  ...
)

# Без этого не будет работать автоподстановка для команд
autoload -Uz compinit && compinit


# Ставим плагин для обновления
$ git clone git://github.com/pyenv/pyenv-update.git $(pyenv root)/plugins/pyenv-update

# Периодически обновляем pyenv (через какое-то время после установки). Можно отредактировать .zshrc и включить автоматическое обновление
$ pyenv update

# Смотрим доступные версии Python для установки
$ pyenv install --list

# Устанавливаем определенную версию Python
$ pyenv install 3.7.3

# Делаем ее глобальной
$ pyenv global 3.7.3

# Или локальной: команда python будет вызывать python 3.7.3 в текущем каталоге и ВО ВСЕХ ДОЧЕРНИХ!!!
$ pyenv local 3.7.3

# Проверяем
$ python
Python 3.7.3 (default, Mar 26 2019, 21:43:19)
[GCC 8.2.1 20181127] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()

Виртуальные окружения

Проблема: при разработке может возникнуть конфликт версий библиотек, так как pip позволяет установить только одну версию библиотеки.

# Создаем виртульное окружение
$ python -m venv .venv

# Активируем его
$ . .venv/bin/activate

# Устанавливаем модули
(.venv) $ pip install -r requirements.txt

# Дактивируем виртуальное окружение
(.venv) $ deactivate

Управление зависимостями

Как выглядит типичный процесс разработки:

# Устанавливаем кучу модулей
$ pip install ...
# Пишем кучу кода
# Сохраняем зависимости
$ pip freeze > requirements.txt
# Публикуем проект
$ git add .
$ git commit -m "some changes"
$ git push

# На сервере
$ git pull
$ pip install -r requirements.txt

При таком подходе возникает проблема: управление зависимостями для продакшена и разработки. Во время разработки используются, например, pylint и pytest. Эти модули не нужны в продакшене. pip freeze по-мимо самих библиотек сохраняет кучу зависимостей, что не позволяет убирать лишнее из requirements.txt вручную.

Решения:

  1. Использовать pip-tools. pip-compile генерирует requirements.txt из requirements.in и setup.py. Т.е. в нем предлагается вручную прописывать зависимости.
  2. pipenv – очередное из серии %something% for Humans. Проблему в принципе решает. Аналог npm. Умеет создавать виртуальные окружения. Небольшой туториал pyenv+pipenv.
  3. poetry. То же самое, что и pipenv только функционал по-богаче.

Poetry

# Устанавливаем модуль
$ pip install poetry

# Если не используется Pyenv, то Poetry лучше установить в домашний каталог 
$ pip install -U poetry

# Добавляеми автоподстановку для Oh-My-Zsh
$ mkdir $ZSH/plugins/poetry
$ poetry completions zsh > $ZSH/plugins/poetry/_poetry
$ nano ~/.zshrc

# Добавляем плагин
$ nano .zshrc
plugins=(
  ...
  poetry
  ...
)

# Виртуальное окружение будет создаваться в каталоге проекта
$ poetry config settings.virtualenvs.path .venv

# Путь по-умолчанию: ~/.cache/pypoetry/virtualenvs/<projectname>-py<vresion>

# Создаем новый проект
$ poetry new <project-name>

# Для создания проекта в интерактивном режиме
$ poetry init

$ cd <project-name>

# Перед добавление библиотек через poetry нужно активировать виртуальное окружение
# иначе он добавит модуль в зависимости и установит его глобально. Это баг? 
# Может какие-то системные переменные окружения не установлены?
$ source $(poetry env info -p)/bin/activate

# Добавляем зависимости
$ poetry add requests

# Добавляем библиотеку с github
$ poetry add Flask-Restless --git https://github.com/jfinkels/flask-restless.git

# Добавляем зависимости для разработки
$ poetry add pylint --dev

# Удаление
$ poetry remove <library>

# Обновление
$ poetry update <library>

# Запуск приложения
$ poetry run python app.py

# Запуск тестов
$ poetry run python -m unittest discover

# Собрать пакет
$ poetry build

# Загрузить собранный пакет в pypi
$ poetry publish

# Poetry не читает ~/.pypirc, поэтому нужно отредактировать два файла с настройками

$ nano ~/.config/pypoetry/config.toml
[repositories]
pypi = {url = "https://upload.pypi.org/legacy/"}
testpypi = {url = "https://test.pypi.org/legacy/"}

$ nano ~/.config/pypoetry/auth.toml
[http-basic]
pypi = {username = "myuser", password = "topsecret"}
testpypi = {username = "myuser", password = "topsecret"}

# Если этих файлов не существует, то нужно выполнить, эта команда создаст настройки
$ poetry config       

# Покажет все настройки
$ poetry config --list

# Путь до виртуального окружения
$ poetry run which python

# Показать зависимости
$ poetry show

# Активировать virtualenv
$ poetry shell

# Запустить тесты
$ poetry run pytest tests

# Создание скриптов
$ nano pyproject.toml
[tool.poetry.scripts]
# <package>:<function>
hello = 'scripts:hello'

$ nano scripts/__init__.py
def hello(*args, **kw):
  print('Hello, World!')

# Запуск скрипта
$ poetry run hello

# Установить пакет в виртуальное окружение
$ poetry install

# То же самое, но без зависимостей для разработчика
$ poetry install --no-dev

# Обновить poetry
$ poetry update:self

Чтобы загружать пакеты в PyPi нужно:

# Добавить pypi в репозитории
$ vim ~/.config/pypoetry/config.toml
...
[repositories]
pypi = {url = "https://upload.pypi.org/legacy/"}

# Прописать свои креды
$ vim ~/.config/pypoetry/auth.toml
[http-basic]
pypi = {username = "tz4678", password = "***"}

Ссылки:

Создаем консольную утилиту на Python с помощью Poetry

Обновим pip:

pip install --upgrade pip

Установим Poetry:

pip install -U poetry

Создадим новый проект:

poetry new hello-world

Перейдем в каталог проекта:

cd hello-world

Отредактируем hello_world/__init__.py:

__version__ = '0.1.0'


def hello(*args, **kw):
  print('Hello, World!')

Изменим файл pyproject.toml, добавив пару строк:

[tool.poetry.scripts]
# <package>:<function>
hello = "hello_world:hello"

Проверим работу скрипта:

$ poetry run hello
Hello, World!

Это обычный консольный скрипт:

sergey@sergey-pc  ~/Development/hello-world  . ~/.cache/pypoetry/virtualenvs/hello-world-py3.7/bin/activate
(hello-world-py3.7)  sergey@sergey-pc  ~/Development/hello-world  hello
Hello, World!

Перед публикацией проекта нужно зарегистрироваться на pypi , а после регистрации создать файл ~/.pypirc с кредами:

[pypi]
username: <username>
password: <password>

Публикация проекта проста:

poetry publish

Загрузка проектов в pypi

Для начала нужно зарегистрироваться на портале. Это можно сделать через веб-сайт либо консоль.

После регистрации создаем файл ~/.pypirc:

[distutils]
index-servers =
    pypi

[pypi]
repository: https://pypi.python.org/pypi
username: <username>
password: <password>

Расписывать процесс создания setup.py нет смысла, в виду того что с современными пакетными менеджерами типа Poetry, которые все это автоматизируют, в этом нет смысла. Для общего развития можно почитать инструкцию с официального сайта.

Логирование

import logging

import logging
# This sets the root logger to write to stdout (your console)
logging.basicConfig()

# By default the root logger is set to WARNING and all loggers you define
# inherit that value. Here we set the root logger to NOTSET. This logging
# level is automatically inherited by all existing and new sub-loggers
# that do not set a less verbose level.
logging.root.setLevel(logging.NOTSET)

# The following line sets the root logger level as well:
logging.basicConfig(level=logging.NOTSET)

# You can either share the `logger` object between all your files or the
# handle `my-app`. The result is the same.
logger = logging.getLogger("my-app")

logger.info("this will get printed")

# In large applications where you would like more control over the logging,
# create sub-loggers from your main application logger.
component_logger = logger.getChild("component-a")
component_logger.info("this will get printed with the prefix `my-app.component-a`")

# If you wish to control the logging levels, you can set the level anywhere in the
# hierarchy:
#
# - root
#   - my-app
#     - component-a
#

# Example for development:
logger.setLevel(logging.DEBUG)

# If that prints too much, enable debug printing only for your component:
component_logger.setLevel(logging.DEBUG)

# Create a file handler
handler = logging.FileHandler('hello.log')
handler.setLevel(logging.INFO)

# Create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')  # Gets applied to all the loggers
handler.setFormatter(formatter)

# add the handlers to the logger
logger.addHandler(handler)

# Types : debug, info, warning, error and critical
logger.info('Basic Info, Routine Calls')
logger.debug('More detailed data values for debugging')
logger.warn('Important informations')
logger.error('Error Signal') #, exc_info=True)  #for giving execution trace
logger.critical('critical message')

VScode

VSCode – это лучший текстовый редактор для программирования на Python. Он бесплатен и вместе с расширениями превращается в полноценную IDE.

Удаленная отладка

Почитать это. А тут есть пример с gunicorn.

Автоформатирование кода

Доступно после установки расширения IntelliCode.

$ poetry add autopep8 --dev

Пример настроек:

{
  ...
  "python.formatting.autopep8Args": [
    "--indent-size=2",
    "--max-line-length=120",
    "--experimental"
  ],
  "python.formatting.provider": "autopep8",
  ...
}

Настройки можно как для проекта задать, так и глобально. Форматирование будет работать по сочетанию Ctrl + Shift + I. Подробнее.

Секреты Python

== vs is

is проверяет ссылаются ли аргументы на один объект, а == — на одинаковое значение, поэтому

Порядок наследования

class Foo:

  def foo(self):
    print('foo')


class Bar:

  def foo(self):
    print('bar')


class Baz(Foo, Bar):
  ...


Baz().foo()  # prints foo


# Правильно в обратном порядке
class Baz(Bar, Foo):
  ...
  

Baz().foo()  # prints bar

Порядок применения декораторов

def foo(f):
  def wrap(*args, **kw):
    print('foo')
    return f(*args, **kw)
  return wrap


def bar(f):
  def wrap(*args, **kw):
    print('bar')
    return f(*args, **kw)
  return wrap


@foo
@bar
def baz():
  ...


baz()

Вывод:

foo
bar

yield from

The explanation that yield from g is equivalent to for v in g: yield v

Сниппеты

# Декартово произведение множеств: находим все возможные компибаниции
>>> [a + b for a in 'ABC' for b in 'xy']
['Ax', 'Ay', 'Bx', 'By', 'Cx', 'Cy']
@s3rgeym
Copy link
Author

s3rgeym commented Jul 1, 2019

Парсим multipart/form-data

>>> from email import message_from_string
>>> message = message_from_string('Content-Type: multipart/form-data; boundary=1234567890\r\n\r\n--1234567890\r\nContent-Disposition: form-data; name="foo"\r\n\r\nbar\r\n--1234567890--')
>>> for part in message.get_payload(): print(part)
... 
Content-Disposition: form-data; name="foo"

bar

@s3rgeym
Copy link
Author

s3rgeym commented Jul 22, 2019

# Запускаем PyDoc сервер
$ pydoc -b
$ pydoc -p 3456

# Запуск IDLE
$ nohup idle {{filename}} &

@s3rgeym
Copy link
Author

s3rgeym commented Oct 3, 2019

Перед запуском poetry install нужно выполнить poetry shell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment