Часть 1Часть 2Часть 3Часть 4

Понимание работы Brython

Ваше знакомство с различными способами установки Brython дало вам общее представление о том, как работает реализация. Вот краткое изложение некоторых характеристик, которые вы узнали на этом этапе руководства:

  • Это реализация Python на JavaScript.

  • Это транслятор Python в JavaScript и среда выполнения, работающая в браузере.

  • Он предоставляет две основные библиотеки, доступные в виде файлов JavaScript:

  • Он вызывает brython(), который компилирует код Python, содержащийся в тегах script с типом text/python. В следующих разделах вы более подробно рассмотрите принцип работы Brython.

Основные компоненты Brython

Ядро Brython содержится в brython.js или в brython.min.js, минимизированной версии движка Brython. Обе включают следующие ключевые компоненты:

  • brython() — это основная функция JavaScript, представленная в глобальном пространстве имен JavaScript. Вы не можете выполнить какой-либо код Python без вызова этой функции. Это единственная функция JavaScript, которая вызывается явно.

  • __BRYTHON__ — глобальный объект JavaScript, который содержит все внутренние объекты, необходимые для запуска скриптов Python. Этот объект не используется напрямую при написании приложений Brython. Если вы посмотрите на код Brython — как JavaScript, так и Python, то увидите регулярное появление __BRYTHON__. Вам не нужно использовать этот объект, но вы должны знать о нем, когда видите ошибку или когда хотите отладить свой код в консоли браузера.

  • Встроенные типы — это реализации встроенных типов Python в JavaScript. Например, py_int.js, py_string.js и py_dicts.js — это соответствующие реализации int, str и dict.

  • browser — это модуль браузера, который предоставляет объекты JavaScript, обычно используемые в интерфейсных веб-приложениях, такие как интерфейсы DOM с использованием document и окно браузера с использованием объекта window.

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

Стандартная библиотека Brython

Теперь, когда у вас есть общее представление о базовом Brython-скрипте brython.js вы узнаете о сопутствующем ему brython_stdlib.js.

brython_stdlib.js предоставляет стандартную библиотеку Python. Посредством этого скрипта Brython компилирует стандартную библиотеку Python в JavaScript и объединяет результат с brython_stdlib.js.

Brython призван быть максимально приближенным к CPython, эталонной реализации Python. Для получения дополнительной информации о CPython ознакомьтесь с материалами Your Guide to the CPython Source Code и CPython Internals.

Поскольку Brython работает в контексте веб-браузера и у него есть некоторые ограничения. Например, браузер не разрешает прямой доступ к файловой системе, поэтому открытие файла с помощью os.open() невозможно. Функции, не относящиеся к веб-браузеру, могут быть не реализованы. Например, код ниже работает в среде Brython:

>>> import os
>>> os.unlink()
Traceback (most recent call last):
  File , line 1, in 
NotImplementedError: posix.unlink is not implemented

os.unlink() вызывает исключение, поскольку удаление локального файла из среды браузера небезопасно, а API файлов и записей каталогов является лишь черновым вариантом.

Brython поддерживает только собственные модули Python. Он не поддерживает модули Python, созданные на C, если только они не были переписаны на JavaScript. Например, hashlib написан на C в CPython и реализован на JavaScript в Brython. Вы можете ознакомиться со списком модулей в дистрибутиве Brython, чтобы сравнить его с реализацией CPython.

Чтобы импортировать модули из стандартной библиотеки Python вам необходимо включить brython_stdlib.js или brython_stdlib.min.js.

Brython в действии

К этому моменту вы, вероятно, задаетесь вопросом, как Brython работает в браузере, который поддерживает только JavaScript. С помощью уже рассмотренных примеров и инструментов браузера вы узнаете, как выполняется код на Python в этой среде.

В разделе Установка CDN вы видели следующий пример:

<!doctype html>
<html>
    <head>
        <script
            src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.13.0/brython.js">
        </script>
    </head>
    <body onload="brython()">
        <script type="text/python">
            import browser
            browser.alert("Hello Real Python!")
        </script>
    </body>
</html>

После загрузки и анализа HTML-страницы brython() выполняет следующие действия:

  1. Считывает код Python, содержащийся в элементе <script type="text/python">

  2. Компилирует Python-код в эквивалентный JavaScript-код

  3. Оценивает полученный код JavaScript с помощью eval()

В приведенном выше примере код Python встроен в HTML-файл:

<script type="text/python">
    import browser
    browser.alert("Hello Real Python!")
</script>

Другой вариант — загрузить код Python из отдельного файла:

