четверг, 25 апреля 2013 г.

Flask. Документация. Предисловие для опытных программистов

Один из дизайнерских подходов Flask - что простые задачи должны быть простыми, не требовать большого количества кода и при этом не должны ограничивать Вас. Поэтому Flask реализует некоторые решения, которые некоторые могут счесть внезапными или не ортодоксальными. Например, Flask использует локальные для потоков объекты, так что Вы не должны передавать объекты из функции в функцию в пределах запроса чтобы избежать проблем с потоками. Это общепринятый подход, но он требует корректного контекста запроса для внедрения зависимостей или если Вы хотите повторно использовать код, который использует значения, привязанные к запросу. Flask гордится этим, не скрывает этого и заявляет об этом и в коде и в докуменации.

Осторожная разработка для веба

Всегда, когда Вы создаёте веб-приложение, помните о безопасности.
Если Вы пишете веб-приложение, Вы, скорее всего, разрешаете пользователям регистрироваться и оставлять данные на вашем сервере. Пользователи доверяют Вам свои данные. И даже если Вы единственный, кто хранит данные в вашем приложении, Вы всё равно хотите чтобы ваши данные надёжно хранились.
К сожалению, есть множество способов скомпроментировать безопасность веб-приложений. Flask защитит Вас от наиболее популярных проблем современных веб-приложений: cross-site scripting (XSS). Пока Вы намеренно не пометите небезопасный HTML как безопасный, Flask, и работающий с ним движок шаблонов Jinja2, будут Вас прикрывать. Но всё равно у Вас ещё остаётся куча способов оставить дыры в приложении.
Документация будет предупреждать Вас о тех аспектах веб-разработки, которые требуют особого внимания к обеспечению безопасности. Некоторые из них гораздо сложнее, чем Вы могли бы подумать, и все мы иногда недооцениваем возможность эксплуатации уязвимости, пока какой-нибудь сообразительный парень не найдёт способ это сделать. И не думайте, что ваше приложение слишком малоценно, чтобы заинтересовать атакующего. В зависимости от типа атаки, есть вероятность того, что боты автоматически постараются заполнить вашу БД спамом, ссылками на вирусное ПО и т.п.
Flask не отличается от других фреймворков в том плане, что Вы, как разработчик, должны быть осторожны, учитывая возможные векторы атаки.

Статус Python 3

На данный момент сообщество Python работает над тем, чтобы библиотеки поддерживали новую версию Python. Хотя ситуация улучшается, тем не менее всё ещё есть несколько проблем, которые не позволяют нам до сих пор переключиться на Python3. Частично эти проблемы связаны  изменениями в языке, которые слишком долго остаются не пересмотренными, частично из-за того, что мы не до конца понимаем, как должно измениться API для учёта изменения подхода к юникоду в Python 3.
Werkzeug и Flask будут портированны на Python 3 как только будет надено решение этих проблем и мы предоставим советы по переходу на новую версию языка. До тех пор мы строго рекомендуем использовать Python 2.6 и 2.7 со включёнными предупреждениями Python 3 при разработке. Если Вы планируете переход на Python 3 в ближайшем будущем, мы крайне рекомендуем Вам прочитать "Как писать совместимый с будущим код Python"7
Продолжением является либо Установка, либо Быстрый старт

Flask. Документация. Предисловие


Прочитайте это перед тем, как Вы начнёте работать с Flask. Это наверняка ответит на некоторые ваши вопросы о целях этого проекта и поможет Вам понять, когда его стоит использовать, а когда нет.

Что означает "микро"?

Микро не означает, что всё ваше веб-приложение уместиться в один файл Python, хотя это и возможно. Кроме того, это не означает, что Flask не хватает какой-то функциональности. "Микро" в слове "микрофреймворк" означает, что Flask старается держать своё ядро простым, но расширяемым. Flask не будет принимать какие-то решения за Вас, например, какую БД использовать. Те решения, которые он делает, например, использование конкретного движка шаблонов, очень легко можно изменить. Всё остальное зависит полностью от Вас, так что Flask может быть всем, что Вам нужно и не быть тем, чем не нужен.
По умолчанию, Flask не включает слой абстракции БД, валидации форм или что-либо ещё, что может сделать другая существующая библиотека. Вместо этого Flask поддерживает расширения, которые позволяют добавить эту функциональность к вашему приложению, как если бы это было реализовано в самом Flask. Некоторые расширения предоставляют интеграцию с БД, валидацию форм, обработку загрузок, различные открытые технологии аутентификации и так далее. Flask может быть "микро", но готов к использованию в различных целях.

Конфигурация и допущения

Flask имеет много конфигурационных значений, с адекватными значениями по умолчанию, и использует некоторые допущения. В частности, предполагается, что шаблоны и статические файлы хранятся в подкаталогах в дереве приложения Python с именами templates и static соответственно. Хотя это и можно изменить, скорее всего Вы не будете это делать, особенно в самом начале.

Двигаемся дальше

После того, как Вы начали пользоваться Flask, Вы обнаружите большое количество доступных расширений, которые Вы можете интегрировать в ваш проект. Команда Flask просматривает эти расширения и следит за тем, чтобы их можно было использовать в новых релизах.
По мере роста вашего кода Вы можете выбрать любой дизайн кода, который Вам больше нравится. Flask продолжит предоставлять Вам лучшее, что может предоставить Python. Вы можете реализовать продвинутые шаблоны в SQLAlchemy или другом инструменте работы с БД, хранить данные не в релиационном хранилище и использовать преимущества инструментов, разработанных для WSGI - Python веб-интерфейса.
Flask включает множество хуков для настройки его поведения. Если Вам нужна более тонкая настройка, класс Flask может быть использован для создания подклассов. Если Вам это интересно, обратитесь к главе Becoming Big. Если Вам любопытны принципы дизайна Flask, обращайтесь к разделу Дизайнерские решения в Flask.

Обнаружение пакетов и доступ к ресурсам при помощи pkg_resources




Модуль pkg_resouces, распространяемый с setuptools, предоставляет API для библиотек Python для доступа к их файлам ресурсов и для предоставления возможности приложениям и фреймворкам автоматически обнаруживать плагины. Кроме того, он предоставляет поддержку времени выполнения для использования расширений C, находящихся в zip файле формата egg, поддержки слияния пакетов, которые имеют отдельно распространяемые модули или подпакеты, API для управления текущим "рабочим набором" активных пакетов Python.

Обзор

"Яйца" - это формат распространения модулей Python, похожий на jar в Java или gem в Ruby. Они отличаются от предыдщуего формата распространения Python тем, что он импортируемый (то есть, они могут быть добавлены в sys.path) и они "отыскиваемые", то есть, они содержат метаданные, которые однозначно идентифицируют их содержимое и зависимости, и потому могут быть автоматически обнаружены и добавлены в sys.path в ответ на простой запрос вроде "дайте мне всё, что мне нужно для использования поддержки PDF в docutils".
Модуль pkg_resources предоставляет средства времени выполнения для поиска, исследования (интроспекции), активирования и использования яиц и других "подключаемых" форматов распространения. Поскольку это новые концепции для Python (и не настолько хорошо проработаны и в других языках), неплохо бы сперва обозначить несколько специальных терминов для обсуждения "яиц" и того, как они могут быть использованы:
  • проект - библиотека, фреймворк, скрипт, плагин, приложение или набор данных или других ресурсов, или любая комбинация этих элементов. Предполагается, что проекты имеют уникальные имена, т.е. имена, зарегистрированные в PyPI. 
  • релиз - снапшот проекта на некий момент времени, определяемый идентификатором версии 
  • дистрибутив - файл или файлы, представляющие конкретный релиз 
  • импортируемый дистрибутив - файл или катаолг, который, если его поместить в sys.path, позволяет Python'y импортировать любые модули, расположенные в нём
  • дистрибутив-плагин - импортируемый дистрибутив, где имена файлов однозначно определяют их релиз (т.е. проект и версию), и чьё содержимое однозначно определяет какие релизы других проектов ему необходимы для работы.
  • экстра - необязательные возможности релиза, которые могут налагать дополнительные требования в процессе выполнения. Например, если поддержка pdf в docutils требует библиотеку поддержки pdf, docutils может объявить поддержку pdf как экстра и указать, какие ещё релизы проектов нужны для обеспечения этой функциональности
  • окружение - набор дистрибутивов потенциально доступный для импорта, но не обязательно активных. В окружении может присутствовать более одного дистрибутива (т.е. релиза) для данного проекта.
  • рабочий набор - набор дистрибутивов актуально доступных для импорта из sys.path. Максимум один дистрибутив (релиз) данного проекта может присутствовать в рабочем наборе, иначе нет однозначности того, что импортировать.
  • яйца - яйца - это дистибутив-плагин в одном из трёх форматов, поддерживаемых на данный момент pkg_resources. Есть собранные яйца, разрабатываемые яйца и яйца-ссылки. Собранные яйца - это каталоги или zip файлы с имененм, заканчивающимся на .egg и соответствующим договорённости об имени яиц, и содержащих подкаталог EGG-INFO. Разрабатываемые яйца - обычные каталоги кода Python c одним или более подкаталогом ИмяПроекта.egg-info. Яйца-ссылки - это файлы .egg-link, которые содержат имя собранного яйца или разрабатываемого яйца для того, чтобы обеспечить функционал символических ссылок на платформах, где нет встроенной поддержки такой функциональности.
(Более подробно эти термины и концепты описаны в обзоре архитектуры pkg_resources и Python Eggs).

Руководство разработчика

Этот раздел ещё не написан. Планируемое содержание:
  • Accessing Resources
  • Finding and Activating Package Distributions
    • get_provider()
    • require()
    • WorkingSet
    • iter_distributions
  • Running Scripts
  • Configuration
  • Namespace Packages
  • Extensible Applications and Frameworks
    • Locating entry points
    • Activation listeners
    • Metadata access
    • Extended Discovery and Installation
  • Supporting Custom PEP 302 Implementations
