10  Brython

11 Brython 예제

이 예제 모음은 Real Python 튜토리얼 Brython: 브라우저 속의 파이썬을 보충하기 위한 것입니다.

대부분의 예제는 기사에서 찾을 수 있으며, 일부는 기사 범위에 맞지 않아 추가된 내용입니다.

11.1 async

  • Brython 비동기 함수를 사용하여 서버 API에서 데이터를 요청하는 방법을 보여주는 프로젝트
  • 다음 예제에서 API는 “Real Python”이라는 텍스트를 반환하는 간단한 텍스트 파일입니다.
  • 예제를 테스트하려면 각 디렉토리에서 로컬 웹 서버를 실행하십시오.
  • 권장되는 Python 개발 서버는 다음 명령으로 시작합니다: python -m http.server
  • https://realpython.com/brython-python-in-browser/#applying-asynchronous-development-in-brython

11.1.1 aio

  • Brython에서 asyncio를 대체하는 browser.aio의 사용법을 보여줍니다.
  • 이 예제는 튜토리얼에 있습니다: https://realpython.com/brython-python-in-browser/#async-io-in-brython
  • https://www.brython.info/static_doc/en/aio.html

11.1.2 ajax

  • browser.ajax의 사용법을 보여줍니다.
  • 이 예제는 튜토리얼에 있습니다: https://realpython.com/brython-python-in-browser/#ajax-in-brython
  • https://www.brython.info/static_doc/en/ajax.html

11.1.3 fetch

  • Brython에서 JavaScript fetch를 사용하는 방법을 보여줍니다.
  • 이 예제는 튜토리얼에 없으며 추가로 제공됩니다. 이 섹션과 관련이 있습니다: https://realpython.com/brython-python-in-browser/#applying-asynchronous-development-in-brython

11.2 Base64

문자열을 입력으로 받아 문자열의 Base64 인코딩 값을 생성하고 페이지에 표시하는 앱부터 시작하여 DOM API에 액세스하는 방법을 보여주는 예제입니다. 각 예제는 다음 섹션에 설명된 대로 약간 다릅니다.

11.2.1 embed

  • 이 애플리케이션은 Python 코드가 내장된 단일 index.htm입니다. 인터넷 브라우저로 파일을 열어 실행할 수 있습니다. 로컬 웹 서버를 시작할 필요가 없습니다.
  • 사용자는 브라우저의 표준 프롬프트 메시지 상자를 통해 인코딩할 문자열을 입력합니다.
  • 이 예제는 다음 예제와 동일하지만 Python 코드가 HTML에 내장되어 있습니다.
  • 이 형식으로는 튜토리얼에 없지만 https://realpython.com/brython-python-in-browser/#the-dom-api-in-brython 와 관련이 있습니다.

11.2.2 sep

  • 이 프로젝트는 embed와 동일하지만 Python 코드가 별도의 파일인 main.py에 있습니다.
  • 이 예제를 테스트하려면 별도의 Python 파일이 필요하므로 로컬 서버를 시작해야 합니다 (python3 -m http.server).

11.2.3 form

  • 이 애플리케이션은 Python 코드가 별도의 main.py 파일에 있는 index.html입니다. 로컬 웹 서버를 시작해야 합니다 (python3 -m http.server).
  • 사용자는 메인 페이지의 HTML 양식에 문자열을 입력합니다.
  • 이 예제는 튜토리얼에서 찾을 수 있습니다: https://realpython.com/brython-python-in-browser/#the-dom-api-in-brython

11.2.4 storage

  • 이 예제는 localstorage를 사용하고 페이지 새로고침 사이에 데이터를 저장하는 방법을 보여주는 form 프로젝트의 확장입니다. 로컬 웹 서버를 시작해야 합니다 (python3 -m http.server).
  • 데이터는 로컬 저장소의 단일 키와 연결된 JSON 문서로 저장됩니다. JSON 파일에 더 많은 요소를 추가하면 성능이 저하됩니다.
  • 이 예제는 튜토리얼에 설명되어 있습니다: https://realpython.com/brython-python-in-browser/#browser-web-api

11.2.5 storage_perf

  • storage 예제의 성능 문제를 극복하기 위해 이 예제는 각 base64 인코딩된 값을 별도의 키에 저장합니다. 키는 사용자가 입력한 원래 문자열입니다.
  • 이 예제는 추가 사항이며 튜토리얼에 설명되어 있지 않지만 https://realpython.com/brython-python-in-browser/#browser-web-api 와 관련이 있습니다.