import browser
browser.alert("Hello Real Python!")

В этом случае файл Python будет выглядеть так:

import browser
browser.alert("Hello Real Python!")

Разделение кода Python и кода HTML — более чистый подход, позволяющий воспользоваться преимуществами и функциональными возможностями редакторов кода. Большинство редакторов поддерживают встроенный в HTML JavaScript, но они не поддерживают встроенный в HTML Python.

Внутренности Brython

В этом разделе представлен более глубокий анализ процесса преобразования кода Python в JavaScript. Если вас не интересуют эти подробности — можете пропустить этот раздел, так как он не требуется для понимания остальной части руководства. Чтобы проиллюстрировать этот процесс и заглянуть во внутренности Brython, выполните следующие шаги:

Откройте домашнюю страницу Brython.

  1. Откройте веб-консоль с помощью Cmd+Alt+I на Mac или Ctrl+Shift+I на Windows и Linux.

  2. В браузере JavaScript REPL введите и выполните следующий код:

> eval(__BRYTHON__.python_to_js("import browser; browser.console.log('Hello Brython!')"));

python_to_js() анализирует и компилирует передаваемый код Python в JavaScript, а затем выполняет JavaScript в веб-браузере. Вы должны получить следующий результат:

Оценка Python в JavaScript
Оценка Python в JavaScript

Применение eval() к коду Brython выводит в консоли браузера "Hello Brython!". Функция JavaScript возвращает undefined, что является возвращаемым значением по умолчанию для функции в JavaScript.

При создании приложения Brython вам не нужно явно вызывать функцию в __BRYTHON__ модуле JavaScript. Этот пример приведен только для того, чтобы продемонстрировать, как Brython работает за кулисами. Знание __BRYTHON__ может помочь вам читать код Brython и даже вносить вклад в проект по мере приобретения опыта. Это также поможет вам лучше понимать исключения, которые могут отображаться в консоли браузера.

Объект JavaScript __BRYTHON__ доступен в глобальной области видимости JavaScript, и доступ к нему можно получить с помощью консоли JavaScript браузера.

Использование Brython в браузере

На этом этапе у вас уже достаточно знаний о Brython, чтобы работать с более сложными примерами. В этом разделе вы создадите калькулятор Base64, чтобы поэкспериментировать в браузере с API DOM и другими функциями, которые обычно доступны только в JavaScript.

Вы можете загрузить исходный код примеров в этом руководстве, нажав на эту ссылку.

Начнем с изучения того, как манипулировать DOM с помощью Python и HTML.

API DOM в Brython

Чтобы поэкспериментировать с манипуляциями DOM, доступными в Brython, вы создадите форму для кодирования строки в Base64. Готовая форма будет выглядеть так:

Калькулятор формы Base64
Калькулятор формы Base64

Создайте следующий HTML-файл и назовите его index.html:

<!-- index.html -->
<!DOCTYPE html >
<html>
  <head>
    <meta charset="utf-8"/>
    <link rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/pure/3.0.0/pure-min.css" />
    <script
        src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.13.0/brython.min.js">
    </script>
    <script
        src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.13.0/brython_stdlib.min.js">
    </script>
    <script src="main.py" type="text/python" defer></script>
    <style>body { padding: 30px; }</style>
  </head>
  <body onload="brython()">
    <form class="pure-form" onsubmit="return false;">
      <fieldset>
        <legend>Base64 Calculator</legend>
        <input type="text" id="text-src" placeholder="Text to Encode" />
        <button
          type="submit" id="submit"
          class="pure-button pure-button-primary"
          autocomplete="off">Ok</button>
        <button id="clear-btn" class="pure-button">Clear</button>
      </fieldset>
    </form>
    <div id="b64-display"></div>
  </body>
</html>

Приведенный выше HTML-код загружает статические ресурсы, определяет макет пользовательского интерфейса и инициирует компиляцию Python:

  • Строка 7 загружает таблицу стилей PureCSS для улучшения стиля HTML по умолчанию. Это не обязательно для работы Brython.

  • Строка 9 загружает минимизированную версию движка Brython.

  • Строка 12 загружает минимизированную версию стандартной библиотеки Brython.

  • Строка 14 загружает main.py, который обрабатывает динамическую логику этой статической HTML-страницы. Обратите внимание на использование defer. Это помогает синхронизировать загрузку и оценку ресурсов и иногда необходимо, чтобы убедиться, что Brython и любые скрипты Python полностью загружены перед выполнением brython().

  • Строка 21 описывает поле input. В качестве аргумента это поле принимает строку для кодирования.

  • Строки 22–25 определяют значение по умолчанию button, что запускает основную логику страницы. Вы можете увидеть реализацию этой логики main.py ниже.

  • Строка 26 определяет button для очистки данных и элементов на странице. Это реализовано в main.py ниже.

  • В строке 29 объявляется объект div, который должен быть заполнителем для таблицы.