На данный момент пока обращайтесь к Руководству по API 

Руководство по API

Поддержка пакета пространства имён

Пакет пространства имён - это пакет, который содержит только другие пакеты и модули, без своего собственного содержимого. Такие пакеты могут быть разделены между множественными, отдельно упакованными дистрибутивами. Обычно Вам не требуется напрямую использовать API пакетов пространств имён; вместо этого Вы должны предоставить аргумент namespace_package функции setup() в файле setup.py вашего проекта. Более подробно это описано в документации setuptools о пакетах пространства имён.
Тем не менее, если по какой-то причине Вам нужно работать с пакетами пространства имён или напрямую изменить sys.path в процессе выполнения, Вам может помочь следющие API:
declare_namespace(name) - объявляет, что точечное имя пакета name является пакетом пространства имён и пакеты и модули, содержащиеся в нём, могут быть распространены между различными дистрибутивами. __path__ этого пакета будет расширен для добавления соответствующих пакетов из всех дистрибутивов sys.path'a, которые содержат пакет с этим именем. (Более точно, если импортер find_module(name) возвращает загрузчик, тогда он тоже будет исследован в поисках содержимого пакета.) Всякий раз, когда вызван ли метод activate(), проверяется наличие пакета пространства имён и обновляется его __path__.
fixup_namespace_backages(path_item) - объявляет, что path_item является новым добавленым элементом в sys.path, который может быть необходимо использовать для обновления существующего пакета пространства имён. Обычно он вызывается для вас при автоматическом добавлении яйца в sys.path, но если ваше приложение изменяет sys.path для добавления мест, которые могут содержать части пакетов пространства имён, Вам может потребоваться вызвать эту функцию чтобы убедиться, что они добавлены в существующий пакет пространства имён.
Хотя по умолчанию pkg_resources поддерживает пакеты пространства имён только для импортёров файловой системы и zip файлов, Вы можете расширить его для поддержки других импортёров, совместимых с PEP 302 при помощи функции register_namespace_handler(). См ниже раздел "Поддержка пользовательских импортёров".

Объект WorkingSet (рабочий набор)

Класс WorkingSet предоставляет доступ к коллекции активных дистрибутивов. В общем есть только один осмысленный экземпляр WorkingSet - тот, который представляет дистрибутивы, которые на данный момент активны в sys.path. Этот глобальный экземпляр доступен по имени working_set в модуле pkg_resources. Тем не менее, специализированные инструменты могут манитулировать рабочими наборами, которые не отвечают sys.path и потому создавать другие экземпляры WorkingSet.
Важно помнить, что глобальный объект working_set инициализируется из sys.path при первом импорте pkg_resources, а при всех последующих манипуляциях с sys.path ghb помощи API pkg_resources он лишь обновляется. Если Вы вручную изменяете sys.path, Вы должны вызвать соответствующий метод экземпляра working_set для его синхронизации. К сожалению, Python не предоставляет способа обнаружения произвольных изменений в объектах списка, вроде sys.path, так что pkg_resources не может автоматически обновить working_set на основании изменений в sys.path.
WorkingSet(entries=None) - создаёт WorkingSet из итерируемого аргумента entries. Если entries не передан, то по умолчанию используется значение sys.path при вызове конструктора.
Обратите внимание, что обычно Вы не будете создавать экземпляры WorkingSet, вместо этого Вы будете явно или неявно использовать глобальный экземпляр working_set. По большей части API pkg_resources разработан так, что working_set используется по умолчанию, так что Вам не нужно явно обращаться к нему большую часть времени.

Основные методы WorkingSet

Следующие методы объектов WorkingSet доступны так же в качестве функций уровня модуля в pkg_resources и они применяются к экземпляру по умолчанию working_set. Таким образом Вы можете, например, вызвать pkg_resources.require() как более короткую версию pkg_resources.working_set.require().
require(*requirements) - проверяет, что дистрибутивы, соответствующие requirements активны.
requirements должно быть строкой или (с возможностью вложения) последовательностью строк, определяющих требуемые дистрибутивы и версии. Возвращаемым значением является последовательность дистрибутивов, которые необходимо активировать для удволетворения требований; все относящиеся дистрибутивы включены, даже если они уже активированы в этом рабочем наборе.
Относительно синтаксиса передачи требуемых дистрибутивов смотрите раздел ниже "Парсинг требований".
В общем Вам не должно понадобиться использовать этот метод напрямую. Он больше предназначен для использования в быстрых скриптах и интерактивных интерпретаторах, чем для промышленного использования. Если Вы создаёте библиотеку или приложение, крайне рекомендуется, чтобы Вы создали скрипт setup.py, использующий setuptools, и объявили в нём все ваши требования. В этом случае такие инструменты, как EasyInstall, смогут автоматически определить требования вашего пакета и соответственно их обработать.
Обратите внимание, что вызов require('SomePackage') не установит SomePackage, если он отсутствует в системе. Если Вам нужно это сделать, Вы должны использовать метод resolve(), который позволяет Вам передать коллбек installer, который будет вызван в случае, если ye;ysq дистрибуетив не будет найден на локальной машине. Этот коллбэк может затем отобразить вопрос на продолжение операции, автоматически загрузить нужный дистрибутив или сделать ещё что-нибудь. Более подробно это описано в документакции метода resolve() и obtain() объекта Environment.
run_script(requires, script_name) - обнаруживает дистрибутивы, указаные в requires и затем запускает скрипт script_name. requires должно быть строкой, содержащей спецификаторы требований (подробнее - смотрите раздел ниже "Парсинг требований")
Скрипт, если он найден, будет выполнен в глобальном окружении вызывающего. Причина этого в том, что этот метод предназанчен для вызова из обёртывающего скрипта, который работает как прокси для "настоящего" скрипта в дистрибутиве. Скрипт-обёртка обычно не должен делать ничего, кроме вызова этой функции с корректными аргументами.
Если Вам нужно больше контроля над окружением выполнения скрипта, Вы, возможно, захотите использовать метод run_script() из Metadata API объекта Distribution.
iter_entry_points(group, name=None) -   выдаёт (yield) точки входа из group, соответствующие name.
Если name=None, выдаются все точки входа в group из всех дистрибутивов в рабочем наборе; в противном случае будут выданы только те, которые соответствуют и group и name. Точки входа выдаётся из активнрых дистрибутивов в том порядке, в котором эти дистрибутивы возникают в рабочем наборе. Для глобального working_set это должно быть тем же самым порядком, в котором они перечислены в sys.path. Обратите внимание, что точки входа, предоставляемые индивидуальными дистрибутивами, не имеют конкретного порядка.
Более подробно смотрите ниже, в разделе "Точки входа"

Методы и атрибуты WorkingSet

Эти методы используются для запросов или управления содержимым конкретного рабочего набора, так что они должны быть вызваны для конкретного экземпляра WorkingSet.
add_entry(entry) - добавляет путь к entries, находя там все дистрибутивы. Вы должны использовать его когда Вы добавляете элементы к sys.path и хотите, чтобы глобальный working_set отражал эти изменения. Этот метод так же вызывается конструкотором WorkingSet().
Этот метод использует find_distributions(entry, True) для поиска дистрибутивов, которые соответствуют элементу пути, а, затем, добавляют их вызовом add(). entry всегда добавляется к атрибуту entries, даже если он уже присутствует там (причина этого в том, что sys.path может содержать одно и то же значение несколько раз, и атрибут entries должен быть способным отображать это)
__contains__(dist) - True, если dist активен в этом рабочем наборе. Обратите внимание, что только один дистрибутив для данного проекта может быть активен в данном WorkingSet.
__iter__() - выдаёт дистрибутивы для неповторяющихся проектов в рабочем наборе. Порядок выдачи соответствует порядку, в котором пути элементов были добавлены в рабочий набор.
find(req) - обнаруживает дистрибутивы, соответсвующие req (экземпляру класса Requirement). Если это активный дистрибутив для запрашиваемого проекта, то он будет возвращён, если он соответствует версии, определённой в req. Но если есть активный дистрибутив для проекта, который не соответствует требованиям req, будет вызвано исключение VersionConflict. Если нет активного дистрибутива для этого проекта, то будет возвращено None.
resolve(requirements, env=None, installer=None) - список дистрибутивов, необходимых для (рекурсивного) соответствия requirements.
requirements должен быть последовательностью объектов Requirement. env, если предоставлен, должен быть экземпляром Environment. Если он не передан, Environment создаётся из entries рабочего каталога. installer, если передан, будет вызван для каждого требования, которое не удволетворено уже установленными дистрибутивами; он должен возвращать Distribution или None. (Смотрите метод obtain() объекта Environment, где более подробно рассказано об аргументе installer)
add(dist, entry=None) - добавляет dist в рабочий набор, ассоциированный с entry. Если entry не определён, по умолчанию будет использован dist.location. При выходе из этой процедуры, entry добавляется в конец .entries рабочего набора (если его там ещё нет).
dist добавляется в рабочий набор только если это касается проекта, у которого ещё нет этого активного дистрибутива в рабочем наборе. Если он успешно добавлен, все коллбэки, зарегистрированные методом subscritbe(), будут вызваны. (См "Получение оповещений об изменениях" ниже)
Примечание: add() автоматически вызывается для Вас методом require(), так что обычно Вам не потребуется вызывать этот метод напрямую.
entries - этот атрибут представляет "тень" sys.path, в первую очередь полезную для отладки. Если Вы столкнулись с проблемами импорта, проверьте entries глобального объекта working_set и сравните его с sys.path. Если они не совпадают, значит какая-то часть вашей программы работает с sys.path не обновляя соответственно working_set. Важное замечание: не изменяйте напрямую этот атрибут! Установка его эквивалентным sys.path решит вашу проблему не лучше, чем замазывание аварийки починит вашу машину. Если этот атрибут не соответствует sys.path, то это сигнал о проблеме, а не её причина.

