Python LiveReload: автоматическая перезагрузка страницы

Python LiveReload — это вспомогательная утилита для веб-разработчиков. Запускается как сервер в директории проекта и отслеживает изменения файлов. Работает в паре с LiveReload расширением вашего браузера. Это позволяет избавиться от утомительного нажатия F5 или Ctrl+R в браузере после каждой правки кода.

Установка сервера

pip install livereload

Установка расширения для Chrome

https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei

Запуск

cd ~/my/project/path
livereload

Сервер по умолчанию слушает порт 35729. То есть, чтобы все завелось, нужно открыть ссылку http://localhost:35729/ в браузере.

Дополнительно

livereload --help

Документация: http://livereload.readthedocs.org/en/latest/

Страница проекта: https://github.com/lepture/python-livereload

Автор проекта: Hsiaoming Yang

Vim: режим Netrw

NERDTree не нужен. Схожую функциональность полностью обеспечивает встроенный в Vim режим Netrw.

Вход в режим:

:e .

Или сразу запустить Vim из консоли в режиме Netrw:

vim .

Древовидное отображение листинга директории:

let g:netrw_liststyle = 3

Игнорирование файлов и директорий (паттерны регулярок через запятую):

let g:netrw_list_hide = '.*\.swp$,.*\.pyc$,^\.git/$,^tags$,^\.vagrant/$'

Циклическое переключение между режимами отображения игнорируемых файлов — <a>.

Vim: совместное использование YouCompleteMe и Virtualenv

Способ 1

Запускать Vim из терминала с уже активированным окружением Virtualenv.

Способ 2

В Vim после активации виртуального окружения с помощью vim-virtualenv:

:VirtualEnvActivate myenv

перезапустить серверную часть YouCompleteMe:

:YcmRestartServer

Библиотека Requests: быстрый старт

Готовы начать? Эта страница дает достаточное представление о том, как начать работу с Requests.

Во-первых, убедитесь, что:

  1. Requests установлена
  2. Requests обновлена

Давайте начнем с нескольких простых примеров.

Создание запроса

Создание запроса с помощью Requests — это очень просто.

Начните с импорта модуля Requests:

>>> import requests

Теперь попробуем получить веб-страницу. Например, давайте получим публичный тайм-лайн GitHub.

>>> r = requests.get('https://api.github.com/events')

Теперь у нас есть объект Response с именем r. Мы можем получить всю необходимую информацию из этого объекта.

Простой API Requests означает, что все формы HTTP запросов являются очевидными. Например, вот как вы можете сделать HTTP POST запрос:

>>> r = requests.post("http://httpbin.org/post")

Круто? А как насчет других типов HTTP запроса: PUT, DELETE, HEAD и OPTIONS? Их выполнить так же просто:

>>> r = requests.put("http://httpbin.org/put")
>>> r = requests.delete("http://httpbin.org/delete")
>>> r = requests.head("http://httpbin.org/get")
>>> r = requests.options("http://httpbin.org/get") 

Это уже хорошо. Даже здорово. Но это далеко не все из того, что может делать Requests.

Передача параметров в URL

Часто вы хотите послать какие-то данные в строке запроса URL. Если вы строите URL вручную, то эти данные будут представлены в нем в виде пар ключ-значение после знака вопроса. Например, httpbin.org/get?key=val. Requests позволяет передать эти аргументы в качестве словаря, используя аргумент params. В качестве примера, если вы хотите передать key1=value1 и key2=value2 ресурсу httpbin.org/get, вы должны использовать следующий код:

>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http://httpbin.org/get", params=payload)

Вы можете видеть, что URL был закодирован правильно:

>>> print(r.url)

http://httpbin.org/get?key2=value2&key1=value1

Заметим, что любой ключ словаря, значение которого None, не будет добавлен к строке запроса URL.

Содержимое ответа

Мы можем читать содержимое ответа сервера. Рассмотрим тайм-лайн GitHub снова:

>>> import requests
>>> r = requests.get('https://api.github.com/events') 
>>> r.text 
u'[{"repository":{"open_issues":0,"url":"https://github.com/... 

Requests будет автоматически декодировать содержимое ответа сервера. Большинство Unicode кодировок без проблем декодируются.

Когда вы делаете запрос, Requests делает предположение о кодировке, основанное на заголовках HTTP. Кодировка текста, угаданная Requests, используется при обращение к r.text. Вы можете узнать, какую кодировку использует Requests, и изменить её воспользовавшись свойством r.encoding:

>>> r.encoding
'utf-8' 
>>> r.encoding = 'ISO-8859-1' 

