OPS

新技術 PyScriptを解説! jQueryとの違い、使ってみた感想など

2022.06.16

本記事のポイント

現代のWebアプリのフロントエンド開発において、JavaScriptやそのライブラリであるjQueryの存在は無視できません。フロントエンドのスクリプト言語として、絶大な地位を確立しています。しかし、私の願望が多分に入っていますが、もしかしたらその状況が変わるかもしれません。

2022年5月にAnaconda社よりブラウザでPythonが実行できるPyScript がリリースされました。この記事では、リリースされたばかりのPyScriptについてまとめました。



はじめに

2022年5月にPythonの主要なディストリビューションである「Anaconda」などを提供しているAnaconda社から「PyScript」がオープンソースで公開されました。PyScriptはHTMLにPythonを記述し、実行できます。つまり、ブラウザ上でPythonが実行できるようになるのです。今回は、このPyScriptについて解説します!

記事前半ではPyScriptの実装例を、後半ではjQueryとの比較をお伝えするので、是非じっくりとご覧ください。

PyScript(パイスクリプト)とは?

まず、PyScriptの概要についてお伝えします。

PyScriptとはオープンソースのHTMLの中でPythonを記述し、実行できるライブラリです。Anaconda社により開発されています。PyScriptには以下のような特徴があります。

  • ブラウザでPythonを使うことができる
  • Pythonの多くの一般的なパッケージをそのまま使うことができる
  • PythonとJavaScriptのオブジェクトや名前空間が相互に連携している
  • ボタン、コンテナ、テキストボックスなど、UIコンポーネントが利用可能である
  • 新しいプラグインや拡張可能なコンポーネントを直接Pythonで作成し、共有するために活用できる柔軟なフレームワークである
  • このような特徴から、現在のフロントエンド開発でJavaScript、jQueryが主流な状況に変化が現れる可能性があり、フロントエンドからバックエンドまですべてPythonでWebアプリ開発という選択肢が出てきそうです。

    なお、現在PyScriptはアルファ版であることから、本番環境での利用は非推奨となっていますのでご注意ください。

    参考:PyScript公式サイト

    PyScriptの実例

    今回、PyScriptを実際に利用してみるために、以下の環境でPythonのWebアプリフレームワークであるFlaskを用いて、簡単なWebアプリを作成します。

  • Virtual Box: 6.1
  • AlmaLinux 8.5
  • Python 3.10.0
  • Flask 2.1.1
  • 環境構築

    Flaskを以下のコマンドでインストールします。

    $ pip install Flask
    

    インストールが完了したら、以下のコマンドでバージョンが表示されたら正常にインストールできています。

    $ flask --version
    Python 3.10.0
    Flask 2.1.2
    Werkzeug 2.1.2
    

    公式のチュートリアルに沿ってページの表示させてみます。
    hello.pyを作成し、以下のコードを実装します。

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route("/")
    def hello_world():
        return "<p>Hello, World!</p>"
    

    次に、以下のコマンドによりFlaskを起動させます。(-h オプションはホストの指定、-p オプションはポート番号を指定できます。今回は、Virtual Box上の仮想マシンに環境を構築し、ホストマシンからアクセスするためかつ私の開発環境の都合により設定しています。)

    $ export FLASK_APP=hello
    $ flask run -h 0.0.0.0 -p 8000
    

    ブラウザでhttp://localhost:8000/にアクセスして、以下のページが表示されたら成功です。

    ローカルホストのHello World!

    これで、PyScriptを試してみる環境が構築できたので、さっそく新機能のPyScriptを実際に触っていきましょう。

    同じ機能をPyScriptとjQueryそれぞれで実装、比較

    まずは、簡単なコードから試していきます。sample_pyscript.py以下のような実装をしました。

    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    @app.route('/sample/')
    @app.route('/sample/<name>')
    def sample(name=None):
        return render_template('sample.html', name=name)
    
    

    また、templates/sample.htmlのコードは以下の通りです。

    <!doctype html>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
    <py-script>
    if '{{ name }}':
        name: str = '{{ name }}'
        print(f'Hello {name}!')
    else:
        print('Hello World!')
    </py-script>
    
    

    JavaScriptやjQueryでは<>内にコードを記述しましたが、PyScriptでは<py-script>内にPythonコードを記述します。

    http://localhost:8000/sample/にアクセスすると、以下のように表示されました。

    http://localhost:8000/sample/のHello World!

    また、http://localhost:8000/sample/jigsaw/にアクセスすると、以下のように表示されました。

    http://localhost:8000/sample/jigsaw/のHello jigsaw!

    ブラウザ上でPythonコードが動いています。感動です。
    次に、同一の機能を持つページをPyScriptとjQueryの両方で実装してみます。
    まずは、サーバ側を実装します。さきほどの、sample_pyscript.pyに以下を追加します。

    @app.route('/sample_pyscript/')
    def sample_pyscript():
        return render_template('sample_pyscript.html')
    
    @app.route('/sample_jquery/')
    def sample_jquery():
        return render_template('sample_jquery.html')
    

    入力フォームに入力したMarkdownのプレビューのモーダルを表示させる機能を持ったページを実装します。

    sample_jquery.html、sample_pyscript.htmlのHTMLは共通で以下の通りです。

    <!doctype html>
    <html>
        <body>
            <div class="card">
                <div class="card-header">
                    <b>Test App</b>
                </div>
                <div class="card-body">
                    <div class="row">
                        <div class="col-auto"></div>
                        <div class="col-6">
                            <input type="text" id="text" class="form-control">
                        </div>
                        <div class="col-auto"></div>
                    </div>
                </div>
                <div class="card-footer">
                    <div class="pull-right">
                        <button type="button" id="preview-btn" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#previewModalLabel">Preview</button>
                    </div>
                </div>
            </div>
            <div class="modal fade" id="previewModalLabel" tabindex="-1" aria-labelledby="previewModalLabel" aria-hidden="true">
                <div class="modal-dialog">
                  <div class="modal-content">
                    <div class="modal-header">
                      <h5 class="modal-title" id="previewModalLabel">Preview</h5>
                      <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                    </div>
                    <div id="text-in-modal" class="modal-body"></div>
                    <div class="modal-footer">
                      <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                    </div>
                  </div>
                </div>
              </div>
        </body>
    </html>
    
    

    jQueryでの処理部分は、以下のような実装です。(今回、見栄えの面からBootstrap5を使用しています。)

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
    $('#preview-btn').on('click', function () {
        var text = $('#text').val();
        $('#text-in-modal').text(text);
    });
    </script>
    
    

    「Preview」ボタンをクリックすると、入力フォームから値を取得してプレビューモーダルへ挿入するという処理をおこなっています。

    同様の処理をPyscriptで実装すると、以下の通りです。

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
    <py-script>
    from typing import Any
    
    def show_text(*args: Any, **kws: Any) -> None:
        text: str = Element('text').element.value
        pyscript.write('text-in-modal', text)
    </py-script>
    
    

    2つのページへブラウザからアクセスして操作すると、以下の画像のように、実装できています。

    [jQueryで実装したページ(http://localhost:8000/sample_jquery)]

    jQueryのテストアプリプレビュー表示

    [PyScriptで実装したページ(http://localhost:8000/sample_pyscript)]

    PyScriptのテストアプリプレビュー表示

    さらに拡張して、入力フォームにMarkdownを入力するとモーダルでプレビューが見れるようにします。

    sample_jquery.htmlのコードを以下のように修正します。

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <!-- Markdownのパースにmarked.jsを使用します。 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.16/marked.min.js" integrity="sha512-8d9aScHpB0kf4+i5O3JlEP6VfvvjYYyZXa71ZKq0CzytOfDcH8d4Iej33s/0nNDcqWHhdDuAZKjA2y2qXzvJZw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
    $('#preview-btn').on('click', function () {
        var text = $('#text').val();
        var html = marked.parse(text); // <= 取得した値をパースします
        $('#text-in-modal').html(html);
    });
    </script>
    
    

    sample_pyscript.htmlのコードも以下のように修正します。(ライブラリmarkdownをpipインストールする必要があります。)

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
    <!-- 標準ライブラリ以外は、py-envタグに記述する必要があります。 -->
    <py-env>
        - markdown
    </py-env>
    <py-script>
    from markdown import markdown
    from typing import Any
    
    def show_text(*args: Any, **kws: Any) -> None:
        text: str = Element('text').element.value
        html: str = markdown(text) # <= 取得した値をパースします
        pyscript.write('text-in-modal', html)
    </py-script>
    
    

    それぞれの結果は以下の通りです。

    PyScript PyScriptでテストした時の表示
    jQuery JQueryでテストした時の表示

    PyScriptのほうは、うまくいっていないようです。開発者モードでは、両者に差はないように見えますが、残念ながら原因はわかっていません。

    PyScript PyScriptのコード
    jQuery JQueryのコード

    PyScriptとjQueryの比較

    前章では、同じ機能をjQueryとPyScriptそれぞれで実装しました。この章では、具体的な比較や実装段階での気づいた点などについてまとめていきます。

    HTML要素からの値の取得

    前章で作成したコードでは、HTMLの要素からの値の取得を以下のように実装しています。

    PyScript jQuery
    text: str = Element(‘text’).element.value var text = $(‘#text’).val();

    要素のIDを指定して値を取得していますが、両者で大きな違いはありません。ただし、現在のところ、PyScriptではIDを指定することしかできません。

    HTML要素の値の更新

    こちらについても、両者で大きな違いはありません。またこちらについても、現在のところ、PyScriptではIDを指定することしかできません。

    PyScript jQuery
    pyscript.write(‘text-in-modal’, text) $(‘#text-in-modal’).text(text);

    PyScriptのpyscript.write()では、いかのように引数append=Trueを指定することで、値の更新ではなく、追加を行うことが可能です。

    pyscript.write('text-in-modal', text, append=True)
    

    その他、気づいた点などは以下の通りです。

  • PyScriptはページ表示時の読み込みがjQueryの場合と比較して非常に長い
  • 公式ドキュメントやサンプル、ライブラリが少ない(リリースされて日の浅い機能なので当たり前)
  • 要素を指定する方法がPyScriptではIDしか対応していない
  • HTML要素の追加がjQueryと比べて、PyScriptでは少々煩雑
  • まとめ

    今回は、2022年5月に公開された新機能PyScriptを実際に使ってみて、現在私が開発業務で使用しているjQueryと比較してみました。まだ、PyScriptはアルファ版であるため、いろいろな点がデメリットとしてあります。また、公式としても本番環境での使用を推奨していません。

    しかし、筆者はシンプルで可読性の高く、データ処理や分析のライブラリが充実しているPythonがフロントエンドで使用できるPyScriptに非常に大きな期待をもっています。今後、パフォーマンス改善や機能の追加がおこなわれ、ライブラリも充実していき、PythonのみでWebアプリケーション開発という選択肢ができることを期待しています。