Получение оповещений об изменениях

Расширяемые приложения и фреймворки могут иметь потребность в получении оповещений, когда новые дистрибутивы (например, плагины) добавляются в рабочий набор. Для этого предназначены метод subscribe() и функция add_activation_listener().
subscribe(callback) - вызывает callback(distribution) один раз для каждого активного дистрибутива, который  уже находится в наборе или будет добавлен позже. Так как коллбэк вызывается и для уже активных дистрибутивов, Вам не нужно делать цикл по рабочему набору, чтобы обработать существующие элементы; просто зарегистрируйте коллбэк и будьте готовы к тому, что он будет немендленно вызван этим методом.
Обратите внимание, что коллбэки не должны позволять исключениям распространяться, иначе они наложатся на операции других коллбэков, что, возможно, приведёт к противоречивому состоянию рабочего набора. Коллбэки должны использовать блоки try/except чтобы игнорировать, логировать или как-либо ещё обрабатывать ошибки, особенно с учётом того, что код, вызвавший коллбэк скорее всего не сможет обработать ошибки лушче, чем сам коллбэк.
pkg_resources.add_activation_listener() является альтернативой pkg_resources.working_set.subscribe().

Обнаружение плагинов

Расширяемые приложения иногда имеют что-то вроде "каталога плагинов" или набора таких каталогов, откуда они хотят загрузить точки входа или другие метаданные. Метод find_plugins() позволяет Вам сделать это, сканируя окружения в поисках новых версий каждого проекта, которые могут быть безопасно загружены без конфликтов или невыполненных требований.
find_plugins(plugin_env, full_env=None, fallback=True) - сканирует plugin_env и определяет, какие дистрибутивы могут быть добавлены в этот рабочий набор без конфликта версий или невыполненных требований.
Пример использования:

distributions, errors = working_set.find_plugins(
                             Environment(plugin_dirlist)
                        )
map(working_set.add, distributions) # добавляем плагины
                                    # и библиотеки в sys.path
print "Не получилось загрузить", errors # показать ошибки

plugin_env должен быть экземпляром Environment, который содержит только те дистрибутивы, которые находятся в каталоге плагинов проекта. full_env, если указан, должен быть экземпляром Environment, который содержит все текущие доступные дистрибутивы.
Если full_env не задан, он создаётся автоматически из WorkingSet, на котором вызывается этот метод, что обычно означает, что каждый каталог в sys.path будет просканирован в поисках дистрибутивов.
Этот метод возвращает два кортежа: ('distributions', 'error_info'), где distributions - список дистрибутивов, найденых в plugin_env, которые были загружаемы, вместе с другими дистрибутивами, которые были нужны для разврешения их зависимостей. error_info - это словарь, который отображает незагружаемые дистрибутивы плагинов на экземпляры исключений, которые произошли. Обычно это будут экземпляры классов DistributionNotFound или VersionConflict.
Большая часть приложений обычно использует этот метод на экземпляре working_set, а, затем, тут же добавляет возвращённые дистрибутивы в рабочий набор, так что они будут доступны в sys.path. Таким образом, оказывается возможным найти все точки вода и позволяет отслеживать другие метаданные и активировать хуки.
Алгоритм разрешения используемый find_plugins() таков. Сперва имена проектов дистрибутивов, находящихся в plugin_env, сортируются. Затем, проверяется яйцо каждого проекта в порядке уменьшения версии (то есть, начиная с более новой версии проекта).
Производится попытка разрешить зависимости каждого яйца. Если попытка успешна, яйцо и его зависимости добавляются в список вывода и во временную копию рабочего набора. Процесс разрешения продолжается на следующем имени проекта и более старые яйца для этого проекта даже не затрагиваются.
Если попытка разрешения не удаётся, ошибка добавляется в словарь ошибок. Если флаг fallback=True, пробуется более старая версия плагина, до тех пор, пока не будет найдена работающая версия. Если False, то процесс разрешения продолжается для следующего имени проекта.
Некоторые приложения могут иметь более строгие требования к "отступлению (fallback)", чем другие. Например, приложение, у которого есть схема БД или постоянные объекты, может быть не может так просто провести доунгрейд версии пакета. Другие приложения могут хотеть быть уверенными, что новая конфигурация плагинов либо на 100% хороша, либо откатиться к другой гарантированно работающей конфигруации. (То есть, они могут хотеть откатиться к гарантированно работающей конфигурации если значение error_info не пустое.)
Обратите внимание, что этот алгоритм даёт преимущество при удволетворении зависимостей в алфавитном порядке имён проектов в случае конфликта версий. Если два проекта с именами AaronsPlugin и ZekesPlugin оба требуют разные версии TomsLibrary, в таком случае AaronsPlugin получит своё, а ZekesPlugin нет из-за конфликта версий.

Объекты Environment

environment - колекция объектов Distribution, обычно тех, которые присутствуют и потенциально импортируемы на текущей платформе. Объекты Environment используются pkg_resources для индексирования доступных дистрибутивов в процессе разрешения зависимостей.
Environment(search_paht=None, platform=get_supported_platform(), python=PY_MAJOR) - создаёт снимок окружения сканируя search_path в поисках дистрибутивов, совместимых с platform и python. search_path должен быть последовательностью строк, которая могла бы использоваться для sys.paht. Если search_path не указан, будет использоваться sys.path.
platform - опциональная строка, определяющая имя платформы, с которой должны быть совместимы не кроссплатформенные дистрибутивы. Если он не указан, то будет использоваться текущая платформа. python - опциональная строка, указывающая на версию Python (например, "2.4"); по умолчанию - это текущая запущенная версия.
Вы можете принудительно установить platform (и/или python) в None, если Вы хотите включить все дистрибутивы, не только совместимые с текущей платформой или версией.
Обратите внимани, что search_path немендленно сканируется в поисках дистрибутивов и результирующий Environment является снимком найденных дистрибутивов. Он не обновляется автоматически если состояние системы изменяется в следствии, например, установки или удаления дистрибутивов.
__getitem__(project_name) - возвращает список дистрибутивов для данного имени проекта, упорядоченный от более новых к более старым версиям. (Этот формат даёт приоритет для дистрибутивов, которые содержат ту же версию проекта). Если дистрибутивов для проекта не найдено, возвращается пустой список.
__iter__() - выдёт уникальные имена проектов дистрибутивов в этом окружении. Выдаваемые имена всё время в низком регистре.
add(dist) - добавляет dist в окружение, если он соответствует платформе и версии python, определённой на момент создания, и только если дистрибутив ещё не был добавлен (то есть, добавть один дистрибутив более одного раза не получится).
remove(dist) - удаляет dist из окружения.
can_add(dist) - проверяет, приемлем ли dist для этого окружения. Если он не совместим с платформой или версией Python, определёнными при создании окружения, возвращается false.
__add__(dist_or_env) (оператор +) - добавляет дистрибутив или окружение в экземпляр Environment, возвращая новый объект окружения, который содержит все дистрибутивы, содежавшиеся в предыдущих объектах. Новое окружение будет иметь platform и python равным None, что означает, что никакие дистрибутивы не будут отклонены при попытке добавить их; всё, что будет добавляться будет добавлено. Если Вы хотите, чтобы добавляемые дистрибутивы отфильтровывались по платформе или версии, или Вы хотите добавить их в тот же экземпляр окружения, в таком случае Вы должны использовать оператор +=
__iadd__(dist_or_env) (оператор +=) - добавляет дистрибутивы или окружение в экземпляр Environment "на месте", обновляя существующий экземпляр и возвращая его. Дистрибутивы фильтруются по платформе и версии Python.
best_match(req, working_set, installer=None) - находит дистрибутивы, лучше всего удволетворяющие req и которые можно использовать в working_set.
Он вызывает метод find(req) на working_set, чтобы увидеть, активирован ли подходящий дистрибутив. (Это может вызвать исключение VersionConflict, если неподходящая версия проекта уже активирована в заданом working_set). Если нужный дистрибутив не активирован, этот метод возвращает новейший дистрибутив в окружении, который отвечает Requirement в req. Если подходящего дистрибутива не найдено и указан installer, тогда возвращается результат вызова метода obtain(req, installer) окружения.
obtain(requirement, installer=None) - получает дистрибутив, который отвечает требованиям (например, загружая его). В базовом классе Environment  эта процедура лишь возвращает installer(requirement), а если installer=None, возвращается None. Этот метод является "ловушкой", которая позволяет субклассу попробовать другие пути получения дистрибутива до того, как откатиться до аргумента installer.
scan(search_path=None) - сканирует search_path в поисках дистрибутивов, которые можно использовать на platform.
Все найденные дистрибутивы добавляются в окружение. search_path должен быть последовательностью строк, которую можно было бы использовать в качестве sys.path. Если этот аргумент не передан, то используется sys.path. Добавляются только дистрибутивы, подходящие для платформы и версии Python, указанной при инициализации. Этот метод является сокращением для функции find_distributions(), чтобы найти дистрибутивы для каждого элемента в search_path и затем вызвать add() для добавления каждого дистрибутива в окружение.

Объекты Requirement

Объекты Requirement выражают какая версия проекта подходит для каких целей. Эти объекты (или их строковая форма) используются разными API pkg_resources для обнаружения дистрибутивов, которые нужны скрипту или другим дистрибутивам.

Парсинг требований