Если вы измените кодировку, Requests будет использовать новое значение r.encoding всякий раз, когда вы будете использовать r.text. Вы можете сделать это в любой ситуации, где нужна более специализированная логика работы с кодировкой содержимого ответа. Например, в HTML и XML есть возможность задавать кодировку прямо в теле документа. В подобных ситуациях вы должны использовать r.content, чтобы найти кодировку, а затем установить r.encoding. Это позволит вам использовать r.text с правильной кодировкой.

Requests может также использовать пользовательские кодировки в случае, если вы в них нуждаетесь. Если вы создали свою собственную кодировку и зарегистрировали её в модуле codecs, вы можете просто использовать название кодека в качестве значения r.encoding, и Requests будет работать с этой кодировкой для вас.

Бинарное содержимое ответа

Вы также можете получить доступ к телу ответа в виде байтов для нетекстовых запросов:

>>> r.content 
b'[{"repository":{"open_issues":0,"url":"https://github.com/... 

Передача со сжатием gzip и deflate автоматически декодируется.

Например, чтобы создать изображение из бинарных данных, возвращаемых в ответ на запрос, вы можете использовать следующий код:

>>> from PIL import Image 
>>> from StringIO import StringIO 
>>> i = Image.open(StringIO(r.content)) 

JSON содержимое ответа

С библиотекой Requests также поставляется встроенный JSON декодер на случай, если вы имеете дело с данными в формате JSON:

>>> import requests 
>>> r = requests.get('https://api.github.com/events') 
>>> r.json() 
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/... 

В случае, если декодирование JSON не удается, r.json выбрасывает исключение. Например, если приходит ответ с кодом статуса 401 (неавторизованный), попытка обращения к r.json выбрасывает исключение ValueError: No JSON object could be decoded.

Необработанное содержимое ответа

В редких случаях, когда вы хотите получить доступ к сырому ответу сервера на уровне сокета, вы можете обратиться к r.raw. Если вы хотите сделать это, убедитесь, что вы установили stream=True в вашем первом запросе. После этого вы уже можете проделать следующее:

>>> r = requests.get('https://api.github.com/events', stream=True) 
>>> r.raw 
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810> 
>>> r.raw.read(10) 
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03' 

Теперь вы можете использовать подобный код как шаблон, чтобы сохранить получаемый поток в файл:

>>> with open(filename, 'wb') as fd: 
>>>     for chunk in r.iter_content(chunk_size): 
>>>         fd.write(chunk) 

Использование Response.iter_content будет обрабатывать многое из того, с чем бы вам пришлось иметь дело при использовании Response.raw напрямую. Описанное выше является предпочтительным и рекомендуемым способом извлечения содержимого при потоковой загрузке.

Пользовательские HTTP заголовки

Если вы хотите добавить HTTP-заголовки в запрос, просто передайте соответствующий словарь в параметре headers.

Например, мы не указали заголовок content-type в предыдущем примере:

>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'} 
>>> headers = {'content-type': 'application/json'}
>>> r = requests.post(url, data=json.dumps(payload), headers=headers)

Более сложные POST запросы

Зачастую вы хотите послать некоторые form-encoded данные также как это делается в HTML форме. Чтобы сделать это, просто передайте соответствующий словарь в аргументе data. Ваш словарь данных в таком случае будет автоматически закодирован как HTML форма, когда будет сделан запрос:

>>> payload = {'key1': 'value1', 'key2': 'value2'} 
>>> r = requests.post("http://httpbin.org/post", data=payload) 
>>> print(r.text) 
{ ... "form": { "key2": "value2", "key1": "value1" }, ... } 

Но есть много случаев, когда вы можете захотеть отправить данные, которые не закодированы методом form-encoded. Если вы передадите в запрос строку вместо словаря, то данные будут отправлены в неизменном виде.

Например, API v3 GitHub принимает JSON-закодированные POST/PATCH данные:

>>> import json url = 'https://api.github.com/some/endpoint' 
>>> payload = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(payload)) 

Как послать Multipart-Encoded файл

Requests позволяет легко послать на сервер Multipart-Encoded файлы:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "<censored...binary...data>" }, ... } 

Вы можете установить имя файла, content-type и заголовки в явном виде:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})} 
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "<censored...binary...data>" }, ... } 

Если вы хотите, вы можете отправить строки, которые будут приняты в виде файлов:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "some,data,to,send\nanother,row,to,send\n" }, ... } 

В случае, если вы отправляете очень большой файл как multipart/form-data, вы можете захотеть отправить запрос потоком. По умолчанию requests не поддерживает этого, но есть отдельный пакет, который это делает — requests-toolbelt. Ознакомьтесь с документацию Toolbelt.

