├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ └── check-pull-request.yml ├── .gitignore ├── .mypy.ini ├── .python-version ├── .ruff.toml ├── .vscode ├── extensions.json └── settings.json ├── Makefile ├── README.md ├── notebooks └── sample.ipynb ├── pyproject.toml ├── requirements-dev.lock ├── requirements.lock ├── src └── python_docker │ ├── __init__.py │ └── main.py └── tests └── test_main.py /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/base:jammy 2 | 3 | USER vscode 4 | 5 | RUN curl -sSf https://rye.astral.sh/get | RYE_INSTALL_OPTION="--yes" bash \ 6 | && echo 'source "$HOME/.rye/env"' >> ~/.bashrc 7 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ubuntu", 3 | "build": { 4 | "context": ".", 5 | "dockerfile": "Dockerfile" 6 | }, 7 | // If you want to use GPU, uncomment the following 8 | // "runArgs": [ 9 | // "--gpus", 10 | // "all" 11 | // ], 12 | // "features": { 13 | // "ghcr.io/devcontainers/features/nvidia-cuda": { 14 | // "cudaVersion": 11.8 15 | // } 16 | // }, 17 | "onCreateCommand": "rye config --set-bool behavior.use-uv=true && rye sync", 18 | "postStartCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}", 19 | // "mounts": [ 20 | // { 21 | // "source": "${localEnv:HOME}/.config/git", 22 | // "target": "/home/vscode/.config/git", 23 | // "type": "bind" 24 | // } 25 | // ], 26 | "customizations": { 27 | "vscode": { 28 | "settings": { 29 | "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python" 30 | }, 31 | "extensions": [ 32 | "ms-azuretools.vscode-docker", 33 | "ms-python.python", 34 | "ms-toolsai.jupyter", 35 | "charliermarsh.ruff" 36 | ] 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /.github/workflows/check-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Check Pull Request 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, reopened] 6 | 7 | jobs: 8 | check-pull-request: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v3 13 | with: 14 | ref: ${{ github.head_ref }} 15 | - name: Install Rye 16 | uses: eifinger/setup-rye@v2 17 | - name: Set Rye config 18 | run: | 19 | rye config --set-bool behavior.use-uv=true 20 | - name: Install Dependencies 21 | run: rye sync 22 | - name: Lint 23 | run: rye run lint 24 | - name: Test 25 | run: rye run test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | # ruff 163 | .ruff_cache 164 | -------------------------------------------------------------------------------- /.mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12.2 2 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | line-length = 120 2 | indent-width = 4 3 | 4 | [lint.per-file-ignores] 5 | "__init__.py" = ["F401", "F403"] 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-python.python", 4 | "charliermarsh.ruff", 5 | "ms-vscode-remote.vscode-remote-extensionpack" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "[python]": { 4 | "editor.detectIndentation": false, 5 | "editor.insertSpaces": true, 6 | "editor.tabSize": 4, 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll": "always", 9 | "source.organizeImports": "always" 10 | } 11 | }, 12 | "ruff.path": [ 13 | "${workspaceFolder}/.venv/bin/ruff" 14 | ] 15 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | .DEFAULT_GOAL := help 3 | 4 | run: ## Run Python Script 5 | @rye run main 6 | 7 | fmt: ## Format python code 8 | @rye run fmt 9 | 10 | lint: ## Lint python code 11 | @rye run lint 12 | 13 | test: ## Run test 14 | @rye run test 15 | 16 | help: ## Show help 17 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ 18 | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python with Rye 2 | 3 | ## Overview 4 | 5 | Docker で [rye](https://rye-up.com) を使う際のテンプレート. VSCode DevContainer を使って環境構築を行う. 6 | 7 | ## Run Python Script 8 | 9 | 初回のコンテナ起動後に `rye` の仮想環境 `.venv/` がプロジェクト直下に作られる. `.venv/bin/python` が Python のパスとなる. `.venv/` を削除してしまった場合はコンテナを再度ビルドすればよい. また, VSCode でプロジェクトを開くと自動的に `.venv/` を認識するので, Python のパスを設定する必要はない. 10 | 11 | Python のコードを実行するには 12 | 13 | ```sh 14 | rye run python 15 | ``` 16 | 17 | で実行するか, 18 | 19 | ```sh 20 | make run 21 | ``` 22 | 23 | で `src/python_docker/main.py` の `main()` が実行される. 24 | 25 | ## Add Python Package 26 | 27 | ```sh 28 | rye add 29 | ``` 30 | 31 | を実行後, 32 | 33 | ```sh 34 | rye sync 35 | ``` 36 | 37 | で pip と同様にインストール可能. 38 | 39 | ## JupyterLab 40 | 41 | VSCode 上で notebook を実行することができる. notebook を開き, Select Kernel をクリックして `.venv/bin/python` を指定する. 42 | 43 | ## GPU usage 44 | 45 | `.devcontainer/devcontainer.json` の GPU 関連の記述のコメントを外すと CUDA, GPU が利用可能になる. 46 | 47 | ## PyTorch installation 48 | 49 | 以下のコマンドで PyTorch をインストール可能. ただし, CPU 環境と GPU 環境では異なるモジュールがインストールされる. 50 | 51 | ```sh 52 | rye add torch && rye sync 53 | ``` 54 | -------------------------------------------------------------------------------- /notebooks/sample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Hello, World!\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "print(\"Hello, World!\")" 18 | ] 19 | } 20 | ], 21 | "metadata": { 22 | "kernelspec": { 23 | "display_name": "Python 3 (ipykernel)", 24 | "language": "python", 25 | "name": "python3" 26 | }, 27 | "language_info": { 28 | "codemirror_mode": { 29 | "name": "ipython", 30 | "version": 3 31 | }, 32 | "file_extension": ".py", 33 | "mimetype": "text/x-python", 34 | "name": "python", 35 | "nbconvert_exporter": "python", 36 | "pygments_lexer": "ipython3", 37 | "version": "3.12.0" 38 | } 39 | }, 40 | "nbformat": 4, 41 | "nbformat_minor": 4 42 | } 43 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "python-docker" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | dependencies = [ 6 | "numpy>=1.26.4", 7 | "ipykernel>=6.29.4", 8 | ] 9 | readme = "README.md" 10 | requires-python = ">= 3.8" 11 | 12 | [project.scripts] 13 | main = "python_docker:main" 14 | 15 | [build-system] 16 | requires = ["hatchling"] 17 | build-backend = "hatchling.build" 18 | 19 | [tool.rye] 20 | managed = true 21 | dev-dependencies = [ 22 | "ruff>=0.3.7", 23 | "mypy>=1.9.0", 24 | "pytest>=8.1.1", 25 | ] 26 | 27 | [tool.rye.scripts] 28 | fmt = { chain = ["ruff format ./src ./tests"]} 29 | lint = { chain = [ 30 | "ruff check --fix ./src ./tests", 31 | "mypy ./src ./tests" 32 | ]} 33 | test = { chain = ["pytest ./tests"] } 34 | 35 | [tool.hatch.metadata] 36 | allow-direct-references = true 37 | 38 | [tool.hatch.build.targets.wheel] 39 | packages = ["src/python_docker"] 40 | -------------------------------------------------------------------------------- /requirements-dev.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | # generate-hashes: false 10 | 11 | -e file:. 12 | asttokens==2.4.1 13 | # via stack-data 14 | comm==0.2.2 15 | # via ipykernel 16 | debugpy==1.8.1 17 | # via ipykernel 18 | decorator==5.1.1 19 | # via ipython 20 | executing==2.0.1 21 | # via stack-data 22 | iniconfig==2.0.0 23 | # via pytest 24 | ipykernel==6.29.4 25 | # via python-docker 26 | ipython==8.23.0 27 | # via ipykernel 28 | jedi==0.19.1 29 | # via ipython 30 | jupyter-client==8.6.1 31 | # via ipykernel 32 | jupyter-core==5.7.2 33 | # via ipykernel 34 | # via jupyter-client 35 | matplotlib-inline==0.1.7 36 | # via ipykernel 37 | # via ipython 38 | mypy==1.9.0 39 | mypy-extensions==1.0.0 40 | # via mypy 41 | nest-asyncio==1.6.0 42 | # via ipykernel 43 | numpy==1.26.4 44 | # via python-docker 45 | packaging==24.0 46 | # via ipykernel 47 | # via pytest 48 | parso==0.8.4 49 | # via jedi 50 | pexpect==4.9.0 51 | # via ipython 52 | platformdirs==4.2.0 53 | # via jupyter-core 54 | pluggy==1.4.0 55 | # via pytest 56 | prompt-toolkit==3.0.43 57 | # via ipython 58 | psutil==5.9.8 59 | # via ipykernel 60 | ptyprocess==0.7.0 61 | # via pexpect 62 | pure-eval==0.2.2 63 | # via stack-data 64 | pygments==2.17.2 65 | # via ipython 66 | pytest==8.1.1 67 | python-dateutil==2.9.0.post0 68 | # via jupyter-client 69 | pyzmq==26.0.0 70 | # via ipykernel 71 | # via jupyter-client 72 | ruff==0.3.7 73 | six==1.16.0 74 | # via asttokens 75 | # via python-dateutil 76 | stack-data==0.6.3 77 | # via ipython 78 | tornado==6.4 79 | # via ipykernel 80 | # via jupyter-client 81 | traitlets==5.14.2 82 | # via comm 83 | # via ipykernel 84 | # via ipython 85 | # via jupyter-client 86 | # via jupyter-core 87 | # via matplotlib-inline 88 | typing-extensions==4.11.0 89 | # via mypy 90 | wcwidth==0.2.13 91 | # via prompt-toolkit 92 | -------------------------------------------------------------------------------- /requirements.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | # generate-hashes: false 10 | 11 | -e file:. 12 | asttokens==2.4.1 13 | # via stack-data 14 | comm==0.2.2 15 | # via ipykernel 16 | debugpy==1.8.1 17 | # via ipykernel 18 | decorator==5.1.1 19 | # via ipython 20 | executing==2.0.1 21 | # via stack-data 22 | ipykernel==6.29.4 23 | # via python-docker 24 | ipython==8.23.0 25 | # via ipykernel 26 | jedi==0.19.1 27 | # via ipython 28 | jupyter-client==8.6.1 29 | # via ipykernel 30 | jupyter-core==5.7.2 31 | # via ipykernel 32 | # via jupyter-client 33 | matplotlib-inline==0.1.7 34 | # via ipykernel 35 | # via ipython 36 | nest-asyncio==1.6.0 37 | # via ipykernel 38 | numpy==1.26.4 39 | # via python-docker 40 | packaging==24.0 41 | # via ipykernel 42 | parso==0.8.4 43 | # via jedi 44 | pexpect==4.9.0 45 | # via ipython 46 | platformdirs==4.2.0 47 | # via jupyter-core 48 | prompt-toolkit==3.0.43 49 | # via ipython 50 | psutil==5.9.8 51 | # via ipykernel 52 | ptyprocess==0.7.0 53 | # via pexpect 54 | pure-eval==0.2.2 55 | # via stack-data 56 | pygments==2.17.2 57 | # via ipython 58 | python-dateutil==2.9.0.post0 59 | # via jupyter-client 60 | pyzmq==26.0.0 61 | # via ipykernel 62 | # via jupyter-client 63 | six==1.16.0 64 | # via asttokens 65 | # via python-dateutil 66 | stack-data==0.6.3 67 | # via ipython 68 | tornado==6.4 69 | # via ipykernel 70 | # via jupyter-client 71 | traitlets==5.14.2 72 | # via comm 73 | # via ipykernel 74 | # via ipython 75 | # via jupyter-client 76 | # via jupyter-core 77 | # via matplotlib-inline 78 | wcwidth==0.2.13 79 | # via prompt-toolkit 80 | -------------------------------------------------------------------------------- /src/python_docker/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import main 2 | -------------------------------------------------------------------------------- /src/python_docker/main.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def main(): 5 | print(np.arange(15).reshape((3, 5))) 6 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | def test_main(): 2 | None 3 | --------------------------------------------------------------------------------