11.3 chrome_extensions

11.3.1 hello_js

  • JavaScript Google Chrome 확장 프로그램 예제
  • 튜토리얼: https://realpython.com/brython-python-in-browser/#hello-world-extension-in-js

11.3.2 hello_py

  • Brython을 사용하는 hello_js와 동일한 예제
  • 튜토리얼: https://realpython.com/brython-python-in-browser/#hello-world-extension-in-python

11.4 console

  • HTML 파일에 내장된 iframe으로서의 Brython 콘솔. 로컬 웹 서버를 실행할 필요가 없습니다. 브라우저로 index.html을 여는 것만으로 테스트할 수 있습니다.
  • 이것은 튜토리얼에 설명되지 않은 추가 사항입니다.
  • 온라인에서 보려면 https://brython.info/console.html 을 확인하십시오.

11.5 github_install

  • GitHub에서 Brython 엔진을 로드하는 index.html. 파일을 직접 열 수 있습니다. “Hello Real Python”이라는 메시지 상자만 표시됩니다.
  • 튜토리얼: https://realpython.com/brython-python-in-browser/#github-installation

11.6 hashes

  • Base64 인코딩 애플리케이션과 마찬가지로, 이 애플리케이션은 문자열의 해시(SHA-1, SHA-256 또는 SHA-512)를 생성합니다. 원하는 알고리즘(SHA-1, SHA-256 또는 SHA-512)을 선택하기 위한 드롭다운을 추가합니다.
  • 이것은 Vue.js를 사용한 동일한 애플리케이션으로의 번역을 위한 기반이 됩니다 (아래 vuejs 프로젝트 참조).
  • 로컬 웹 서버가 필요합니다.
  • 이것은 튜토리얼에 설명되지 않은 추가 사항이지만 Vue.js 예제의 기반이 되며 https://realpython.com/brython-python-in-browser/#web-ui-framework 에서 읽을 수 있습니다.

11.7 import

  • 외부 Python 모듈을 가져오는 방법을 보여줍니다. 외부 모듈은 functional.py입니다. 기본 Python 코드는 HTML 페이지에 내장되어 있습니다.
  • 로컬 웹 서버가 필요합니다.
  • 튜토리얼에서 읽어보십시오: https://realpython.com/brython-python-in-browser/#import-in-brython

11.8 import_js

  • brython-cli --modules로 생성된 brython_module.js를 생성할 수 있도록 하여 import 예제를 확장합니다.
  • 이를 위해서는 PATH에서 brython-cli를 사용할 수 있도록 Brython이 설치된 Python 가상 환경이 필요합니다 (pip install brython). 생성된 파일은 dist_example 하위 디렉토리에서 사용할 수 있습니다.
  • dist_example/index.html을 브라우저로 열 수 있으며, 종속성이 JS 파일(brython.jsbrython_modules.js)뿐이므로 로컬에서 웹 서버를 실행할 필요가 없습니다.
  • 이 접근 방식에 대한 자세한 내용은 https://realpython.com/brython-python-in-browser/#reduce-import-size 에서 튜토리얼을 참조하십시오.

11.9 npm_install

  • npm으로 설치된 Brython 프로젝트 예제. 자세한 내용은 해당 튜토리얼 섹션을 참조하십시오.
  • 튜토리얼 섹션: https://realpython.com/brython-python-in-browser/#npm-install

11.10 pip_install

  • pip으로 설치된 Brython 프로젝트 예제.
  • 튜토리얼 섹션: https://realpython.com/brython-python-in-browser/#pypi-install

11.11 sha256

  • 주어진 문자열의 SHA-256 해시를 생성하는 애플리케이션. 데이터는 페이지 새로고침 간에 계산을 보존하기 위해 로컬 저장소의 키에 JSON으로 저장됩니다.
  • 이것은 튜토리얼에 설명되지 않은 추가 사항이지만 Vue.js 예제의 기반이 됩니다. https://realpython.com/brython-python-in-browser/#web-ui-framework 에서 읽을 수 있습니다.

11.12 vuejs

  • hashes의 Brython 및 Vue.js 구현. 실행하려면 로컬 웹 서버가 필요합니다.
  • Vue.js 예제는 https://realpython.com/brython-python-in-browser/#web-ui-framework 에 설명되어 있습니다.