Для отправки нескольких файлов в одном запросе, обратитесь к дополнительной документации.

Коды состояния ответа

Мы можем проверить код состояния ответа:

>>> r = requests.get('http://httpbin.org/get') 
>>> r.status_code 
200 

Для удобства Requests также поставляется со встроенным объектом подстановок кодов состояния:

>>> r.status_code == requests.codes.ok 
True 

Если мы сделали плохой запрос (ошибка 4XX клиента или ошибка 5XX ответа сервера), то мы можем возбудить исключение с помощью Response.raise_for_status() :

>>> bad_r = requests.get('http://httpbin.org/status/404') 
>>> bad_r.status_code 
404
>>> bad_r.raise_for_status() 
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error 

Но если status_code для г оказался 200, то когда мы вызываем raise_for_status() мы получаем:

>>> r.raise_for_status() 
None 

Это значит, что все в порядке.

Заголовки ответов

Мы можем просматривать заголовки ответа сервера, используя словарь Python:

>>> r.headers { 'content-encoding': 'gzip', 'transfer-encoding': 'chunked', 'connection': 'close', 'server': 'nginx/1.0.4', 'x-runtime': '148ms', 'etag': '"e1ca502697e5c9317743dc078f67693f"', 'content-type': 'application/json' } 

Однако это словарь особого рода: он сделан специально для HTTP заголовков. Согласно RFC 7230, имена заголовков HTTP нечувствительны к регистру.

Таким образом, мы можем получить доступ к заголовков с капитализацией или без, если захотим:

>>> r.headers['Content-Type'] 
'application/json' 
>>> r.headers.get('content-type') 
'application/json' 

Cookies

Если ответ содержит cookie, вы можете быстро получить к ним доступ:

>>> url = 'http://example.com/some/cookie/setting/url' 
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name'] 
'example_cookie_value' 

Для отправки собственных cookie на сервер, вы можете использовать параметр cookies:

>>> url = 'http://httpbin.org/cookies' 
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies) 
>>> r.text 
'{"cookies": {"cookies_are": "working"}}' 

Редиректы и история

По умолчанию Requests будет выполнять редиректы для всех HTTP глаголов, кроме HEAD.

Мы можем использовать свойство history объекта Response, чтобы отслеживать редиректы.

Список Response.history содержит объекты Response, которые были созданы во время выполнения запроса. Список сортируется от более ранних к более поздним ответам.

Например, GitHub перенаправляет все HTTP запросы на HTTPS:

>>> r = requests.get('http://github.com') 
>>> r.url 'https://github.com/' 
>>> r.status_code 
200 
>>> r.history 
[<Response [301]>] 

Если вы используете GET, OPTIONS, POST, PUT, PATCH или DELETE, вы можете отключить обработку редиректов с помощью параметра allow_redirects:

>>> r = requests.get('http://github.com', allow_redirects=False) 
>>> r.status_code 
301 
>>> r.history 
[] 

Если вы используете HEAD, вы можете включить обработку редиректов:

>>> r = requests.head('http://github.com', allow_redirects=True) 
>>> r.url 'https://github.com/' 
>>> r.history 
[<Response [301]>]

Тайм-ауты

Вы можете сказать Requests прекратить ожидание ответа после определенного количества секунд с помощью параметра timeout:

>>> requests.get('http://github.com', timeout=0.001) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

Примечание: timeout это не ограничение по времени полной загрузки ответа. Исключение возникает, если сервер не дал ответ за timeout секунд (точнее, если ни одного байта не было получено от основного сокета за timeout секунд).

Ошибки и исключения

В случае неполадок в сети (например, отказа DNS, отказался соединения и т.д.), Requests возбудит исключение ConnectionError.

В более редком случае неверного HTTP ответа, Requests возбудит исключение HTTPError.

Если превышено время ожидания ответа, возбуждается исключение Timeout.

Если запрос превышает заданное значение максимального количества редиректов, то возбуждается исключение TooManyRedirects.

Все исключения, которые возбуждает непосредственно Requests, унаследованы от requests.exceptions.RequestException.

Готовы для большего?

Ознакомьтесь с дополнительным разделом документации.

Эта статья — перевод официальной документации Requests. Оригинал: Quickstart — Requests documentation.

Django: загрузка FileField и ImageField из файловой системы

Не знаю как вы, а я чаще стал сталкиваться с ситуацией, когда мне нужно программно загрузить файл из локальной файловой системы или с помощью удаленного URL в модель Django. Это может понадобиться в контексте работы в оболочке Python, исполнения отдельного скрипта или команды управления Django.