parse_requirements(s) - выдаёт объекты Requirement для строк или итерируемых строк. Каждое требование должно начинаться с новой строки. См ниже описание синтаксиса.
Requirement.parse(s) - создаёт объект Requirement и строки или интерируемых строк. Исключение ValueError возбуждается, если строка или строки не содержат корректного определителя требований или содержат более одного определителя. (Для обработки нескольких определителей из строки или итерируемого набора строк используйте parse_requirements().)
Синтаксис определителей требований может быть определён в РБНФ таким образом:

version      ::= [-A-Za-z0-9_.]+
identifier   ::= [-A-Za-z0-9_]+
project_name ::= identifier
extralist    ::= identifier (',' identifier)*
extras       ::= '[' extralist? ']'
comparison   ::= '<' | '<=' | '!=' | '==' | '>=' | '>'
versionspec  ::=comparison version (',' comparison version)*
requirement  ::=project_name versionspec? extras?

Токены могут быть разделены пробелами, а требования могут быть расположены на нескольких строках при помощи бэкслеша (\\). Комментарии в конце строки (с символом #) тоже можно использовать.
Вот несколько примеров корректных определителей требований:

FooProject >= 1.2
Fizzy [foo,bar]
PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1
SomethingWhoseVersionIDontCareAbout

Имя проекта - единственная требуемая часть строки, и если оно указано, то требованию будет отвечать любая версия этого проекта.
extras в требовании используется для запроса опциональных функций проекта, что может требовать дополнительных дистрибутивов для работы. Например, если гипотетический проект Report-O-Rama предлагает опциональную поддержку PDF, он может требовать дополнительную библиотеку для предоставления этой возможности. Таким образом проект, использующий Report-O-Rama c поддержкой PDF должен использовать требование Report-O-Rama[PDF] для того, чтобы запросить установку или активацию и Report-O-Rama и всех необходимых библиотек, которые ему нужны для поддержки PDF. Например, Вы можете использовать
easy_install.py Report-O-Rama[PDF]
для установки необходимых пакетов с помощью программы EasyInstall, или вызвать pkg_resources.require('Report-O-Rama[PDF]') для добавления нужных дистрибутивов в sys.path в процессе выполнения.

Методы и аргументы класса Requirement

__contains__(dist_or_version) - возвращает true если dist_or_version отвечает этому требованию. Если dist_or_version является объектом Distribution, то его имя проекта должно соответствовать имени требуемого проекта, а версия - требованию версии. Если dist_or_version является строкой, то она разбирается при помощи функции parse_version(). В противном случае ожидается, что это будет уже разобранная строка.
Спецификаторы версии Requirement (.specs) сортируются в возрастающем порядке и используются для определения интервалов приемлимых версий. Соседние избыточные условия эффективно объединяются (например, ">1, >2" дадут тот же эффект, что и ">1", a "<2,<3" дадут тот же результат, что и "<3"). "!=" исключает указанные версии из доступных. Версии, прошедшие проверку на приемлимость, затем проходят проверку на присутствие в доступном интервале. (Обратите внимание, что предоставление конфликтующих условий для одной и той же версии (например, "<2,>=2" или "==2,!=2") бессмасленно и потому может выдать причудливый результат при проверке с актуальным номером версии.)
__eq__(other_requirement) - требование считается равным другому требованию если у них одинаковые, независимо от регистра, имена проектов, спецификаторы версии и extras. (Порядок extras и спецификаторов версий не учитывается.) Равные требования так же имеют равные хеши, так что это требование может использоваься в наборах или как ключ словаря.
__str__() - строковая форма Requirement это строка, которую, если передать в Requirement.parse() вернёт равный объект Requirement.
project_name - имя запрашиваемого проекта
key - версия project_name в нижнем регистре. Полезна для сравнения или индексирования.
extras - кортеж имён extras для которых вызывается это требование. (Они будут в нижнем регистре и нормализованы при помощи функции safe_extra(), так что они могут быть не совсем равными extras, c которыми было создано требование)
specs - список кортежей (op,version), отсортированный в возрастающем порядке версий. op в каждом кортеже является оператором сравнения, представленным в виде строки. version - это (не обработанный) номер версии. Относительный порядок кортедежей, содержащих тот же номер версии не определён, так как наличие более одного оператора для одной версии либо избыточно, либо противоречиво.

Точки входа

Точки входа - простой способ для дистрибутива объявить объекты Python (такие, как функции или классы) для использования другими дистрибутивами. Расширяемые приложения или фреймворки могут искать точки входа с определённым именем или группой или в конкретном дистрибутиве, или во всех активных дистрибутивах в sys.path, а затем исследовать или загрузить эти объекты.
Точки входа принадлежат "группам", с точечным названием, аналогичным названиям пакетов или модулей Python. Например, пакет setuptools использует точку входа distutils.commands для того, чтобы найти команды, определённые расширением distutils. setuptools трактует имена точке входа, определённые в этой группе, как допустимые команды для скрипта настройки.
Похожим образом другие пакеты могут определять свои собственные группы точек входа, используя либо динамические имена в группе (как distutils.commands), или используя предопределённые имена. Например, фреймворк блогов, которые предоставляет различные до- и постпубликационные "ловушки" может определить группу точек входа и искать точки входа с именами pre_process и post_process в этой группе.
Для того, чтобы объявить точку входа, проект должен использовать setuptools и предоставить аргумент entry_points функции setup() в своём настроечном скрипте, так что эти точки входа будут добавлены в метаданные дистрибутива. Более подробно смотрите в документации setuptools.
Дистрибутив каждого проекта может объявить максимум одну точку входа определённого имени в определённой группе точек входа. Например, расширение distutils может объявить две разные точки входа distutils.commands, если у них разные имена. Но это не мешает разным проектам объявить точки входа с одинаковыми именами в одинаковых группах. В некоторых случаях это нежелательно, так как приложение или фреймворк, которое использует эти точки входа, может вызывать их как "ловушки" или ещё каким-то образом комбинировать их. Приложение или фреймворк само должно решать, что делать, если разные дистрибутивы объявляют одинаковые точки входа. Например, можно использовать обе точки входа, отобразить сообщение об ошибке, использовать первое найденное и т.д.

API

В следующих функциях аргумент dist может быть экземпляром Distribution, Requirement или строкой, определяющей требования (например, имя проекта, версию и т.д.). Если аргументом является строка или Requirement, ищется определяемый им дистрибутив (и добавляется в sys.path, если его там ещё нет). В случае, если подходящего дистрибутива нет, будет возбуждено исключение.
Аргумент group должен быть строкой, содержащей точечный идентификатор, идентифицирующий группу точек входа. Если Вы определяете группу точек входа, Вы должны включить часть имени вашего пакета в имя группы, чтобы избежать коллизий с группами точек входа других пакетов.
load_entry_point(dist, group, name) - загружает именованную точку входа из заданного дистрибутива или возбуждает ImportError.
get_entry_info(dist, group, name) - возвращает объект EnrtyPoint для указанной group и name из выбранного дистрибутива. Возвращает None, если дистрибутив не объявил подходящей точки входа.
get_entry_map(dist, group=None) - возвращает карту точек входа дистрибутива для group, или всё карту для дистрибутива. Эта функция возвращает словарь, даже если дистрибутив не объявляет точек входа. Если указана group, словарь отображает имена точек входа на соответствующие объекты EntryPoint. Если group=None, то словарь отображает имена групп на словри, которые отображают имена точек входа на соответствующие экзмемпляры EntryPoint в этой группе.
iter_entry_points(group, name=None) - выдаёт объекты точек входа из group подходящие по name.
Если name=None, выдаются все точки входа в group из всех дистрибутивов в рабочем наборе из sys.path, иначе выдаются только те, которые соответствуют и group и name. Точки входа выдаются из активных дистрибутивов в том порядке, как эти дистрибутивы расположены в sys.path (Однако для конкретного дистрибутива нет какого-либо порядка точек входа.)
(Этот API является методом глобального объекта working_set, более подробно о нём говорится в разделе "Основные методы WorkingSet")

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

EntryPoint(name, module_name, attrs=(), extras=(), dist=None) - создаёт экземпляр EntryPoint. name - имя точки входа. module_name - точечное имя модуля, содержадещго объявляемый объект. attrs - опциональный кортеж имён, которые будут искать в модуле для получения объявленного объекта. Например, attrs=("foo","bar") и module_name="baz" будут означать, что объявляемый объект может быть получен следущим кодом:
import baz
advertized_object=baz.foo.bar
extras - опциональный кортеж имён "дополнительных возможностей", которые дистрибутив требует для обеспечения этой точки вохода. Когда точка входа загружается, эти дополнительные возможности отыскиваются в аргументе dist, чтобы понять, какие ещё дистрибутивы должны быть активированы в sys.path; подробнее об этом смотри в методе load(). Аргумент extras имеет смысл только при наличии аргумента dist, который должен быть экземпляром Distribution.
EntryPoint.parse(src, dist=None) (classmethod) - парсит одну точку входа из строки src. Синтаксис точки входа следует форме:
name = some.module:some.attr [extra1, extra2]
Имя точки входа и модуля обязательны, а attrs и [extras] не обязательны, как и пробелы между элементами. Аргумент dist передаётся в конструктор EntryPoint() вместе со значениями, полученными из src.
EntryPoint.parse_group(group, lines, dist=None) (classmethod) - парсит стрики или последовательность строк lines для создания словаря, отображающего имена точек входа на объекты EntryPoint. ValueError возбуждается если имена точек входа повторяются, если group не является корректным именем группы, или если есть ошибки в синтаксисе. (Обратите внимание: параметр group используется только для проверки и для создания более информативных сообщений об ошибке.) Если указан параметр dist, он будет использоваться для установки атрибута dist создаваемых объектов EntryPoint.
EntryPoint.parse_map(data, dist=None) (classmethod) - парсит data в словарь, отображающий имена точек входа на объекты EntryPoint. Если data является словарём, точка ключи будут использоваться как имена групп и значения передаётся в parse_group() в качестве аргумента lines. Если data - стока или последовательность строк, то она сперва делится на .ini-подобные разделы (при помощи функции split_sections()) и имена секций используются как имена групп. В любом случае аргумент dist передётся в parse_group(), так что точки входа будут связаны с определённым дистрибутивом.

Объекты EntryPoint

Для простоты анализа, объекты EntryPoint имеют атрибуты, которые точно отвечают аргументам конструктора: name,module_name, attrs, extras и dist. Кроме того присутствуют следующие методы:
load(require=True, env=None, installer=None) - загружает точку входа, возвращает объявленный объект Python, или возбуждает ImportError, если точка входа не может быть получена. Если require=True, тогда до попытки импорта вызывается require(env, installer).
require(env=None, installer=None) - убеждается, что все extras, которые нужны точке входа доступны в sys.path. UnknownExtra возбуждается, если EntryPoint имеет extras, но при этом не имеет dist, или если имена extras не определены дистрибутивом. Елси указан env, это должно быть экземпляром Environment, и он будет использован для поиска нужных дистрибутивов, если они ещё не присутствуют в sys.path. Если указан installer, он должен быть вызываемым и принимать экземпляр Requirement и возвращать подходящий импортируемый экземпляр Distribution или None.
__str__() - строковая форма EntryPoint; строка, которую можно передать в EntryPoint.parse() для получения эквивалентной EntryPoint.

Объекты Distribution

Объекты Distribution представляют коллекцию кода Python, который может быть импортирован и может иметь ассоциированные с ним метаданные и ресурсы. Их метаданные могут включать информацию, такую как от каких дистрибутивов он зависит, какие точки объявляет и так далее.

Получение или создание Distribution

Чаще всего Вы получаете объекты Distribution из WorkingSet или из Environment. (Смотрите ниже раздел об объектах WorkingSet и Environment, которые являются контейнерами для активных дистрибутивов и доступных дистрибутивов соответственно.) Кроме того, Вы можете получить объекты Distribution из этих API верхнего уровня:
find_distributions(path_item, only=False) - выдёт дистрибутивы, доступные через path_item. Если only=True, выдаёт только дистрибутивы, чьё расположение равно path_item. Другими словами, если only=True, то будут выдаваться только дистрибутивы, которые можно импортировать если sys.path=path_item. В противном случае будут выданы дистрибутивы, которые находятся "в" или "под" path_item, но они не будут доступны для импорта, пока их расположение не будет добавлено в sys.path.
get_distribution(dist_spec) - возвращает объект Distribution для данного Requirement или строки. Если dist_spec уже является экземпляром Distribution, то он и возвращается. Если это объект Requirement или строка, которая может быть преобразована в этот объект, то он используется для поиска и активации подходящего дистрибутива, который затем и возвращается.
Кроме того, если Вы создаёте специальный инструмент для работы с дистрибутивами или создаёте новый формат дистрибутива, Вам может потребоваться создать объект Distribution напрямую при помощи однго из трёх конструкоторов, указаных ниже.
Все эти конструкторы принимают опциональный аргумент metadata, который используется для доступа к любым ресурсам или метаданным, ассоциированным с дистрибутивом. metadata должен быть объектом, реализующим интерфейс IResourceProvider или None. Если это None, то вместо него используется EmptyProvider. Объекты Distribution реализуют и методы IResourceProvider и IMetadataProvider, делегируя их объекту metadata.
Distribution.from_location(location, basename, metadata=None, **kw) (classmethod) - создаёт дистрибутив для location, который должен быть строкой вроде URL, имени файла или любой другой строкой, которую можно использовать в sys.path. Если basename заканчивается на .egg, тогда имя проекта, версия, версия Python и платформа извлекаются из имена файла и используются для задания соответствующих свойств создаваемого дистрибутива. Все дополнительные именованные аргументы направляются конструктору Distribution().
Distribution.from_filename(filename, metadata=None, **kw) (classmethod) создаёт дистрибутив, распарсив локальное имя файла. Это более простой способ, аналогичный Distribution.from_location( normalize_path(filename), os.path.basename(filename), methadata). Другими словами, это создаёт дистрибутив с расположением, полученным нормализацией имени файла, где имя и версия получаются из расширения файла. Все дополнительные аргументы передаётся в конструктор Distribution().
Distribution(location, metadata, project_name, version, py_version, platform, precedence) - создаёт дистрибутив на основе переданных параметров. Все аргументы опциональные и по умолчанию равны None, кроме py_version (который по умолчанию имеет значение текущей версии Python) и precedence (которое по умолчанию равно EGG_DIST; подробнее смотрите раздел "precedence" в "Атрибуты Distribution" ниже). Обратите внимание, что чаще используются конструкторы from_filename() или from_location().

Атрибуты Distribution

location - строка, определяющая место расположения дистрибутива. Для импортируемых дистрибутивов эта строка будет добавлена в sys.path, чтобы его можно было реально импортировать. Для не импортируемых дистрибутивов это просто имя файла, URL, или другой указатель места нахождения дистрибутива.
project_name - строка, обозначающая проект этого дистрибутива. Имена проектов определяются в скрипте установки проектов и они используются для идентификации проектов в PyPI. Когда конструируется Distribution, аргумент project_name передаётся через функцию safe_name() чтобы отфильтровать все неподходящие символы.
key - dist.key это сокращение dist.project_name.lower(). Он используется для сравнений без учёта регистра и индексации дистрибутивов по имени проекта.
extras - список строк, содержащих имена дополнительных возможностей, определённых списком зависимости проекта (аргумент extra_require определённый в скрипте установки проекта)
version - строка, определяющая релиз проекта, который содержится в этом дистрибутиве. Когда конструируется Distribution, аргумент version передаётся через функцию safe_version() для того чтобы отфильтровать все неподходящие символы. Если version yt задан, тогда при обращении к этому атрибуту будет произведена попытка определить значение этого аргумента прочитывая файл метаданных PKG-INFO. Если PKG-INFO yt доступен или не может быть обработан, будет вызвано исключение ValueError.
parsed_version кортеж, представляющи "отпарсенную" форму version. dist.parsed_version является более коротким аналогом parse_version(dist.version). Он используется для сравнения или сортировки дистрибутивов согласно номеру версии. (Смотрите раздел "Утилиты для парсинга" ниже.) Обратите внимание что обращение к parsed_version может вызвать исключение ValueError, если Distribution был создан без version и без метаданных, которые могли бы указать версию дистрибутива.
py_version - Мажорная/минорная версия Python, которую поддерживает данный дистрибутив; строка. Например, "2.3" или "2.4". По умолчанию - текущая версия Python.
platform - строка, представляющая платформу, для которой предназначен данный дистрибутив, или Noneбесли дистрибутив предназначен для "чистого" Python и потому кросс-платформен. Смотрите "Утилиты платформ", где об этом говорится подробнее.
precedence - используется для определения относительного порядка двух дистрибутивов с одинаковыми project_name и parsed_version. По умолчанию - pkg_resources.EGG_DIST, обладающий высшим приоритетом (то есть, наиболее предпочтителен). Полный список в сторону уменьшения предпочтнеия, таков: EGG_DIST, BINARY_DIST, SOURCE_DIST, CHECKOUT_DIST, DEVELOP_DIST. Обычно другой порядок используется только в модуле setuptools.package_index, когда сортируются дистрибутивы, найденные в индексе пакетов для определения их пригодности для установки. "Яйца" System и Development (то есть те, которые используют формат .egg-info) автоматически получают уровень прироритета DEVELOP_DIST.

Методы Distribution

activate(path=None) - проверяет, что дистрибутив можно импортировать через path. Если path=None, то используется sys.path. Проверяется, что location дистрибутива находится в списке path и так же производятся все необходимые исправления пространства имён или объявлений. (То есть, если дистрибутив содержит пакеты пространств имён, этот метод проверяет, что они объявлены и что содержимое этих пакетов слито с содержимым, предоставляемым другими активными дистрибутивами. Смотрите раздел "Поддержка пакетов пространства имён")
pkg_resources добавляет каллбэк оповещения в глобальный working_set, который проверяет, что этот метод вызывается когда к нему добавляется дистрибутив. Поэтому обычно Вам не нужно вызывать этот метод вручную. (Обратите внимание, что это означает, что пакеты имён в sys.path всегда импортируются pkg_resources, что является ещё одной причиной почему пакеты пространства имён не должны содержать никакого кода или выражений импорта)
as_requirement() - возвращает экземпляр Requirement, который соответствует имени проекта дистрибутива и версии.
requires(extras=()) - список объектов Requirement, которые определяют зависимости дистрибутивов. Если extras определён, то это должна быть последовательность имён extras, определённых дистрибутивом и в таком случае возвращаемый список будет содержать все необходимые зависимости, которые нужны для обеспечения этих extras.
clone(**kw) создаёт копию дистрибутива. Все переданные именованные аргументы переопределяют соответствующие аргументы конструктора Distribution(), позволяя Вам изменить некоторые атрибуты копируемого дистрибутива.
egg_name() - возвращает стандартное имя файла, которое должно было бы быть у этого дистрибутива, без расширения .egg. Например, дистрибутив проекта Foo версии 1.2, который предназначен для Python 2.3 под Windows вернёт Foo-1.2-py2.3-win32. Все тире в имени или версии будут заменены на нижние подчёркивания. (Distribution.from_location() выполняет обратное преобразование, выполняя парсинг имени файла)
__cmp__(other), __hash__() - объекты Distribution хешируемы и их можно сравнивать на основе их версий, приоритетов, ключей (имени проекта в нижнем регистре), месте расположения, версии Python и платформе.

Следующие методы использутся для доступа к объектам EntryPoint, объявленных в дистрибутивах. Более подробно смотрите об этом в разделе, посвященном этому классу.
get_entry_info(group, name) - возвращает объект EntryPoint для данных group и name, или None если такой точки не объявленно в этом дистрибутиве.
get_entry_map(group=None) - возвращает карту точек входа для group. Если group=None, возвращается словарь, отображающий имена груп на карты точек входа для всех групп. (Карта точек входа - это словарь, отображающий имена точек входа на объекты EntryPoint)
load_entry_point(group, name) - более короткий вариант get_entry_info(group, name).load(). Возвращает объект, объявленный этой точкой входа, или возбуждает исключение ImportError, если эта точка входа не объявлена в этом дистрибутиве или есть ещё какие-то проблемы.

Кроме этих методов объекты Distribution так же реализуют методы IResourceProvider и IMetadataProvider (описаны ниже в этом разделе)5
  • has_metadata(name)
  • metadata_isdir(name)
  • metadata_listdir(name)
  • get_metadata(name)
  • get_metadata_lines(name)
  • run_script(script_name, namespace)
  • get_resource_filename(manager, resource_name)
  • get_resource_stream(manager, resource_name)
  • get_resource_string(namager, resource_name)
  • has_resource(resource_name)
  • resource_isdir(resource_name)
  • resource_listdir(resource_name)
Если дистрибутив был создан с аргументом metadata, то эти методы доступа к ресурсам и метаданным делегируются провайдеру metadata. Иначе они делегируются EmptyProvider, так что у дистрибутива не будет ни ресурсов ни метаданных. Такой подход к делегации используется для того, чтобы обеспечение поддержки пользовательских импортеров или новых форматов дистрибутивов сводилось к простому созданию соответствующей реализации IResourceProvider. Смотрите раздел "Поддержка пользовательских импортёров" ниже.

API ResourceManager

Класс ResourceManager предоставляет единообразный доступ к пакету ресурсов, вне зависимости от того, существуют эти ресурсы как файлы, каталоги или архивы.
Обычно Вам не нужно создавать или явно управлять экземплярами ResourceManager, так как модуль pkg_resources создаёт для Вас глобальный экземпляр и делает большинство его методов доступными для Вас как имена верхнего уровня в пространстве имён pkg_resources. Так, например, этот код вызывает метод resource_string() глобального ResourceManager:

import pkg_resources
my_data=pkg_resources.resource_string(__name__, "foo.dat")

Таким образом, Вы можете использовать описанное ниже API без нужды в явном экземпляре ResourceManager; просто импортируйте модуль и используйте при необходимости.

Базовый доступ к ресурсам

В следующих методах аргумент package_or_requirement может быть либо  именем модуля/пакета Python (например, foo.bar) или экземпляром Requirement. Если это имя пакета или модуля, то этот модуль должен быть импортируемым (то есть путь к нему должен быть в sys.path), а аргумент resource_name интерпретируется как относительный путь к пакету. (Обратите внимание, что если используется имя модуля, тогда имя ресурса трактуется относительно пакета, содержащего этот модуль. Кроме того, Вы не должны использовать пакеты пространств имён, так как эти пакеты могут быть разделяемыми между различными дистрибутивами, и потому возникает двусмысленность в том, в каком дистрибутиве искать требуемый ресурс.)
Если же этот аргумент является экземпляром Requirement, в таком случае эти требования автоматически разрешаются (используя текущий Environment при необходимости) и подходящие дистрибутивы добавляются в WorkingSet и sys.path, если его там ещё нет. (Если же подходящего дистрибутива нет, то вызывается исключение.) Аргумент resource_name затем интерпретируется относительно корня выявленного дистрибутива; то есть первый сегмент пути будет трактоваться как верхняя часть модуля или пакета.
Обратите внимание, что имена ресурсов должны иметь пути с разделителями / и не могут быть абсолютными (то есть начинаться с /) или содержать относительные имена вроде "..".  Не используйте os.path для управления путём к ресурсам, так как они предоставляют не пути файловой системы.
resource_exists(package_or_requirement, resource_name) - существует ли искомый ресурс? Возвращает True или False.
resource_stream(package_or_requirement, resource_name) - возвращает читаемый файлоподобный объект для указанного ресурса; это может быть собственно файл, StringIO или что-то вроде этого. Поток находится в "двоичном режиме" в том смысле, что все байты будут считаны так, как они есть.
resource_string(package_or_requirement, resource_name) - возвращает искомый ресурс как строку. Ресурс читается в двоичном режиме, так что строка содержит те же байты, что и искомый ресурс.
resource_isdir(package_or_requirement, resource_name) - является ли искомый ресурс каталогом? Возращает True или False
resource_listdir(package_or_requirement, resource_name) - возвращает список содержимого указанного каталога ресурсов, как os.listdir за исключением того, что работает даже если ресурсы находятся в zip файле.

Обратите внимание, что только resource_exists() и resource_isdir() не учитывают тип ресурса. Вы не можете использовать resource_listdir() применительно к файлу или resource_string() или resource_stream() к каталогу. Использование неподходящего метода может привести к исключению или неопределённому поведению в зависимости от платформы и формата дистрибутива.

Извлечение ресурсов

resource_filename(package_or_requirement, resource_name) - иногда, когда не достаточно получить ресурс в форме потока или строки, нужно имя файла с ресурсом. В этом случае Вы можете использовать этот метод (или функцию уровня модуля) для получения имени файла этого ресурса. Если ресурс находится в архивированном дистрибутиве (например зазипованном яйце), то он будет извлечён оттуда в кэш-каталог и будет возвращено имя файла в этом кэше. Если запрошенный ресурс - каталог, тогда извлекаются и всё ресурсы из этого каталога (включая подкаталоги). Если этот ресурс - С расширение или "eager resource" (более подробно в документации setuptools), тогда все расширения С и eager русурсы тоже будут извлечены.
Заархивированные ресурсы извлекаются в кэш-каталог, который управляеся двумя методами:
set_extraction_path(path) - задаёт место, куда должны быть извлечены ресурсы.
Если Вы не вызывали этот метод перед извлечением, то по умолчанию значением является результат вызова функции get_default_cache() (основанное на переменной окружения PYTHON_EGG_CACHE, что зависит от конкретной системы. Подробнее об этом - в документации к этой функции)
Ресурсы извлекаются в подкаталоги этого пути, основываясь на информации, получаемый из провайдера ресурсов. Вы можете указать в качестве этой точки временный каталог, но затем Вы должны будете вызвать cleanup_resources() чтобы удалить извлечённые файлы, когда они Вам будут не нужны. Нет никакой гарантии того, что cleanup_resources() удалит все извлечённые файлы. (Например, под Windows Вы не сможете удалить файлы .pyd или .dll, которые на данный момент используются.)
Обратите внимание, что Вы не должны менять путь для извлечения ресурсов, если Вы уже туда что-то извлекали, если перед этим Вы не вызвали cleanup_resources().
cleanup_resources(force=False) - удаляет все извлечённые файлы ресурсов и каталоги, возвращая список имён файлов или каталогов, удалить которые не получилось. Эта функция не имеет защиты для параллельного выполнения, так что её надо вызывать только в случаях, когда путь для извлечения используется только одним процессом. Этот метод не вызывается автоматически; вы должны либо вызвать его явно, либо зарегистрировать его как функцию atexit6 если Вы хотите быть уверенными, что временные файлы будут удалены.

Интерфейс Provider

Если Вы реализуете IResourceProvider и / или IMetadataProvider для нового формата дистриутивов, Вам могут понадобиться следующие методы IResourceManager для управления извлечением ресурсов в соответствующий каталог. Если Вы не реализуете архивный формат, Вам эти методы не понадобятся. В отличие от других методов, перечисленных выше, они не доступны как функции уровня модуля, привязанные к глобальному ResourceManager; для работы с ними у Вас должен быть явный экземпляр ResourceManager.
get_cache_path(archive_name, names=()) - возвращает абсолютный genm в кэше для archive_name и names.
Родительский каталог возвращаемого пути будет создан, если он ещё не существует. archive_name должно быть именем файла объемлющего яйца (что может не совпадать с имеем объемлющего архивного файла!), включая его расширение .egg. Если указан names, то это должно быть последовательностью путей в пути извлечения яйца.
Этот метод должен быть вызван провайдером ресурсов, который должен обслуживать место извлечения и только для тех имён, которые он должен извлечь, так как он хранит полученные имена для дальнейшего удаления временных файлов.
extraction_error() - возбуждает исключение ExtractionError, describing the active exception as interfering with the extraction process. Вы должны вызвать его если Вы встречаетесь с ошибками ОС при извлечении файлов в кэш; он отформатирует для Вас исключение ОС и добавит информацию в экземпляр ExtractionError, который может понадобиться программам, желающим самим обернуть или обработать ошибку.
postprocess(tempname, filename) - обеспечивает постобработку зависящую от платформы для tempname. Провайдеры ресурсов должны вызывать этот метод только после успешного извлечения сжатых ресурсов. Он не должен вызываться для ресурсов, которые уже находятся в файловой системе.
tempname - текущее (временное) имя файла, а filename - имя, в которое он будет переименован после завершения функции.

Metadata API

API метаданных используется для доступа к метаданным ресурсов, постовляемых в комплекте с подключаемыми (pluggable) дистрибутивами. Ресурсы метаданнх - это виртуальные файлы или каталоги, содержащие информацию о дистрибутиве, который может быть использован расширяемым приложением или фреймворком для подключения "плагинов". Как и другие ресурсы, имена метаданных разделены / и не должны содержать .. или начинаться с /. Вы не должны использовать os.path для работы с путём ресурсов.
API метаданных предоставляется объектами, реализующими интерфейс IMetadataProvider или IResourceProvider. Объекты Distribution реализуют этот интерфейс, как и объекты, возвращаемые функцией get_provider():
get_provider(package_or_requirement) - если указано имя пакета, то возвращается IResourceProvider для пакета. Если же передан объект Requirement, то возвращается подходящий Distribution из текущего рабочего набора (при необходимости производится поиск в текущем Environment и найденный Distribution добавляется в рабочий набор). Если именованный пакет не может быть импортирован или Requirement не могут быть удволетворены, возбуждается исключение.
Примечание: если Вы используете имя пакета, а не Requirement, то объект, который Вы получете может не быть подключемым дистрибутивом, в зависимости от метода, которым был установлен этот пакет. В частности, "development" пакеты и "single-version externally-managed" пакеты не могут быть отражены из имени пакета на подходящие метаданные. Не пишите код, который передаёт имена пакетов в get_provider() а затем пытается получить метаданные проекта из возвращённого объекта. Это может сработать, если указанный пакет будет .egg файлом или каталогом, но в других случаях Вы получите ошибку. Если Вам нужны метаданные проекта, Вы должны искать проект, а не пакет.

Методы IMetadataProvider

Методы, предоставляемые объектами (такими, как экземпляры Distribution), которые реализуют интерфейсы IMetadataProvider или IResourceProvider:
has_metadata(name) - существуют ли именованные ресурсы метаданных?
metadata_isdir(name) - запрашиваемые ресурсы метаданных являются каталогом?
metadata_listdir(name) - список имён метаданных в каталоге (похоже на os.listdir())
get_metadata(name) - возвращает именованный ресурс метаданных в качестве строки. Данные считываются в двоичной форме, то есть возвращаются байты ресурса так, как они есть.
get_metadata_lines(name) - выдёт (yield) ресурсы метаданных в виде списка не пустых и не закомментированных строк. Это более короткая версия вызова yield_lines(provider.get_metadata(name)). Смотрите раздел yield_lines() ниже, где более подробно рассказано о его синтаксисе.
run_script(script_name, namespace) - выполняет указанный скрипт в словаре пространства имён. Возбуждает исключение ResolutionError если нет скрипта с таким именем в каталоге scripts метаданных. namespace должен быть словарём Python, обычно это словарь модуля, если скрипт запускается как модуль.

Исключения

pkg_resources предоставляет простую иерархию исключений для проблем, которые могут возникнуть при обработке запроса для обнаружения и активации пакетов:
....ResoulutionError
........DistributionNotFound
........VersionConflict
........UnknownExtra
....ExtractionError
ResolutionError - этот класс используется как базовый класс для других трёх исключений, так что Вы можете поймать всех их одним выражением except.Оно так же возбуждается непосредственно при возникновении различных проблем разрешения требований, как попытка запустить скрипт, который не существует в запрашиваемом дистрибутиве.
DistributionNotFound - дистрибутив, удволетворяющий требованиям не может быть найден.
VersionConflict - запрашиваемая версия проекта конфликтует с уже активной версией этого же проекта.
UnknownExtra - один из запрошенных extra не был опознан дистрибутивом, где его искали.
ExtractionError - проблема возникла при извлечении ресурса в кэш яиц Python. Следующие атрибуты доступны для экземпляров этого исключения:
manager - менеджер ресурсов, который вызвал исключение
chache_path - корневой каталог для извлечения ресурсов
original_error - экземпляр исключения, который привёл к ошибке извлечения

Поддержка пользовательских импортёров (раздел будет доработан позже, когда разберусь с PEP302)

По умолчанию pkg_resources поддерживает нормальные импортёры файлововй системы и zipimport. Если Вы хотите использовать возможности pkg_resources вместе с другими (PEP 302 совместимыми) импортёрами или загрузчиками модулей, Вам может потребоваться зарегистрировать различные обработчики и вспомогательные функции при помощи следующих API:
register_finder(importer_type, distribution_finder) - регистрирует distribution_finder чтобы найти дистрибутивы в элементах sys.path. importer_type - тип или класс PEP 302 Importer (обработчик элемента sys.path) и distribution_finder вызывается, когда



Supporting Custom Importers
===========================
``register_finder(importer_type, distribution_finder)``
    Register `distribution_finder` to find distributions in ``sys.path`` items.
    `importer_type` is the type or class of a PEP 302 "Importer" (``sys.path``
    item handler), and `distribution_finder` is a callable that, when passed a
    path item, the importer instance, and an `only` flag, yields
    ``Distribution`` instances found under that path item.  (The `only` flag,
    if true, means the finder should yield only ``Distribution`` objects whose
    ``location`` is equal to the path item provided.)

    See the source of the ``pkg_resources.find_on_path`` function for an
    example finder function.

``register_loader_type(loader_type, provider_factory)``
    Register `provider_factory` to make ``IResourceProvider`` objects for
    `loader_type`.  `loader_type` is the type or class of a PEP 302
    ``module.__loader__``, and `provider_factory` is a function that, when
    passed a module object, returns an `IResourceProvider`_ for that module,
    allowing it to be used with the `ResourceManager API`_.

``register_namespace_handler(importer_type, namespace_handler)``
    Register `namespace_handler` to declare namespace packages for the given
    `importer_type`.  `importer_type` is the type or class of a PEP 302
    "importer" (sys.path item handler), and `namespace_handler` is a callable
    with a signature like this::

        def namespace_handler(importer, path_entry, moduleName, module):
            # return a path_entry to use for child packages

    Namespace handlers are only called if the relevant importer object has
    already agreed that it can handle the relevant path item.  The handler
    should only return a subpath if the module ``__path__`` does not already
    contain an equivalent subpath.  Otherwise, it should return None.

    For an example namespace handler, see the source of the
    ``pkg_resources.file_ns_handler`` function, which is used for both zipfile
    importing and regular importing.


IResourceProvider
-----------------

``IResourceProvider`` is an abstract class that documents what methods are
required of objects returned by a `provider_factory` registered with
``register_loader_type()``.  ``IResourceProvider`` is a subclass of
``IMetadataProvider``, so objects that implement this interface must also
implement all of the `IMetadataProvider Methods`_ as well as the methods
shown here.  The `manager` argument to the methods below must be an object
that supports the full `ResourceManager API`_ documented above.

``get_resource_filename(manager, resource_name)``
    Return a true filesystem path for `resource_name`, co-ordinating the
    extraction with `manager`, if the resource must be unpacked to the
    filesystem.

``get_resource_stream(manager, resource_name)``
    Return a readable file-like object for `resource_name`.

``get_resource_string(manager, resource_name)``
    Return a string containing the contents of `resource_name`.

``has_resource(resource_name)``
    Does the package contain the named resource?

``resource_isdir(resource_name)``
    Is the named resource a directory?  Return a false value if the resource
    does not exist or is not a directory.

``resource_listdir(resource_name)``
    Return a list of the contents of the resource directory, ala
    ``os.listdir()``.  Requesting the contents of a non-existent directory may
    raise an exception.

Note, by the way, that your provider classes need not (and should not) subclass
``IResourceProvider`` or ``IMetadataProvider``!  These classes exist solely
for documentation purposes and do not provide any useful implementation code.
You may instead wish to subclass one of the `built-in resource providers`_.


Built-in Resource Providers
---------------------------

``pkg_resources`` includes several provider classes that are automatically used
where appropriate.  Their inheritance tree looks like this::

    NullProvider
        EggProvider
            DefaultProvider
                PathMetadata
            ZipProvider
                EggMetadata
        EmptyProvider
            FileMetadata


``NullProvider``
    This provider class is just an abstract base that provides for common
    provider behaviors (such as running scripts), given a definition for just
    a few abstract methods.

``EggProvider``
    This provider class adds in some egg-specific features that are common
    to zipped and unzipped eggs.

``DefaultProvider``
    This provider class is used for unpacked eggs and "plain old Python"
    filesystem modules.

``ZipProvider``
    This provider class is used for all zipped modules, whether they are eggs
    or not.

``EmptyProvider``
    This provider class always returns answers consistent with a provider that
    has no metadata or resources.  ``Distribution`` objects created without
    a ``metadata`` argument use an instance of this provider class instead.
    Since all ``EmptyProvider`` instances are equivalent, there is no need
    to have more than one instance.  ``pkg_resources`` therefore creates a
    global instance of this class under the name ``empty_provider``, and you
    may use it if you have need of an ``EmptyProvider`` instance.

``PathMetadata(path, egg_info)``
    Create an ``IResourceProvider`` for a filesystem-based distribution, where
    `path` is the filesystem location of the importable modules, and `egg_info`
    is the filesystem location of the distribution's metadata directory.
    `egg_info` should usually be the ``EGG-INFO`` subdirectory of `path` for an
    "unpacked egg", and a ``ProjectName.egg-info`` subdirectory of `path` for
    a "development egg".  However, other uses are possible for custom purposes.

``EggMetadata(zipimporter)``
    Create an ``IResourceProvider`` for a zipfile-based distribution.  The
    `zipimporter` should be a ``zipimport.zipimporter`` instance, and may
    represent a "basket" (a zipfile containing multiple ".egg" subdirectories)
    a specific egg *within* a basket, or a zipfile egg (where the zipfile
    itself is a ".egg").  It can also be a combination, such as a zipfile egg
    that also contains other eggs.

``FileMetadata(path_to_pkg_info)``
    Create an ``IResourceProvider`` that provides exactly one metadata
    resource: ``PKG-INFO``.  The supplied path should be a distutils PKG-INFO
    file.  This is basically the same as an ``EmptyProvider``, except that
    requests for ``PKG-INFO`` will be answered using the contents of the
    designated file.  (This provider is used to wrap ``.egg-info`` files
    installed by vendor-supplied system packages.)


Utility Functions
=================

In addition to its high-level APIs, ``pkg_resources`` also includes several
generally-useful utility routines.  These routines are used to implement the
high-level APIs, but can also be quite useful by themselves.


Parsing Utilities
-----------------

``parse_version(version)``
    Parse a project's version string, returning a value that can be used to
    compare versions by chronological order.  Semantically, the format is a
    rough cross between distutils' ``StrictVersion`` and ``LooseVersion``
    classes; if you give it versions that would work with ``StrictVersion``,
    then they will compare the same way.  Otherwise, comparisons are more like
    a "smarter" form of ``LooseVersion``.  It is *possible* to create
    pathological version coding schemes that will fool this parser, but they
    should be very rare in practice.

    The returned value will be a tuple of strings.  Numeric portions of the
    version are padded to 8 digits so they will compare numerically, but
    without relying on how numbers compare relative to strings.  Dots are
    dropped, but dashes are retained.  Trailing zeros between alpha segments
    or dashes are suppressed, so that e.g. "2.4.0" is considered the same as
    "2.4". Alphanumeric parts are lower-cased.

    The algorithm assumes that strings like "-" and any alpha string that
    alphabetically follows "final"  represents a "patch level".  So, "2.4-1"
    is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is
    considered newer than "2.4-1", which in turn is newer than "2.4".

    Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that
    come before "final" alphabetically) are assumed to be pre-release versions,
    so that the version "2.4" is considered newer than "2.4a1".  Any "-"
    characters preceding a pre-release indicator are removed.  (In versions of
    setuptools prior to 0.6a9, "-" characters were not removed, leading to the
    unintuitive result that "0.2-rc1" was considered a newer version than
    "0.2".)

    Finally, to handle miscellaneous cases, the strings "pre", "preview", and
    "rc" are treated as if they were "c", i.e. as though they were release
    candidates, and therefore are not as new as a version string that does not
    contain them.  And the string "dev" is treated as if it were an "@" sign;
    that is, a version coming before even "a" or "alpha".

