Pythonプロジェクトの作成(pyenv + Poetry)

バージョン情報 pyenv 2.4.0 Poetry 1.8.2 Python 3.11.9 定義・ディレクトリ構成 説明のため、プロジェクトディレクトリ名my_project、パッケージ名my-project、主要なモジュール名my_projectとします。 以下のようなディレクトリ構成にすることを想定しています。 - my_project/ - pyproject.toml - Dockerfile - my_project/ - __init__.py - __main__.py - cli.py - tests/ - __init__.py - test_my_project.py Python/Poetryのインストール pyenvでPythonをインストールします。 記事作成時点で最新のリビジョン(0.0.x)を記載していますが、適宜新しいバージョンが出ているか確認し、 更新してください。 マイナーバージョン(0.x.0)を変更する場合、依存する予定のライブラリが動作するかなど、プロジェクトの要件と相談してください。 env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.11.9 PYTHON_CONFIGURE_OPTS="--enable-shared"は、PyInstallerが動作するようにするために設定しています。 pyenv and PyInstaller — PyInstaller 6.5.0 documentation Poetryをインストールします。 Poetry Installation # Linux, macOS, WSL curl -sSL https://install.python-poetry.org | python3 - # Windows (PowerShell) (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python - Poetryプロジェクトの作成 Poetryのグローバル設定を変更し、Python仮想環境がプロジェクトのディレクトリ/.venvに作成されるようにします。 これは、VSCode拡張機能のPylanceがPython仮想環境を認識できるようにする、または手動で設定しやすくするための変更です。 ...

2023年8月7日 · aoirint

pyenvでPyInstallerを使えるようにする

env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.11.3 https://pyinstaller.org/en/stable/development/venv.html

2023年4月16日 · aoirint

Python, asyncioを使った同期・非同期処理の実装例

Python 3.9.13 (pyenv) Ubuntu 20.04 (WSL2) https://github.com/aoirint/python_asyncio_examples なんかこう書くとうまく動くということしかわからん、という実装例です。 https://twitter.com/aoirint/status/1544891079786627074 基本実装 同期関数から非同期関数を同期的に呼び出す asyncio.run Details import asyncio import time def main(): async def func(): await asyncio.sleep(3) print('func exited') # 1 asyncio.run(func()) time.sleep(1) print('main exited') # 2 main() print('exited') # 3 同期関数から非同期関数を非同期的に呼び出す threading.Thread + asyncio.run Details import asyncio from concurrent.futures import ThreadPoolExecutor import threading import time def main(): async def func(): await asyncio.sleep(3) print('func exited') # 3 thread = threading.Thread(target=lambda: asyncio.run(func())) thread.start() time.sleep(1) print('main exited') # 1 main() print('exited') # 2 非同期関数から同期関数を同期的に呼び出す ふつうに呼び出す Details import asyncio import time async def main(): def func(): time.sleep(3) print('func exited') # 1 func() await asyncio.sleep(1) print('main exited') # 2 asyncio.run(main()) print('exited') # 3 非同期関数から同期関数を非同期的に呼び出す asyncio.new_event_loop + ThreadPoolExecutor + EventLoop.run_in_executor Details import asyncio from concurrent.futures import ThreadPoolExecutor import time async def main(): def func(): time.sleep(3) print('func exited') # 3 loop = asyncio.new_event_loop() executor = ThreadPoolExecutor() loop.run_in_executor(executor, func) await asyncio.sleep(1) print('main exited') # 1 asyncio.run(main()) print('exited') # 2 非同期関数から非同期関数を同期的に呼び出す awaitキーワード Details import asyncio async def main(): async def func(): await asyncio.sleep(3) print('func exited') # 1 await func() await asyncio.sleep(1) print('main exited') # 2 asyncio.run(main()) print('exited') # 3 非同期関数から非同期関数を非同期的に呼び出す threading.Thread + asyncio.run Details import asyncio import threading async def main(): async def func(): await asyncio.sleep(3) print('func exited') # 3 thread = threading.Thread(target=lambda: asyncio.run(func())) thread.start() await asyncio.sleep(1) print('main exited') # 1 asyncio.run(main()) print('exited') # 2 非同期関数から同期間数を非同期的に3つずつ呼び出す asyncio.new_event_loop + ThreadPoolExecutor + EventLoop.run_in_executor Details import asyncio from concurrent.futures import ThreadPoolExecutor import time async def main(): def func(): time.sleep(3) print('func exited') # 3 loop = asyncio.new_event_loop() executor = ThreadPoolExecutor(max_workers=3) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) loop.run_in_executor(executor, func) await asyncio.sleep(1) print('main exited') # 1 asyncio.run(main()) print('exited') # 2 よく見るエラー RuntimeError: Event loop is closed TBW ...