В типичном веб-приложении такая задача встречается нечасто. Я заметил, что мы это делаем, когда переносим сайт с какой-то другой технологии на Django, и привязываем изображение к некой контентной сущности.

Если вы поищете ответ как сделать это в Google, то обнаружите документацию Django File Uploads, File objects и Managing Files, которые сами по себе отличные. Тем не менее они не содержат последовательного руководства для решения нашей задачи.

Первый порыв — присвоить полю модели путь к файлу, но это ведет к потере информации об относительном пути к файлу, в которой нуждается Django.

Я выделил время, чтобы написать этот пост в основном для собственного блага, чтобы не искать заново решение в будущем. Но, надеюсь, вы также получите некоторую пользу.

Этапы загрузки локального файла в Django ImageField

Действия, которые необходимо выполнить:

  1. Получить файл (если он удаленный) и сохранить его локально
  2. Открыть файл как обычный объект файла Python
  3. Конвертировать открытый файл в Django FIle
  4. Привязать его к полю вашей модели

Пример модели

Предположим, у нас есть модель вроде этой:

from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=100) 
    logo = models.ImageField()

Давайте создадим запись для RevSys, скачав логотип с помощью библиотеки requests:

import requests
from django.core.files import File

from .models import Company

r = requests.get(“http://media.revsys.com/img/revsys-logo.png”)

with open(“/tmp/revsys-logo.png”, “wb”) as f:
    f.write(r.content)

reopen = open(“/tmp/revsys-logo.png”, “rb”)
django_file = File(reopen)

revsys = Company()
revsys.name = “Revolution Systems”
revsys.logo.save(“revsys-logo.png”, django_file, save=True)

Последняя строка здесь самая важная. Методу save передается три аргумента:

  1. Относительный путь и имя файла внутри MEDIA_ROOT.
  2. Файл, открытый с помощью File объекта Django.
  3. Логическое значение, показывающее хотим ли мы сохранить экземпляр revsys модели Company после того, как изображение сохранено.

Иногда, если вы работаете с набором изображений, вам требуется распарсить URL или пути к файлам для извлечения имен файлов. Нижеследующий код послужит отправной точкой. Обратите внимание, что тут используется Python 3:

import os 
from urllib.parse import urlparse

# from urlparse import urlparse for Python 2.7

url = “http://media.revsys.com/img/revsys-logo.png” 
filename = os.path.basename(urlparse(url).path)

# This returns ‘revsys-logo.png’ from the URL

…

revsys.logo.save(filename, django_file, save=True)`

Надеюсь, это вам поможет!

Эта статья перевод. Оригинал: Loading Django FileField and ImageFields from the file system

Как изменить размер шрифта в сайдбаре Sublime Text 3

  1. Открыть в меню Sublime Text -> Preferences -> Browse Packages.
  2. В открывшемся файловом проводнике перейти в User.
  3. Создать в этой директории файл Default.sublime-theme со следующим содержимым и подходящими значениями опций.

    [
        {
            "class": "sidebar_label",
            "color": [0, 0, 0],
            "font.bold": false,
            "font.size": 12
        },
    ]
    

Как добавить непустую директорию в Git без содержимого

В дополнение к задачке про пустую директорию в Git. Что делать, если нужно добиться обратного эффекта? Например, нужно чтобы в репозитории присутствовала директория с бинарными данными, но отсутствовали сами эти данные.

Нужно создать в директории файл .gitignore с таким содержимым:

!.gitignore
*

Очевидно, что это значит игнорировать все, кроме самого гитигнора.

Django — ошибка во время миграции

Во время миграции схемы базы данных для модели, в которую добавлено ForeignKey поле, может возникнуть ошибка вида:

django.db.utils.OperationalError: (1005, "Can't create table '...' (errno: 150)")

Это происходит из-за разных типов хранилищ таблиц (MyISAM/InnoDB). В таком случае MySQL не может корректно создаться индекс по внешнему ключу.

Следует руками привести обе таблицы к типу хранилища InnoDB, сделав ALTER TABLE, и повторить миграцию.

Как добавить пустую директорию в Git

Git отслеживает изменения только непосредственно файлов и не хранит в индексе изменения директорий как таковых. Если по какой-то причине надо добавить в репозиторий пустую директорию, существует соглашение о создании в ней пустого файла с именем .gitkeep или .keep. Добавление в индекс файла сохранит информацию и о директории его содержащей.

Чеклисты

Я стартовал новый проект Чеклисты.

Чеклисты — незаменимый инструмент микропланирования и контроля, широко используемый как профессионалами в разнообразных областях, так и в быту.

Под капотом Django, обивка Bootsrap, рулевая jQuery.