.. _yield_lines():

``yield_lines(strs)``
    Yield non-empty/non-comment lines from a string/unicode or a possibly-
    nested sequence thereof.  If `strs` is an instance of ``basestring``, it
    is split into lines, and each non-blank, non-comment line is yielded after
    stripping leading and trailing whitespace.  (Lines whose first non-blank
    character is ``#`` are considered comment lines.)

    If `strs` is not an instance of ``basestring``, it is iterated over, and
    each item is passed recursively to ``yield_lines()``, so that an arbitarily
    nested sequence of strings, or sequences of sequences of strings can be
    flattened out to the lines contained therein.  So for example, passing
    a file object or a list of strings to ``yield_lines`` will both work.
    (Note that between each string in a sequence of strings there is assumed to
    be an implicit line break, so lines cannot bridge two strings in a
    sequence.)

    This routine is used extensively by ``pkg_resources`` to parse metadata
    and file formats of various kinds, and most other ``pkg_resources``
    parsing functions that yield multiple values will use it to break up their
    input.  However, this routine is idempotent, so calling ``yield_lines()``
    on the output of another call to ``yield_lines()`` is completely harmless.

``split_sections(strs)``
    Split a string (or possibly-nested iterable thereof), yielding ``(section,
    content)`` pairs found using an ``.ini``-like syntax.  Each ``section`` is
    a whitespace-stripped version of the section name ("``[section]``")
    and each ``content`` is a list of stripped lines excluding blank lines and
    comment-only lines.  If there are any non-blank, non-comment lines before
    the first section header, they're yielded in a first ``section`` of
    ``None``.

    This routine uses ``yield_lines()`` as its front end, so you can pass in
    anything that ``yield_lines()`` accepts, such as an open text file, string,
    or sequence of strings.  ``ValueError`` is raised if a malformed section
    header is found (i.e. a line starting with ``[`` but not ending with
    ``]``).

    Note that this simplistic parser assumes that any line whose first nonblank
    character is ``[`` is a section heading, so it can't support .ini format
    variations that allow ``[`` as the first nonblank character on other lines.

``safe_name(name)``
    Return a "safe" form of a project's name, suitable for use in a
    ``Requirement`` string, as a distribution name, or a PyPI project name.
    All non-alphanumeric runs are condensed to single "-" characters, such that
    a name like "The $$$ Tree" becomes "The-Tree".  Note that if you are
    generating a filename from this value you should combine it with a call to
    ``to_filename()`` so all dashes ("-") are replaced by underscores ("_").
    See ``to_filename()``.

``safe_version(version)``
    Similar to ``safe_name()`` except that spaces in the input become dots, and
    dots are allowed to exist in the output.  As with ``safe_name()``, if you
    are generating a filename from this you should replace any "-" characters
    in the output with underscores.

``safe_extra(extra)``
    Return a "safe" form of an extra's name, suitable for use in a requirement
    string or a setup script's ``extras_require`` keyword.  This routine is
    similar to ``safe_name()`` except that non-alphanumeric runs are replaced
    by a single underbar (``_``), and the result is lowercased.

``to_filename(name_or_version)``
    Escape a name or version string so it can be used in a dash-separated
    filename (or ``#egg=name-version`` tag) without ambiguity.  You
    should only pass in values that were returned by ``safe_name()`` or
    ``safe_version()``.


Platform Utilities
------------------

``get_build_platform()``
    Return this platform's identifier string.  For Windows, the return value
    is ``"win32"``, and for Mac OS X it is a string of the form
    ``"macosx-10.4-ppc"``.  All other platforms return the same uname-based
    string that the ``distutils.util.get_platform()`` function returns.
    This string is the minimum platform version required by distributions built
    on the local machine.  (Backward compatibility note: setuptools versions
    prior to 0.6b1 called this function ``get_platform()``, and the function is
    still available under that name for backward compatibility reasons.)

``get_supported_platform()`` (New in 0.6b1)
    This is the similar to ``get_build_platform()``, but is the maximum
    platform version that the local machine supports.  You will usually want
    to use this value as the ``provided`` argument to the
    ``compatible_platforms()`` function.

``compatible_platforms(provided, required)``
    Return true if a distribution built on the `provided` platform may be used
    on the `required` platform.  If either platform value is ``None``, it is
    considered a wildcard, and the platforms are therefore compatible.
    Likewise, if the platform strings are equal, they're also considered
    compatible, and ``True`` is returned.  Currently, the only non-equal
    platform strings that are considered compatible are Mac OS X platform
    strings with the same hardware type (e.g. ``ppc``) and major version
    (e.g. ``10``) with the `provided` platform's minor version being less than
    or equal to the `required` platform's minor version.

``get_default_cache()``
    Determine the default cache location for extracting resources from zipped
    eggs.  This routine returns the ``PYTHON_EGG_CACHE`` environment variable,
    if set.  Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of
    the user's "Application Data" directory.  On all other systems, it returns
    ``os.path.expanduser("~/.python-eggs")`` if ``PYTHON_EGG_CACHE`` is not
    set.


PEP 302 Utilities
-----------------

``get_importer(path_item)``
    Retrieve a PEP 302 "importer" for the given path item (which need not
    actually be on ``sys.path``).  This routine simulates the PEP 302 protocol
    for obtaining an "importer" object.  It first checks for an importer for
    the path item in ``sys.path_importer_cache``, and if not found it calls
    each of the ``sys.path_hooks`` and caches the result if a good importer is
    found.  If no importer is found, this routine returns an ``ImpWrapper``
    instance that wraps the builtin import machinery as a PEP 302-compliant
    "importer" object.  This ``ImpWrapper`` is *not* cached; instead a new
    instance is returned each time.

    (Note: When run under Python 2.5, this function is simply an alias for
    ``pkgutil.get_importer()``, and instead of ``pkg_resources.ImpWrapper``
    instances, it may return ``pkgutil.ImpImporter`` instances.)


File/Path Utilities
-------------------

``ensure_directory(path)``
    Ensure that the parent directory (``os.path.dirname``) of `path` actually
    exists, using ``os.makedirs()`` if necessary.

``normalize_path(path)``
    Return a "normalized" version of `path`, such that two paths represent
    the same filesystem location if they have equal ``normalized_path()``
    values.  Specifically, this is a shortcut for calling ``os.path.realpath``
    and ``os.path.normcase`` on `path`.  Unfortunately, on certain platforms
    (notably Cygwin and Mac OS X) the ``normcase`` function does not accurately
    reflect the platform's case-sensitivity, so there is always the possibility
    of two apparently-different paths being equal on such platforms.

вторник, 16 апреля 2013 г.

South. Документация. Конвертирование приложения

Конвертирование приложения

Конвертировать приложение для  использования South очень легко:
  • Отредактируйте ваш settings.py и укажите south в INSTALLED_APPS (мы предполагаем, что Вы установили south туда, куда нужно)
  • Запустите ./manage.py syncdb для того, чтобы загрузить таблицу South в БД. Обратите внимание, что теперь syncdb не такой как раньше - South его изменил
  • Запустите ./manage.py convert_to_south myapp - South автоматически подготовит и выполнит первую миграцию
Обратите внимание, что Вы должны конвертировать приложение до того, как Вы сделаете изменения; South обнаруживает изменения по сравнению с замороженным состоянием последней миграции, так что он не сможет обнаружить изменения, сделанные до конвертирования в South.

Конвертирование других установок и серверов

Команда convert_to_south рабоатет целиком на первой машине, на которой Вы его запустите. После того, как Вы подтвердите начальную миграцию, которую он сделал, в системе управления версиями, Вы должны запустить ./manage.py migrate myapp 0001 --fake на всех машинах, которые содержат копию кода (убедитесь, что они имеют обновлённый код и схему).
(Для тех, кому интересно, это нужно поскольку начальная миграция, которую делает convert_to_south, проверит и создат все существующие таблицы; вместо этого Вы говорите South, что это уже сделано при помощи --fake, так что следующие миграции будут применены корректно).
Помните, что новые установки кода после этого не потребуют этих шагов, Вам нужно будет лишь сделать syncdb а, затем, провести нормальную миграцию.