2022年7月7日 · aoirint

Django 3.xから4.xへの更新でPOSTリクエスト時にCSRF検証に失敗する

想定: 3.xで動いていたフォーム送信が4.xへの更新でCSRF検証に失敗するために動かなくなった settings.pyにCSRF_TRUSTED_ORIGINSを追加すればよい。 https://docs.djangoproject.com/en/4.0/ref/settings/#std-setting-CSRF_TRUSTED_ORIGINS Origin: e.g. https://example.com https://developer.mozilla.org/ja/docs/Glossary/Origin CSRF検証のドキュメントを3.xと比べると、以下の記述が増えている。 CsrfViewMiddleware verifies the Origin header, if provided by the browser, against the current host and the CSRF_TRUSTED_ORIGINS setting. This provides protection against cross-subdomain attacks. 4.x: https://docs.djangoproject.com/en/4.0/ref/csrf/#how-it-works 3.x: https://docs.djangoproject.com/en/3.2/ref/csrf/#how-it-works

2022年6月24日 · aoirint

pip-compileでMetadataGenerationFailedの例外が起きる

pip-compileは、pip-toolsパッケージ(jazzband/pip-tools)に含まれるコマンドで、 Pythonパッケージのリストrequirements.inから、 依存ツリーのライブラリバージョンリストrequirements.txtを出力してくれる便利なツールである。 入力例 requirements.in django requests gunicorn mysqlclient python-dateutil requests-oauthlib schedule pip-compile requirements.in 出力例 requirements.txt # # This file is autogenerated by pip-compile with python 3.8 # To update, run: # # pip-compile requirements.in # asgiref==3.5.2 # via django backports-zoneinfo==0.2.1 # via django certifi==2022.6.15 # via requests charset-normalizer==2.0.12 # via requests django==4.0.5 # via -r requirements.in gunicorn==20.1.0 # via -r requirements.in idna==3.3 # via requests mysqlclient==2.1.1 # via -r requirements.in oauthlib==3.2.0 # via requests-oauthlib python-dateutil==2.8.2 # via -r requirements.in requests==2.28.0 # via # -r requirements.in # requests-oauthlib requests-oauthlib==1.3.1 # via -r requirements.in schedule==1.1.0 # via -r requirements.in six==1.16.0 # via python-dateutil sqlparse==0.4.2 # via django urllib3==1.26.9 # via requests # The following packages are considered to be unsafe in a requirements file: # setuptools https://pypi.org/project/pip-tools/ (現在 pip-tools==6.6.2) https://github.com/jazzband/pip-tools ところでPythonパッケージは、システムパッケージの事前インストールを要求することがある。 以下、システムパッケージ名はDebian/Ubuntuを想定する。 ...

2022年6月24日 · aoirint

Python subprcess stdout, stderrをキャプチャする