11.13 wasm

  • WASM 파일 생성, 파일 로드 및 Brython에서 함수 사용을 보여주는 예제. WASM 파일의 소스 코드는 Rust입니다.
  • 이를 위해서는 로컬 머신에 Rust 컴파일러가 설치되어 있어야 합니다. Brython 튜토리얼에서 자세한 내용을 확인하십시오. wasm 파일을 로드해야 하므로 로컬 웹 서버가 필요합니다.
  • 웹 서버는 wasm/op/web 디렉토리에서 시작할 수 있습니다. 디버그 wasm 파일이 포함되어 있습니다. 이것은 데모용입니다. add 함수는 음수 및 큰 정수를 처리하지 않습니다.
  • 튜토리얼에 설명되어 있습니다: https://realpython.com/brython-python-in-browser/#webassembly

11.14 zero_install

  • 최소한의 Brython 프로젝트를 보여주는 예제. Brython 엔진은 CDN에서 가져오고 Python 코드는 페이지에 내장됩니다. 로컬 웹 서버도, 로컬 Python 환경도 필요 없으며 JavaScript가 활성화된 브라우저만 있으면 됩니다 :-)
  • 튜토리얼: https://realpython.com/brython-python-in-browser/#cdn-server-install

11.15 파일: async/aio/main.py

from browser import aio, document


def log(message):
    document["log"].value += f"{message} \n"


async def process_get(url):
    log("Before await aio.get")
    req = await aio.get(url)
    log(f"Retrieved data: '{req.data}'")


def aio_get(evt):
    log("Before aio.run")
    aio.run(process_get("/api.txt"))
    log("After aio.run")


document["get-btn"].bind("click", aio_get)

11.16 파일: async/ajax/main.py

from browser import ajax, document


def show_text(req):
    if req.status == 200:
        log(f"Text received: '{req.text}'")
    else:
        log(f"Error: {req.status} - {req.text}")


def log(message):
    document["log"].value += f"{message} \n"


def ajax_get(evt):
    log("Before async get")
    ajax.get("/api.txt", oncomplete=show_text)
    log("After async get")


def ajax_get_blocking(evt):
    log("Before blocking get")
    try:
        ajax.get("/api.txt", blocking=True, oncomplete=show_text)
    except Exception as exc:
        log(f"Error: {exc.__name__}")
        log("Did you start a web server (ex: 'python3 -m http.server')?")
    else:
        log("After blocking get")


document["get-btn"].bind("click", ajax_get)
document["get-blocking-btn"].bind("click", ajax_get_blocking)

11.17 파일: base64/form/main.py

import base64

from browser import alert, document, html

b64_map = {}


def base64_compute(evt):
    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(evt):
    b64_map.clear()
    document["b64-display"].clear()


def display_map():
    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)

11.18 파일: base64/sep/main.py

import base64

from browser import document, html, prompt  # type: ignore

b64_map = {}


def base64_compute(_) -> None:
    default = "Real Python"
    data = prompt("Enter a string:", default)
    data = data or default
    b64data = base64.b64encode(data.encode()).decode()
    b64_map[data] = b64data
    display_map()


def display_map() -> None:
    table = html.TABLE(style={"border": "1 solid grey"})
    table <= 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["b64-btn"].bind("click", base64_compute)

11.19 파일: base64/storage/main.py

import base64
import json

from browser import alert, document, html
from browser.local_storage import storage


def load_data():
    data = storage.get("b64data")
    if data:
        b64_map = json.loads(data)
    else:
        storage["b64data"] = json.dumps({})
        b64_map = {}
    return b64_map


def base64_compute(evt):
    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
    storage["b64data"] = json.dumps(b64_map)
    display_map()


def clear_map(evt):
    b64_map.clear()
    storage["b64data"] = json.dumps({})
    document["b64-display"].clear()


def display_map():
    if not b64_map:
        return
    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 = ""


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

11.20 파일: base64/storage_perf/main.py

import base64

from browser import alert, document, html
from browser.local_storage import storage


def load_data():
    b64_map = {}
    for key in storage.keys():
        b64_map[key] = storage.get(key)
    return b64_map


def save_data(key, value):
    storage[key] = value


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


def clear_map(evt):
    b64_map.clear()
    storage.clear()
    document["b64-display"].clear()


def display_map():
    if not b64_map:
        return
    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 = ""


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

