Deno + Python
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
モチベ
deno から python を使う deno_python に関して、まともな日本語情報がないのでここに記録しておく。
deno v1.25 では deno_python v2.1 は動かず、v2.2 に上げる必要がある。
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
エラー対策その1 in 導入
公式の "Python Installation" にあるように、deno_python の利用には deno がpython310.dll
などのライブラリにアクセスできることが必要。
とりあえず↓を実行してみる
import { python } from "https://deno.land/x/python@0.2.2/mod.ts"
以下のようなメッセージが出る場合は deno がライブラリにアクセスできていない。
Could not find Python library!
Tried searching for these versions:
(略)
However, if your Python distribution is not in search
path, you can set DENO_PYTHON_PATH env variable pointing
to dll/dylib/so file for Python library.
この場合、メッセージにあるように環境変数にDENO_PYTHON_PATH
として dll までのパス(C:\Users\*****\anaconda3\python39.dll
など)を設定する。
Deno.env.get('DENO_PYTHON_PATH')
でこのパスが取得できるようになればOK[1]。
-
実際にはOKとは限らない。Anaconda Prompt から deno を起動して deno_python を利用した場合、
Deno.env.get('DENO_PYTHON_PATH')
でパスが取得できてもこのエラーが出る。 ↩︎
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
エラー対策その2 in 導入
DENO_PYTHON_PATH
設定後、あるいは設定前に(細かい部分は違うかもしれないが)以下のような書式のエラーが出る場合がある。
Python path configuration:
PYTHONHOME = (not set)
PYTHONPATH = (not set)
program name = 'python'
isolated = 0
environment = 1
user site = 1
(略)
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'
これは python 側のエラーで、PYTHONHOME
とPYTHONPATH
が未設定であることが原因。
↓の回答にあるように、環境変数PYTHONHOME
としてpython.exe
があるディレクトリ(C:\Users\*****\anaconda3
など)を、PYTHONPATH
としてsite-packages
のディレクトリ(C:\Users\*****\anaconda3\Lib\site-packages
など)を設定すればOK。
ざっと検索したところ、python 自体はこの情報をほぼ使わないので未設定でも問題ないが、外部?から python を使おうとすると問題になる要素らしい
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
エラー対策 in 利用
公式の例にあるように、python の各ライブラリは python.import()
で読み込んで利用する。
注意点として、書式が似ているからと言ってimport {} from ...
を使ってはいけない。
// python: from PIL import Image, ImageOps
const { Image, ImageOps } = python.import("PIL")
const img = Image.open("image.png")
// → Uncaught TypeError: Cannot read properties of undefined (reading 'open')
公式の例(python.import("matplotlib.pyplot")
)が示すように、ファイルを指定する形式で読み込みを行う必要がある。
// python: from PIL import Image, ImageDraw, ImageOps
const Image = python.import("PIL.Image")
const ImageDraw = python.import("PIL.ImageDraw")
const img = Image.open("image.png")
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
ハマりがちなポイントその1
↓の場合、Image
が undefined になるだけでエラーにはならないので、気づきにくい。
const { Image, ImageOps } = python.import("PIL")
console.log()
を使うと、明示的に読み込み結果を確認できる。
const Image = python.import("PIL.Image")
console.log(Image)
// <module 'PIL.Image' from 'C:\\Users\\*****\\anaconda3\\Lib\\site-packages\\PIL\\Image.py'>
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
ハマりがちなポイントその2
これは Anaconda Prompt を利用している場合のみの現象かもしれない。
import だけに限らないが、python の関数の実行中にエラーが発生した場合、エラー処理(≒エラー表示)に移行せずそのままスクリプト実行が強制終了することがある。
例えば、読み込むファイル名を間違えている以下のスクリプトにおいて、deno run -A --unstable deno_py.ts
を実行すると、コンソールには何も表示されないままスクリプト実行が終了する。
const Image = python.import("PIL.image") // 正しくは "PIL.Image"
console.log(Image)
厄介なのが エラーになった時点で強制終了される点で、Error が throw すらされないのでtry {} catch {}
を使っても失敗箇所を特定することができない。そのため、一文ごとに消したりconsole.log()
を挟んだりして地道に調べることが必要になって辛い。
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
その他情報
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
python
から使える機能
- 文字列:
python.str( {name: "Bob"} ) → "{'name': 'Bob'}"
- タプル:
python.tuple( [0,1,2] ) → (0, 1, 2)
、配列を渡すことが必要 - ブーリアン:
- 辞書:
- Ellipsis (...):
- 整数:
- 浮動小数点数:
- リスト:
- None:
- set:
- ビルトイン関数:
const { print } = python.builtins
orpython.builtins.print("PIYO")
python のドキュメントに記載があるもの全てを呼び出すことができる[1] - kw:python 関数にキーワード付き引数を与えるためのヘルパー (詳細は以下に)
-
もちろん python39.dll にアクセスしている場合は 3.10 の関数は呼び出せない ↩︎
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
python.kw()
kw の後ろにname=${value}
の書式で記述すると、関数にname=value
として変数が渡される。
// python: Image.save(image, format="PNG")
// ↓↑
Image.save(image, python.kw`format=${"PNG"}`)
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
その他注意点
![nikogoli](https://res.cloudinary.com/zenn/image/fetch/s--_v5xyIxs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_70/https://storage.googleapis.com/zenn-user-upload/avatar/9abd4fc4b7.jpeg)
バイナリシーケンス型
組み込み型において、バイナリシーケンス型 (bytes
)はサポートされていない。
- そのため、バイトオブジェクトを作ることはできない。が、array を読み込んで
array.array()
を利用することで、 bytes-like object を作ることはできる - バイトオブジェクトそのものはサポートされている。なので、python 関数が返してくる
bytes
は普通にその機能を利用することができる。