threading https://ja.stackoverflow.com/questions/60539/subprocess-popenのstdoutとstderrをリアルタイムに取得する # python 3.9 import subprocess from threading import Thread def main(): command = [ 'mycommand', '-opt1', ] proc = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) def read_stdout(stdout): for line in stdout: line_text = line.decode('utf-8').strip() print(f'STDOUT: {line_text}', flush=True) print('stdout closed') # closed when process exited def read_stderr(stderr): for line in stderr: line_text = line.decode('utf-8').strip() print(f'STDERR: {line_text}', flush=True) print('stderr closed') # closed when process exited Thread(target=read_stdout, args=(proc.stdout,)).start() Thread(target=read_stderr, args=(proc.stderr,)).start() while True: returncode = proc.poll() if returncode is not None: break time.sleep(0.01) print(f'exited {returncode}') asyncio https://stackoverflow.com/questions/28492103/how-to-combine-python-asyncio-with-threads https://qiita.com/matsui-k20xx/items/4d1c00c4eefd60ba635b import time import asyncio from concurrent.futures import ThreadPoolExecutor async def main(): loop = asyncio.get_event_loop() executor = ThreadPoolExecutor(2) def operation(): time.sleep(1) print('aaa', flush=True) # 2個ずつ実行される(asyncio.runの実行終了まで2秒かかる) loop.run_in_executor(executor, operation) loop.run_in_executor(executor, operation) loop.run_in_executor(executor, operation) loop.run_in_executor(executor, operation) # main関数の実行はブロックされない print('before aaa') asyncio.run(main()) import time import asyncio from asyncio.subprocess import create_subprocess_exec, PIPE from concurrent.futures import ThreadPoolExecutor async def main(): command = [ 'mycommand', '-opt1', ] proc = await create_subprocess_exec( command[0], *command[1:], stdout=PIPE, stderr=PIPE, ) loop = asyncio.get_event_loop() executor = ThreadPoolExecutor() def read_stdout(stdout): while True: line = asyncio.run_coroutine_threadsafe(stdout.readline(), loop).result() if not line: break line_text = line.decode('utf-8').strip() print(f'STDOUT: {line_text}', flush=True) print('stdout closed') # closed when process exited def read_stderr(stderr): while True: line = asyncio.run_coroutine_threadsafe(stderr.readline(), loop).result() if not line: break line_text = line.decode('utf-8').strip() print(f'STDERR: {line_text}', flush=True) print('stderr closed') # closed when process exited loop.run_in_executor(executor, read_stdout, proc.stdout) loop.run_in_executor(executor, read_stderr, proc.stderr) await proc.wait() print(f'exited {proc.returncode}') https://stackoverflow.com/questions/45600579/asyncio-event-loop-is-closed-when-getting-loop 終了時にEvent loop is closedというエラーが出ることがある? ...

2022年6月6日 · aoirint

Pythonパッケージ中でパッケージに同梱したファイルを読み込む

Python 3.7以降の場合、標準モジュールimportlib.resourcesが利用できる。 https://docs.python.org/ja/3/library/importlib.html#module-importlib.resources 以下のようなディレクトリ構造でファイルを同梱する。 setup.py MANIFEST.in README.md LICENSE mymodule __init__.py mymodule.py mydirectory __init__.py myfile1.txt myfile2.bin 注意点 読み込むファイルのあるディレクトリに__init__.pyを作成する MANIFEST.inでファイルがパッケージに同梱されるように記述する include README.md include LICENSE recursive-include mymodule/mydirectory * import importlib.resources as ILR # テキストファイル ILR.read_text('mymodule.mydirectory', 'myfile1.txt', encoding='utf-8') # -> str # バイナリファイル ILR.read_binary('mymodule.mydirectory', 'myfile2.bin') # -> bytes

2022年5月27日 · aoirint

Python, Basic認証情報の入ったURLをURLと認証情報に分けるために書いたけどrequestsがもとからこの機能持ってたのでいらなくなったスニペット

https://gist.github.com/aoirint/ddbc49898d39cc0faa15765db8aa247a

2022年5月22日 · aoirint

Ubuntu, pyenv環境でtkinterを使う

sudo apt install tk-dev python-tk python3-tk pyenv install 3.8.6 pyenv global 3.8.6 python3 -m tkinter

2020年10月4日 · aoirint

Python Requests

ses = requests.Session() Headers ses.headers.update({ }) Copy cookies from Selenium ses.cookies.clear() for cookie in driver.get_cookies(): rc = requests.cookies.create_cookie(domain=cookie['domain'], name=cookie['name'], value=cookie['value']) ses.cookies.set_cookie(rc) Download a file file_url: str dest_path: str with tempfile.NamedTemporaryFile() as fp: with ses.get(file_url, stream=True) as r: r.raise_for_status() for chunk in r.iter_content(chunk_size=8192): fp.write(chunk) fp.flush() shutil.copy(fp.name, dest_path)

2020年9月28日 · aoirint