11.21 파일: chrome_extensions/hello_py/popup.py

from browser import document, prompt


def hello(evt):
    default = "Real Python"
    name = prompt("Enter your name:", default)
    if not name:
        name = default
    document["hello"].innerHTML = f"Hello, {name}!"


document["hello-btn"].bind("click", hello)

11.22 파일: hashes/main.py

import hashlib

from browser import alert, document, html

hashes = {
    "sha-1": hashlib.sha1,
    "sha-256": hashlib.sha256,
    "sha-512": hashlib.sha512,
}


def compute_hash(evt):
    value = document["text-src"].value
    if not value:
        alert("You need to enter a value")
        return
    algo_dropdown = document["algo"]
    algo = algo_dropdown.options[algo_dropdown.selectedIndex].value
    hash_object = hashes[algo]()
    hash_object.update(value.encode())
    hex_value = hash_object.hexdigest()
    display_hash(hex_value)


def display_hash(hex_value):
    text = html.P(hex_value)
    hash_display = document["hash-display"]
    hash_display.clear()
    hash_display <= text


document["submit"].bind("click", compute_hash)

11.23 파일: import/functional.py

# main.py

import itertools


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

11.24 파일: import_js/functional.py

# functional.py

import itertools


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

11.25 파일: sha256/main.py

import hashlib
import json

from browser import alert, document, html
from browser.local_storage import storage

LOCAL_STORAGE = "hashdata"


def load_data():
    data = storage.get(LOCAL_STORAGE)
    if data:
        hash_map = json.loads(data)
    else:
        storage[LOCAL_STORAGE] = json.dumps({})
        hash_map = {}
    return hash_map


def compute(evt):
    value = document["text-src"].value
    if not value:
        alert("You need to enter a value")
        return
    if value in hash_map:
        alert(
            f"The SHA-256 value of '{value}' already exists: '{hash_map[value]}'"
        )
        return
    hash = hashlib.sha256()
    hash.update(value.encode())
    hash_hex = hash.hexdigest()
    hash_map[value] = hash_hex
    storage[LOCAL_STORAGE] = json.dumps(hash_map)
    display_map()


def clear_map(evt):
    hash_map.clear()
    storage[LOCAL_STORAGE] = json.dumps({})
    document["hash-display"].clear()


def display_map():
    if not hash_map:
        return
    table = html.TABLE(Class="pure-table")
    table <= html.THEAD(html.TR(html.TH("Text") + html.TH("SHA-256")))
    table <= (
        html.TR(html.TD(key) + html.TD(hash_map[key])) for key in hash_map
    )
    hash_display = document["hash-display"]
    hash_display.clear()
    hash_display <= table
    document["text-src"].value = ""


hash_map = load_data()
display_map()
document["submit"].bind("click", compute)
document["clear-btn"].bind("click", clear_map)

11.26 파일: vuejs/main.py

import hashlib

from browser import alert, window
from javascript import this

hashes = {
    "sha-1": hashlib.sha1,
    "sha-256": hashlib.sha256,
    "sha-512": hashlib.sha512,
}

Vue = window.Vue


def compute_hash(evt):
    value = this().input_text
    if not value:
        alert("You need to enter a value")
        return
    hash_object = hashes[this().algo]()
    hash_object.update(value.encode())
    hex_value = hash_object.hexdigest()
    this().hash_value = hex_value


def created():
    for name in hashes:
        this().algos.append(name)
    this().algo = next(iter(hashes))


app = Vue.createApp(
    {
        "el": "#app",
        "created": created,
        "data": lambda _: {
            "hash_value": "",
            "algos": [],
            "algo": "",
            "input_text": "",
        },
        "methods": {"compute_hash": compute_hash},
    }
)

app.mount("#app")

11.27 파일: wasm/op/web/main.py

from browser import document, window

double_first_and_add = None


def add_rust_fn(module):
    global double_first_and_add
    double_first_and_add = module.instance.exports.double_first_and_add


def add_numbers(evt):
    nb1 = document["number-1"].value or 0
    nb2 = document["number-2"].value or 0
    res = double_first_and_add(nb1, nb2)
    document["result"].innerHTML = f"Result: ({nb1} * 2) + {nb2} = {res}"


document["submit"].bind("click", add_numbers)
window.WebAssembly.instantiateStreaming(window.fetch("op.wasm")).then(
    add_rust_fn
)