Соответствующий код Python main.py выглядит следующим образом:

from browser import document, prompt, html, alert
import base64

b64_map = {}

def base64_compute(_):
    value = document["text-src"].value
    if not value:
        alert("You need to enter a value")
        return
    if value in b64_map:
        alert(f"'The base64 value of '{value}' already exists: '{b64_map[value]}'")
        return
    b64data = base64.b64encode(value.encode()).decode()
    b64_map[value] = b64data
    display_map()

def clear_map(_) -> None:
    b64_map.clear()
    document["b64-display"].clear()

def display_map() -> None:
    table = html.TABLE(Class="pure-table")
    table <= html.THEAD(html.TR(html.TH("Text") + html.TH("Base64")))
    table <= (html.TR(html.TD(key) + html.TD(b64_map[key])) for key in b64_map)
    base64_display = document["b64-display"]
    base64_display.clear()
    base64_display <= table
    document["text-src"].value = ""

document["submit"].bind("click", base64_compute)
document["clear-btn"].bind("click", clear_map)

Код Python показывает определение функций обратного вызова и механизм управления DOM:

  • Строка 1 импортирует модули, которые вы используете для взаимодействия с DOM и кодом API браузера в brython.min.js.

  • Строка 2 импортирует base64, который доступен в стандартной библиотеке Brython, brython_stdlib.min.js.

  • В строке 4 объявляется словарь, в котором будут храниться данные в течение всего срока существования HTML-страницы.

  • Строка 6 определяет обработчик событий base64_compute(), который кодирует Base64-значение текста, введенного в поле ввода, с ID text-src. Это функция обратного вызова, которая принимает событие в качестве аргумента. Этот аргумент не используется в функции, но является обязательным в Brython и необязательным в JavaScript. В качестве соглашения вы можете использовать _в качестве фиктивного заполнителя. Пример такого использования описан в руководстве по стилю Google Python.

  • Строка 7 извлекает значение элемента DOM, идентифицированного с помощью text-src.

  • Строка 18 определяет обработчик событий clear_map(), который очищает данные и представление данных на этой странице.

  • Строка 22 определяет display_map(), который берет данные, содержащиеся в b64_map и отображает их под формой на странице.

  • Строка 26 извлекает элемент DOM с идентификатором text-src.

  • Строка 29 очищает значение элемента DOM с идентификатором text-src.

  • Строка 31 привязывает событие onclick кнопки submit к base64_compute().

  • Строка 32 привязывает событие onclick кнопки clear-btn к clear_map().

Для управления DOM Brython использует два оператора:

  1. <= — новый оператор, специфичный для Brython, который добавляет потомка к узлу. Вы можете увидеть несколько примеров такого использования в display_map() в строке 22.

  2. + является заменой Element.insertAdjacentHTML('afterend') и добавляет родственные узлы.

Вы можете увидеть оба оператора в следующем утверждении, взятом из display_map():

table <= html.THEAD(html.TR(html.TH("Text") + html.TH("Base64")))

Можете трактовать приведенный выше код как добавление к элементу таблицы элемента заголовка таблицы, содержащего элемент строки таблицы, состоящий из двух смежных элементов ячеек данных таблицы. Он отображается в браузере как следующий HTML-код:

<table>
<thead><tr><th>Text</th><th>Base64</th></tr></thead>
</table>

HTML-код показывает вложенную структуру для строки заголовка элемента таблицы. Вот более читаемый формат того же кода:

<table>
  <thead>
    <tr>
      <th>Text</th>
      <th>Base64</th>
    </tr>
  </thead>
</table>

Чтобы увидеть результат в консоли Brython, введите следующий код:

>>> from browser import html
>>> table = html.TABLE()
>>> table <= html.THEAD(html.TR(html.TH("Text") + html.TH("Base64")))
>>> table.outerHTML
'<table><thead><tr><th>Text</th><th>Base64</th></tr></thead></table>'

Для выполнения полного кода нужно запустить веб-сервер. Как и прежде, вы запускаете встроенный веб-сервер Python в том же каталоге, что и два файла index.html и main.py:

$ python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...

После запуска веб-сервера перейдите в браузере на http://localhost:8000. Страница выглядит так:

Калькулятор формы Base64
Калькулятор формы Base64

Вы расширите этот пример в разделе Web API браузера, разрешив сохранение данных между перезагрузками страницы.

Импорт в Brython

Вы можете использовать import для доступа к модулям Python или модулям Brython, скомпилированным в JavaScript.

Модули Python — это файлы с расширением .py в корневой папке вашего проекта либо, для пакета Python, в подпапке, содержащей файл __init__.py. Чтобы импортировать модули Python в ваш код Brython, нужно запустить веб-сервер. Для получения дополнительной информации о модулях Python ознакомьтесь с руководством Python Modules and Packages – An Introduction.

Чтобы изучить, как импортировать модули Python в код Brython, следуйте инструкциям, описанным в разделе Установка PyPI, создайте и активируйте виртуальную среду Python, установите Brython и измените его index.html следующим образом:

<!doctype html>
<html>

<head>
<meta charset="utf-8">
<script type="text/javascript" src="brython.js"></script>
<script type="text/javascript" src="brython_stdlib.js"></script>
</head>

<body onload="brython()">

<script type="text/python">
from browser import document, html, window
import sys
import functional

selection = functional.take(10, range(10000))
numbers = ', '.join([str(x) for x in selection])

document <= html.P(f"{sys.version=}")
document <= html.P(f"{numbers=}")
</script>

</body>

</html>

HTML-файл выше отображает модули, импортированные из ядра движка (browser), из стандартной библиотеки (sys) и из локального модуля Python (functional). Вот содержимое functional.py:

import itertools

def take(n, iterable):
    "Return first n items of the iterable as a list"
    return list(itertools.islice(iterable, n))

Этот модуль реализует take(), одну из утилит itertools. take() возвращает первые n элементов заданного итерируемого объекта. Он опирается на itertools.slice().

Если вы попытаетесь открыть файл index.html из файловой системы с помощью браузера, то в консоли браузера вы увидите следующую ошибку:

Traceback (most recent call last):
  File file:///Users/andre/brython/code/import/index.html/__main__
  line 3, in 
    import functional
ModuleNotFoundError: functional

Импорт модуля Python требует запуска локального веб-сервера. Запустите локальный веб-сервер и укажите в браузере http://localhost:8000. Вы должны увидеть следующую HTML-страницу:

Импорт Python
Импорт Python

При работающем веб-сервере браузер загружает модуль functional.py и затем выполняется import functional. Результаты обоих значений sys.version и numbers вставляются в HTML-файл последними двумя строками встроенного скрипта Python и отображаются браузером.

Уменьшение размера импорта

В каталоге проекта предыдущего примера для уменьшения размера импортируемых модулей JavaScript и предварительной компиляции модулей Python в JavaScript можно использовать brython-cli с опцией make_modules:

$ brython-cli make_modules
Create brython_modules.js with all the modules used by the application
searching brython_stdlib.js...
finding packages...
script in html index.html

Это сгенерирует brython_modules.js, и вы можете изменить элемент head в index.html следующим образом:

<head>
<meta charset="utf-8">
<script type="text/javascript" src="brython.js"></script>
<script type="text/javascript" src="brython_modules.js"></script>
</head>

В строке 4 исходный скрипт заменен с brython_stdlib.js на brython_modules.js.

Открытие браузером index.html или адреса локального сервера отображает ту же самую HTML-страницу. Обратите внимание на следующие моменты:

  • Вы можете отобразить HTML-страницу в своем браузере, не запуская веб-сервер.

  • Вам не нужно распространять его functional.py, так как код был преобразован в JavaScript и упакован в brython_modules.js.

  • Вам не нужно загружать brython_stdlib.js.

Инструмент командной строки brython-cli make_modules предоставляет решение для удаления ненужного кода из стандартных библиотек и компилирует ваш модуль python в код JavaScript. Это помогает упаковать ваше приложение и приводит к меньшей загрузке ресурсов.

Примечание: Аналогично импорту модуля Python, загрузка модуля Python с элементом script требует запуска веб-сервера. Рассмотрим следующий элемент script:

<script src="main.py" type="text/python"></script>

Когда функция Brython выполняется и загружает содержимое script, указывающее на Python-файл, она пытается выполнить Ajax-вызов, который может быть выполнен только при работающем веб-сервере. Если вы попытаетесь открыть файл из файловой системы, то в консоли JavaScript браузера отобразится ошибка, похожая на следующую:

IOError: can't load external script at file:///project/main.py
(Ajax calls not supported with protocol file:///)

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

Далее..

Комментарии (0)