Глава 5. Конфигурация и входные данные
Содержание
Теперь, когда в рассмотрели грамматику Python, самое время исследовать как код переходит в исполняемое состояние.
Существует множество способов, коими код Python может запускаться в CPython. Вот некоторые из наиболее употребимых подходов:
-
Запустить
python -c
и некую строку Python -
Запустить
python -m
и название некого модуля -
Запустить
python <file>
с путём к некому файлу, который содержит код Python -
Направить в конвейере код Python в некий исполняемый
python
черезstdin
, скажемcat <file> | python
-
Запустить REPL и выполнять команды по одной за раз
-
Применяя API C пользоваться Python как некой встроенной средой
Смотри также | |
---|---|
В Python имеется настолько много способов исполнения сценариев, что это может слегка подавлять. Для дополнительных сведений относительно запуска сценариев Python отсылаем вам к материалам Real Python "How to Run Your Python Scripts". |
Для исполнения кода Python его интерпретатору требуется обладать тремя элементами:
-
Исполняемым модулем
-
Неким состоянием для удержания таких сведений как переменные
-
Некой конфигурацией, например, какие варианты включены
При наличии этих трёх компонентов имеющийся интерпретатор способен исполнить код и предоставить некий вывод:
Замечание | |
---|---|
Аналогично стилю руководства PEP 8 для кода Python, имеется руководство стиля PEP 7 для кода C CPython. Он включает в себя следующие стандарты именования для исходного кода C:
В отличии от PEP 8, имеется несколько инструментов для проверки совместимости в PEP 7. Вместо этого данная задача выполняется разработчиками ядра в рамках проверки кода. Как и любой управляемый человеком процесс, этот тип проверки не является защищённым от ошибок, а потому вы, скорее всего, обнаружите код, не соответствующий PEP 7.. Единственным встроенным инструментом для автоматизации этого процесса выступает сценарий с названием
Он распространит ошибку вверх для всех символов, которые пребывают в |
Прежде чем исполнять любой код Python, имеющийся CPython времени исполнения вначале устанавливает значение конфигурации времени исполнения и разные параметры предоставляемые пользователем.
Как это предопределено PEP 587, значение конфигурации времени исполнения располагается в трёх местах:
-
PyPreConfig, используемом для конфигурации предварительной инициализации
-
PyConfig, применяемом для конфигурации времени выполнения
-
В скомпилированной конфигурации самого интерпретатора CPython
Обе структуры данных, PyPreConfig
и PyConfig
определены в
nclude/cpython/initconfig.h
.
Конфигурация предварительной инициализации отделена от конфигурации времени выполнения, поскольку её свойства относятся к установленной операционной системе или среде пользователя.
PyPreConfig
обладает тремя первичными функциями:
-
Монтаже распределителя памяти Python
-
Настройке значения локализации LC_CTYPE на значение системной или предпочитаемой пользователем локализации
-
Монтаже режима UTF-8 (PEP 540)
Сам тип PyPreConfig
содержит следующие поля, причём все с типом int
:
-
allocator
: Выбирает распределитель памяти, например,PYMEM_ALLOCATOR_MALLOC
. Для получения дополнительных сведений относительно распределителя памяти запустите./configure --help
. -
configure_locale
: Устанавливает локализацию LC_CTYPE в значение предпочитаемой пользователем. Когда равен0
, тогда устанавливаетcoerce_c_locale
иcoerce_c_locale_warn
равным0
. -
coerce_c_locale
: Если равен2
, то приводится к локализации C. Если равен1
, тогда считывается значение локализации LC_CTYPE для принятия решения относительно тог должна ли они приводиться. -
coerce_c_locale_warn
: В случае когда не равно нулю, выдаётся предупреждение если приводится локализация C/ -
dev_mode
: Включает режим разработки. -
isolated
: Разрешает режим изоляции.sys.path
не содержит ни значения каталога сценария, ни значения каталога пакетов площадки пользователя. -
legacy_windows_fs_encoding
: (Только для Windows) при неравенстве нулю, отключает режим UTF-8 и устанавливает файловую систему Python на кодирование вmbcs
. -
parse_argv
: При неравенстве нулю используются параметры командной строки. -
use_environment
: Когда больше нуля, применяются переменные среды. -
utf8_mode
: При неравенстве нулю разрешается режим UTF-8.
Ниже приводятся относящиеся к PyPreConfig
исходные файлы:
Файл | Назначение |
---|---|
|
Загружает значения конфигурации из среды своей системы и сливает её со всеми флагами командной строки |
|
Определяет структуру данных инициализирующей конфигурации |
Второй этап настроек состоит в конфигурации времени выполнения. Структура данных настроек времени выполнения в PyConfig содержит некоторые значения, включая следующие:
-
Флаги времени выполнения для таких режимов как отладочный и оптимизирующий
-
Значение режима исполнения, например, в качестве файла сценария,
stdin
или модуля -
Расширенные опции, предписываемые через
-X <option>
-
Переменные среды для настроек времени выполнения
Эти данные конфигурации применяются CPython времени выполнения для включения и отключения функциональных возможностей.
Python также поставляется с некоторыми вариантами взаимодействия командной строки. К примеру, CPython обладает режимом с названием verbose mode (подробный режим). Он в первую очередь предназначен разработчикам для отладки CPython.
Вы можете разрешить подробный режим при помощи флага -v
и Python будет печатать на экран сообщения
при загрузке модулей:
$ ./python -v -c "print('hello world')"
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
...
Вы обнаружите сотни строк или даже больше со всеми вашими импортами пакетов вашей пользовательской площадки и чем- то ещё в вашей системной среде.
Поскольку конфигурация времени выполнения может настраиваться различными способами, настройки конфигурации обладают уровнями следования. Вот порядок следования подробного режима:
-
Установленные по умолчанию значения для
config->verbose
жёстко закодированы в-1
в имеющемся исходном коде -
Для настройки значения
config->verbose
применяется переменная средыPYTHONVERBOSE
-
Если эта переменная не существует, тогда остаётся назначенным установленное по умолчанию значение значение
-1
-
В config_parse_cmdline() внутри
Python/initconfig.c
, если он предоставлен, применяется для установки соответствующего значения флаг командной строки. -
Это значение копируется в глобальную переменную,
Py_VerboseFlag
через _Py_GetGlobalVariablesAsDict().
Все значения PyConfig
следуют теми же самыми последовательностями и порядком следования:
Интерпретаторы CPython обладают неким набором флагов времени выполнения. Эти
флаги выступают расширенными функциональными возможностями, применяемыми для переключения специфичного для CPython поведения. Внутри некого
сеанса Python вы можете получать доступ к значениям флагов времени выполнения, причём как в подробном, так и в тихом режиме через кортеж с
названием sys.flags
.
Все флаги -X
доступны внутри установленного словаря
sys._xoptions
:
$ ./python -X dev -q
>>> import sys
>>> sys.flags
sys.flags(debug=0, inspect=0, interactive=0, optimize=0,
dont_write_bytecode=0, no_user_site=0, no_site=0,
ignore_environment=0, verbose=0, bytes_warning=0,
quiet=1, hash_randomization=1, isolated=0,
dev_mode=True, utf8_mode=0)
>>> sys._xoptions
{'dev': True}
Помимо значений конфигурации времени исполнения в Include/cpython/initconfig.h
,
также имеются встроенная конфигурация, располагающаяся в pyconfig.h
в корне папки. Этот файл
динамически создаётся на этапе ./configure
в процессе сборки для macS и Linux, или через
build.bat
в Windows.
Вы можете обнаружить эту встроенную конфигурацию, выполнив следующее:
$ ./python -m sysconfig
Platform: "macosx-10.15-x86_64"
Python version: "3.9"
Current installation scheme: "posix_prefix"
Paths:
data = "/usr/local"
include = "/Users/anthonyshaw/CLionProjects/cpython/Include"
platinclude = "/Users/anthonyshaw/CLionProjects/cpython"
...
Встроенные свойства конфигурации являются значениями времени компиляции, применяемыми для выбора дополнительных модулей, для их связывания с создаваемым исполняемым файлом. Например, отладчиков, инструментальных библиотек, а также распределителей памяти, которые все монтируются во время компиляции.
Со всеми этими тремя этапами конфигурации, наш CPython теперь может получать входные данные и перерабатывать получаемый текст в исполняемый код.
Прежде чем код может быть исполнен, он должен быть скомпилирован в некий модуль из получаемых на вход данных. Как уже обсуждалось ранее, входные данные могут различаться по своему типу:
-
Локальные файлы и пакеты
-
Потоки ввода/ вывода, например,
stdin
или конвейер в памяти -
Строки
Входные данные считываются, передаются своему синтаксическому анализатору, а затем передаются в сам компилятор:
Благодаря такой гибкости большая часть исходного кода CPython посвящена обработке входных данных в своём синтаксическом анализаторе CPython.
Имеются четыре основных файла, с которыми имеет дело интерфейс командной строки:
Файл | Назначение |
---|---|
|
Модуль стандартной библиотеки для импорта модулей Python и их исполнения |
|
Обёртывающие исполнение внешнего кода, например, из файла, модуля или входного потока функции |
|
Точка входа для исполняемого |
|
Обёртывающие внутренние API C функции для обработки входных данных из командой строки |
После того как CPythonобладает конфигурацией времени исполнения и необходимыми аргументами командной строки, он может загружать код, который
ему надлежит выполнять. Эта задача обрабатывается pymain_main() из Modules/main.c
.
CPython теперь выполнит предоставленный код со всеми предписанными параметрами во вновь созданном экземпляре
PyConfig
.
CPython способен выполнять небольшие приложения Python из командной строки с параметром -c
. Например,
рассмотрим что произойдёт когда вы выполните print(2 ** 2)
:
$ ./python -c "print(2 ** 2)"
4
Прежде всего, внутри Modules/main.c
выполняется
pymain_run_command(), беря ту команду,
которая передаётся в -c
в качестве аргумента в C с типом
wchar_t*
.
Замечание | |
---|---|
Тип При преобразовании значения Строки Unicode Python подробно обсуждаются в разделе Тип строки Unicode Главы 11, Объекты и типы. |
После того как это осуществлено, pymain_run_command() передаёт Python объект байт в PyRun_SimpleStringFlags() для исполнения.
PyRun_SimpleStringFlags()
является частью Python/pythonrun.c
. Его цель состоит в повороте
строки в модуль Python с последующей отправкой её на выполнение.
Модулю Python требуется некая точка входа, __main__
, для его обособленного исполнения и
PyRun_SimpleStringFlags()
создаёт её в неявном виде.
После того как PyRun_SimpleStringFlags() создал модуль и некий словарь, он вызывает PyRun_StringFlags(). PyRun_StringFlags() создаёт поддельное имя файла и затем вызывает синтаксический анализатор Python для создания дерева абстрактного синтаксиса (AST, abstract syntax tree) из этой строки и возвращает некий модуль. В нашей следующей главе вы получите дополнительные сведения относительно AST.
Замечание | |
---|---|
Модули Python это определённые структуры данных, применяемые для удобства синтаксического анализа кода в самом компиляторе. Структурой C
для модуля Python выступает |
Другой способ выполнения команд Python состоит в применении параметра -m
с названием некого модуля.
Типичным примером является python -m unittest
, который запускает модуль
unittest
в стандартной библиотеке.
Такая возможность исполнения модулей в качестве сценариев изначально предлагалась в PEP 338. Окончательный стандарт для относительных импортов в явном виде был определён в PEP 366.
Флаг -m
подразумевает, что внутри соответствующего пакета модуля вы желаете выполнить что- то внутри
соответствующей точки входа (__main__). Это также подразумевает,
что вы хотите отыскать sys.path
для модуля с этим названием.
Такой механизм поиска в соответствующей импортируемой библиотеке (importlib
) это то, почему вам нет
нужды помнить где ваш модуль unittest
хранится в вашей файловой системе.
CPython импортирует стандартный библиотечный модуль, runpy
и выполняет его при помощи
и PyObject_Call(). Такой импорт
выполняется при помощи API функции Import_ImportModule() C, обнаруживаемой внутри файла
Python/import.c
.
Замечание | |
---|---|
В Python, если у вас имеется некий объект и вы желаете получить атрибут, вы можете вызвать
Если вы желаете вызвать вызывавшего, тогда вы можете заключить его в круглые скобки, или же можете вызывать свойство
|
Этот модуль runpy
написан на чистом Python и располагается в
Lib/runpy.py
.
Выполнение python -m <module>
эквивалентно python -m runpy <module>
.
Модуль runpy
был создан для абстрагирования самого процесса от местоположения и выполнения модулей в
некой операционной системе.
Модуль runpy
для запуска своего целевого модуля выполняет три основных момента:
-
Вызывает
_import__()
для того модуля, название которого вы предоставили -
Устанавливает
__name__
(название вашего модуля) в пространство имён с названием__main__
-
Выполняет данный модуль внутри соответствующего пространства имён
__main__
Модуль runpy
также поддерживает исполнение каталогов и файлов ZIP.
Когда самый первый аргумент для python
является названием файла, например,
python test.py
, тогда CPython откроет некий файловый дескриптор и передаст его обработку в
PyRun_SimpleFileExFlags()
внутри файла Python/pythonrun.c
.
Существует три пути, которые может предпринять эта функция:
-
Когда путь к файлу это файл
.pyc
, тогда она вызовет run_pyc_file() -
Если путём к файлу выступает некий файл сценария
.py
, тогда она запускает Главе 2, -
Если файловым путём является
stdin
по той причине, что пользователь запустил<command> | python
, тогда он трактуетstdin
как некий дескриптор файла и запускает PyRun_FileExFlags()
Для stdin
и базовых файлов сценариев CPython будет передавать файловый дескриптор в
PyRun_FileExFlags(),
расположенный в файле Python/pythonrun.c
.
Основное назначение PyRun_FileExFlags() аналогично PyRun_SimpleStringFlags(). CPython будет загружать полученный файловый дескриптор в PyParser_ASTFromFileObject().
Идентично PyRun_SimpleStringFlags(), после того как PyRun_FileExFlags() создал некий модуль Python из соответствующего файла, он отправляет этот модуль в run_mod() на исполнение.
Когда некий пользователь запускает python
с каким- то путём к файлу .pyc
,
тогда вместо загрузки такого файла как обычного текстового файла и его синтаксического анализа, CPython будет предполагать что такой файл
.pyc
содержит некий кодовый объект, записанный на диск.
В PyRun_SimpleFileExFlags()
имеется некое выражение для того чтобы соответствующий пользователь предоставлял некий путь файла для файла
.pyc
.
run_pyc_file() внутри
Python pythonrun.c
выстраивает (маршализует) соответствующий
кодовый объект из своего файла .pyc
применяя некий файловый дескриптор.
Такая структура кодового объекта на диске является тем способом, коим компилятор CPython кэширует скомпилированный код с тем, чтобы не выполнять его синтаксический анализ всякий раз, когда вызывается данный сценарий.
Замечание | |
---|---|
Marshaling (выстраивание) это термин для копирования необходимого содержимого какого- то файла в оперативную память и преобразование его в специфическую структуру данных. |
После того как соответствующий кодовый объект был выстроен в оперативной памяти, он отправляется в
run_eval_code_obj(),
который вызывает Python/ceval.c
для выполнения этого кода.
В этой главе вы раскрыли как загружается большое число вариантов конфигурации Python и как код предоставляется на вход его интерпретатору.
Гибкость Python с входными данными превращает его в великолепный инструмент для некого диапазона приложений, таких как:
-
Утилиты командной строки
-
Сетевые приложения с длительным временем работы, например, веб серверы
-
Коротких, компактных сценариев
Способность Python устанавливать свойства конфигурации многими способами вносит некую сложность. Например, когда вы тестируете некое приложение Python в Python 3.8 и оно выполняется корректно, но если затем оно отказывает в некой другой среде, тогда вам потребуется разобраться какие настройки были другими в этой среде.
Это означает, что вам потребуется проинспектировать переменные среды, флаги времени выполнения и даже свойства системной конфигурации.
Соответствующие свойства времени компиляции, обнаруживаемые в системной конфигурации могут разниться между дистрибутивами Python. Например, Python 3.8, выгруженный с Python.org под macOS имеет другие значения по умолчанию, нежели тот дистрибутив Python 3.8, который обнаруживается в Homebrew или в дистрибутиве Anaconda.
Все эти методы входных данных выдают некий модуль Python. В нашей следующей главе вы увидите как из входных данных создаются модули.