ã¿ãªããããã«ã¡ã¯ã é»é ç·ç ã³ãŒããŒã¬ãŒãæ¬éšã·ã¹ãã æšé²éšã®äœè€å€ªäžã§ãã ãã®èšäºã§ã¯ã VS Code ã®Dev Containerã䜿ã£ãŠOSã«äŸåããªã Python ã®éçºç°å¢ãæ§ç¯ããæ¹æ³ãã¹ããã ãã€ã¹ ãããã§äžå¯§ã«èª¬æããŸãã VS Code ã®å©çšçµéšãããããŸã Python ã«ããã¢ããªã±ãŒã·ã§ã³éçºã«èå³ã®ããæ¹ãæ³å®èªè
ãšããŠèšè¿°ããŠããŸãã Python ã®åå¿è
ããäžçŽè
åããæèããŠæžããŠããŸãã®ã§ãæå³ããŠåé·ãªèª¬æãããŠããŸãã ãã§ã« Python ã«ããã¢ããªã±ãŒã·ã§ã³éçºã«ååã«è©³ããæ¹ã¯ããŸãã¯ãŸãšãã ãèªãã§ã¿ãŠãã ãããç§èªèº«ããã»ã© Python ã®ãšã³ã·ã¹ãã ã«è©³ããããã§ã¯ãããŸããã®ã§ãç¥èã®æãæŒãã¯æããããã§ãããããããã£ãäºã«æ°ãä»ããããXãªã©ã® SNS ã§ãã®èšäºã®URLãä»ããŠã³ã¡ã³ããããŠããã ãããšå¹žãã§ãã ã¯ããã« äºåã®æºå æå°éã®Dev Container Dev Containerã®èµ·å devcontainer.jsonãç·šéããç°å¢ã®æ§ç¯ æå°éã®PythonçšVS Codeæ¡åŒµ uvã®å°å
¥ Pythonä»®æ³ç°å¢ã®æ§ç¯ ãããžã§ã¯ãæ§æãã¡ã€ã«ã®äœæ ã³ã³ããäœææã«åäœããã·ã§ã«ã¹ã¯ãªããã®è¿œå Pythonã®ä»®æ³ç°å¢çšããªã¥ãŒã ã®ããŠã³ã ãããžã§ã¯ãããŒã«ã«ã«ä»®æ³ç°å¢ãäœã ãããPythonãåããã ãã©ãŒããã¿ãšLinterã®å°å
¥ pytestã®å°å
¥ ãã¹ãã³ãŒãã®è¿œå ãã¹ããã¿ãŒããã«ããåãã ãã¹ããGUIããåãã ãã¹ãããããã¬ããåãã ãã¹ãã«ãã¬ããžãèšæž¬ãã pytest-covã®å°å
¥ ãã¹ãã«ãã¬ããžã®åŸ®èª¿æŽ å®è¡æãªãã·ã§ã³ã®èª¿æŽ ã«ãã¬ããžé€å€ã®èª¿æŽ VS Codeã«ã«ãã¬ããžã¬ããŒããçµ±åãã VS Codeã«ãããã«ãã¬ããžã¬ããŒãã®ç¢ºèªæ¹æ³ ã¿ã¹ã¯ã©ã³ããŒã®å°å
¥ uvã§ã®ã¿ã¹ã¯ã©ã³ã㌠ãŸãšã ãã®èšäºã§ç޹ä»ããŠããéçºç°å¢ã®æ§æãã¡ã€ã« .devcontainer/devcontainer.json .devcontainer/postCreateCommand.sh .gitignore pyproject.toml ã¯ããã« ãœãããŠã§ã¢éçºãããŒã ã§è¡ãã«ããã£ãŠããã£ãšãå°é£ãªèª²é¡ã®äžã€ã¯éçºç°å¢ã®åçŸæ§ãæ
ä¿ããããšã§ãã éçºããŒã ã®ã¡ã³ããŒã¯ããããããåºæã®çµéšãšç¥èãæã£ãŠããŸããããããžã§ã¯ãã«åç»ããéã®å¥çŽãç°ãªãããšãå€ãã§ããããå
šãŠã®ã¡ã³ããŒããåäžã®ãããžã§ã¯ãã«äœ¿ãããå
šãŠã® å·¥æ° ã䜿ã£ãŠãããžã§ã¯ãã«è²¢ç®ã§ããããã§ã¯ãããŸããã人ã«ãã£ãŠã¯ãè€æ°ã®ãããžã§ã¯ããæãæã¡ããŠããããšã¯ãããŸãã ããéçºã¡ã³ããŒã¯æ
£ããŠãã Mac ã䜿ããããšèããæãéãã¡ã³ããŒã¯äŒç€Ÿããæšæºçã«è²žäžããã Windows ã§éçºããããšèãããããããŸãããããããã¢ããªã±ãŒã·ã§ã³ã®ãããã€å
㯠Ubuntu ã Debian ãšãã£ã Linux ã ã£ããããŸãã ãããã£ãç¶æ³ã§ã¯ãç¹å®ã®ã¡ã³ããŒã§ã®ã¿çºçããäžå
·åãååšãããããŸãã¯ç¹å®ã®ã¡ã³ããŒã®ããŒã«ã«ç°å¢ã§ããã¢ããªã±ãŒã·ã§ã³ãåäœããªããšãã£ãäºãèµ·ãããŸãã åå ãæããã«ãªãã°ãåã«PATH ç°å¢å€æ° ã®éãã®ãããªç°¡åãªããšãããããŸãããããã®éçšã¯åŸã
ã«ããŠå°é£ãªãã®ã«ãªããŸãããããã£ã现ããç°å¢å·®ç°ãåå ãšããåé¡ã®èª¿æ»ã¯ã·ãã¢ãªã¡ã³ããŒã«ããã§ããªãããšãå€ãã®ã§ãã ãããããã®èª¿æ»ã®æéã¯æããã«ç¡é§ã§ãããã»ãŒäœã®äŸ¡å€ããããŸããã ä»å 䟡å€ã®é«ãã·ãã¢ãªã¡ã³ããŒã® å·¥æ° ãããããã£ã調æ»ã§æµªè²»ããããšã¯ãããžã§ã¯ãã«ãšã£ãŠæãŸãããªãã§ãããã ãã®èšäºã§ç޹ä»ããææ³ã§ã¯ãDockerããŒã¹ã®ã³ã³ããæè¡ã䜿ãããšã§ãã¡ã³ããŒæ¯ã®ç°å¢å·®åãã§ããã ãå°ããã§ããŸããããŒããŠã§ã¢ã¹ããã¯ããããã¯ãŒã¯ã«èµ·å ãããã®ä»¥å€ã¯å·®åãå
šãååšããªããªããšèšã£ãŠãè¯ãã§ããããã€ãŸããéçºã¡ã³ããŒã®ãªãã§èª°ãã®ãã·ã³ã§èµ·ããåé¡ã¯ãã¡ã³ããŒå
šå¡ã®ãã·ã³ã§åçŸããŸãã æ°ã«ãªã£ãŠããŸãããïŒããã§ã¯ãDev Containerã䜿ã£ãéçºç°å¢æ§ç¯ã«ã€ããŠèª¬æãå§ããŸãã äºåã®æºå ãã®èšäºãåæãšããç°å¢ã«ã€ããŠè»œã説æããŸãã ãŸãã VS Code ãäºåã«ã€ã³ã¹ããŒã«ããŠãããŠãã ããã æ¬¡ã«ã Docker Desktop ãã€ã³ã¹ããŒã«ããŠåäœããç¶æ
ã«ããŠãã ãããåºæ¬çã«ã¯åã« ã€ã³ã¹ããŒã© ãå®è¡ããã°åäœããç¶æ
ã«ãªããŸãã ãããŠã VS Code ã« Dev Containers æ¡åŒµãã€ã³ã¹ããŒã«ããŠãããŠãã ããã æåŸã«ãäœæ¥çšã®ãããžã§ã¯ã ãã£ã¬ã¯ã ãªãäœæããŠãã ãããããã§ã¯ã devcontainer-python ãšãã ãã£ã¬ã¯ã ãªãäœæããŠãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªãšããŠããŸãã æå°éã®Dev Container ãŸãã¯ãDev Containerã§å
¬åŒã«æäŸãããŠãã Python ã®éçºç°å¢ãå°å
¥ããŠã¿ãŸãããã ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªã«ã .devcontainer ãšãã ãã£ã¬ã¯ã ãªãäœã£ãŠããã®äžã« devcontainer.json ãšãããã¡ã€ã«åã§ä»¥äžã®å
容ãä¿åããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] } name ã®å€ã¯ãåãããããååãªãäœã§ãããã§ããããã§ã¯ãdevcontainer- python ãšããŠããŸãã image ã®å€ã¯ãmcr. microsoft .com/devcontainers/ python :3.12-bookworm ãšããŠããŸããããã¯ãå
¬åŒã®ã€ã¡ãŒãžåã§ãã ããŒã¹ã€ã¡ãŒãžã Python ã©ã³ã¿ã€ã ã®ããŒãžã§ã³ãå¥ãªãã®ã«ãããå Žåã«ã¯ã https://github.com/microsoft/vscode-dev-containers/tree/main/containers/python-3 ããæ¢ããŠãã ããã containerEnv ã®å€ã¯ãã³ã³ããå
ã§åç
§ããã ç°å¢å€æ° ã§ããããã§ã¯ ã¿ã€ã ãŸãŒã³ ã Asia/Tokyo ã«ãªãããèšå®ããŠããŸããæå»ã«èµ·å ããåé¡ã®èª¿æ»ã¯é£ããã®ã§ãããã§æç€ºçã«èšå®ããŠããŸãã runArgs ã®å€ãšã㊠--init ãæž¡ãããšã§ãDockerã /dev/init ãšããã·ã°ãã«ãã³ããªã³ã°çšã®ããã»ã¹ãèµ·åããŠãããŸããããã«ãã£ãŠã³ã³ãããå®å®çã«ã·ã£ããããŠã³ã§ããããã«ãªããŸãã Dev Containerã®èµ·å ããã§ã¯ãäœã£ãDev Containerãèµ·åããŠã¿ãŸãããã ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªã VS Code ã§éããäžã§ç»é¢å·Šç«¯ã®ã¢ã€ã³ã³ãã¯ãªãã¯ããŠREMOTE EXPLORER ã衚瀺ããŸãã reopen the current folder in a container ã®ãªã³ã¯ãã¯ãªãã¯ãããšDev Containerã®èµ·åãå§ãŸããŸãã ç»é¢å³äžã«ãDev Containerã®èµ·åãå§ãŸã£ãããšãéç¥ãããã€ã¢ãã°ãæ°ç§éã ã衚瀺ãããã®ã§ããµããšã¯ãªãã¯ããŸãããã ã¯ãªãã¯ãããšèµ·åãã°ãæµããŠããæ§åã芳å¯ã§ããŸãã èµ·åãã°ã®æµããèœã¡çããããã¿ãŒããã«ãèµ·åããŠã¿ãŸãããããã°ã®å³äžãããã«ãã + ãã¿ã³ã§ããCtrlããŒãš@ããŒãåæã«æŒããŠãæ§ããŸããã 以äžã®ããã«ã¿ãŒããã«ã衚瀺ãããŸããã ã³ã³ããå
ã§ã¯ vscode ãšãããŠãŒã¶ãŒã§ã workspace/devcontainer-python ãšãã ãã£ã¬ã¯ã ãªãã«ã¬ã³ã ãã£ã¬ã¯ã ãªã«ããŠããŸãã ãã£ã¬ã¯ã ãªãã¹ã® devcontainer-python éšåã¯ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªãšåãã«ãªã£ãŠããã¯ãã§ãã devcontainer. json ãç·šéããç°å¢ã®æ§ç¯ ãããããdevcontainer. json ãç·šéããªããéçºç°å¢ãæ§ç¯ããŠããã®ã§ããŸãã¯å¿«é©ã« json ãã¡ã€ã«ãç·šéã§ããããã«ããŸãããã devcontainer. json ã«ã¯ãDev ContainerãšããŠèµ·åãã VS Code ãæ§æããããã®èšå®é
ç®ããããŸãã®ã§ãããããç·šéããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode " ] } } } customizations ãšããããŒãDev Containerã®æ§æãè¡ãããã®èšå®é
ç®ã§ãããã®äžã« vscode ãšããé
ç®ããããŸããã settings ã®äžã§ã¯ã VS Code ã®èšå®é
ç®ã管çããŸãã editor.renderWhitespace ã®å€ãšããŠã all ãèšå®ããŠããã®ã¯ããã¡ã€ã«ã®äžã«çŽã蟌ãã å
šè§ã¹ããŒã¹ãèŠã€ããããããããã§ããç§ãã¡ã IME ã䜿ã£ãŠãã以äžãæå³ããªãå Žæã«å
šè§ã¹ããŒã¹ãå
¥ã蟌ãã§ããŸããããã«ãã£ãŠçè§£ãå°é£ãªãšã©ãŒã¡ãã»ãŒãžãèªãããšã«ãªãã®ã¯é¿ããããŸãããå
šè§ã¹ããŒã¹ãèŠããŠããã°ããããã£ãããããããæãåºãããããªããŸãã [json][jsonc] ã®å€ãšããŠãããã€ãèšå®ããŠããŸããã¡ãªã¿ã«ãjsoncã¯ã JSON with commentsã®ç¥ç§°ã§ãã editor.defaultFormatter ã®å€ãšããŠãesbenp.prettier- vscode ãèšå®ããŠããŸããããã«ãã£ãŠprettierã䜿ã£ããã©ãŒããããè¡ãããŸãã editor.formatOnSave ã®å€ãšããŠãtrueãèšå®ããããšã§ãã¡ã€ã«ä¿åæã«ãã©ãŒããããè¡ãããããã«ããŠããŸãã editor.codeActionsOnSave ã®å€ãšããŠãsource.fixAll ã«"explicit"ãæå®ããããšã§èªåçã«è£æ£ã§ãããã©ãŒããããšã©ãŒãprettierãç©æ¥µçã«è£æ£ããŠãããŸãã extensions ã®äžã§ã¯ãDev ContainerãšããŠèµ·åããã VS Code ã«ã€ã³ã¹ããŒã«ããã VS Code æ¡åŒµãåæããŸããããã§ã¯ã JSON ãèªåãã©ãŒãããããããã® esbenp.prettier-vscode ãèšå®ããŠããŸãã æå°éã® Python çš VS Code æ¡åŒµ 次ã¯ã Python çšã® VS Code æ¡åŒµãããã€ã远å ããŠãããŸãããã ããããã®æ¡åŒµã¯ã Python Python Indent autoDocstring ã®äžã€ã§ããä»ã«ã䟿å©ãªãã®ã¯å€ããããŸãããç¹ã«è°è«ã®äœå°ãªãå°å
¥ã§ãããã®ã¯ãããã§ãã devcontainer. json ã®extensionsã«ãããã®æ¡åŒµã远å ãããšã以äžã®ããã«ãªããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json ãä¿®æ£ããããå¿ããã«Dev Containerããªãã«ãããŸãããã ãªãã«ãããã«ã¯ãREMOTE EXPLORER ã衚瀺ããŠåäœããŠããã³ã³ãããå³ã¯ãªãã¯ããŸãã ããã§è¡šç€ºããã ã³ã³ããã¹ãã¡ãã¥ãŒ ãã Rebuild Container ãéžæããŸãã ããã§ã VS Code ã Python çšã®ãšãã£ã¿ãšããŠäœ¿ãããã®æºåã¯æŽã£ããšèšããŸãã ããããªãããã¢ããªã±ãŒã·ã§ã³ã®éçºç°å¢ãšåŒã¶ã«ã¯ããŸã ãŸã äžè¶³ããããŸãã®ã§é 次æŽããŠãããŸãããã uvã®å°å
¥ Python ã§ã¢ããªã±ãŒã·ã§ã³éçºãè¡ããªããæšæºã©ã€ãã©ãªã䜿ãã ãã§ãªã巚倧ãªã³ãã¥ããã£å
ã§æäŸãããã¢ãžã¥ãŒã«ãå©çšããŠããã®æ©æµã«ãããããŸãããã pipã³ãã³ãã ãã§ OSS ã®ã¢ãžã¥ãŒã«ãã€ã³ã¹ããŒã«ãããšãã ã¹ããã³ã°ã¹ ã¿ã€ã«ãè¯ããã®ã§ãããç§ãæšå¥šããã®ã¯uvã䜿ã£ãã¢ãžã¥ãŒã«ç®¡çã§ãã以åã¯Poetryãæšå¥šããŠããŸããããä»ã¯Rustã§å®è£
ãããŠããŠé«éã«åäœããuvããå§ãããŠããŸãã Dev Containerã«ã¯ãfeaturesãšããæ©èœãããdevcontainer. json ãå°ãæžãå ããã ãã§uvã䜿ããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } features ã®å€ãšããŠã "ghcr.io/jsburckhardt/devcontainer-features/uv:1": {} ãèšè¿°ãããŠããŸããã ããããããšã§ãDev Containerèµ·åæã«uvãã³ã³ããå
ã®ã²ã¹ãOSã«ã°ããŒãã«ã€ã³ã¹ããŒã«ãããã®ã§ãã devcontainer. json ãæžãæãããDev Containerããªãã«ãããŸãããã Python ä»®æ³ç°å¢ã®æ§ç¯ Dev Containerã®ãªãã«ãããæ»ã£ãŠããããuvã䜿ããããžã§ã¯ãæ§æãã¡ã€ã«ãäœããŸãããã ãããžã§ã¯ãæ§æãã¡ã€ã«ã®äœæ ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªã§ã uv init ã³ãã³ããå®è¡ãããšããã€ãã®ãã¡ã€ã«ãäœæãããŸãã ç¹ã«éèŠãªpyproject.tomlã ãæç²ããŸãã [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] ãã®ãã¡ã€ã«ãäœãæ¹æ³ã¯è²ã
ãããŸãã®ã§ã詳现ãç¥ãããæ¹ã¯uvã®ããã¥ã¡ã³ããåèã«ããŠãã ããã Working on projects ã³ã³ããäœææã«åäœãã ã·ã§ã«ã¹ã¯ãªãã ã®è¿œå 次ã¯ãã³ã³ããããã«ãããçŽåŸã ãåäœãã ã·ã§ã«ã¹ã¯ãªãã ã远å ããŸãã ãã® ã·ã§ã«ã¹ã¯ãªãã ã工倫ãããšã³ã³ããå
ã§è²ããªäœæ¥ãããŠãæ··ä¹±ç¶æ³ã«ãªã£ãŠããªãã«ãããã ãã§å
šãŠãå
ã«æ»ããŸãã ãŸãã¯ãç°¡å㪠ã·ã§ã«ã¹ã¯ãªãã ããå§ããŸãããã.devcontainer ãã£ã¬ã¯ã ãªã«postCreateCommand.shãšãããã¡ã€ã«åã§ä»¥äžã®å
容ãä¿åããŸãããã®ãã¡ã€ã«ã®æ¹è¡ã³ãŒãã¯LFã«ãŠãã ããã #!/bin/sh # postCreateCommand.sh echo "START Install" sudo chown -R vscode:vscode . echo "FINISH Install" å
容ãšããŠã¯ããããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªä»¥äžã«ãããã¡ã€ã«ã ãã£ã¬ã¯ã ãªã®ãªãŒããŒæš©éãå
šãŠ vscode ãŠãŒã¶ãŒããã³ vscode ã°ã«ãŒãã«ãããšãããã®ã§ãã 以äžã®ã³ãã³ããã¿ãŒããã«ã§å®è¡ããŠå®è¡æš©éãä»äžããŠäžããã chmod +x .devcontainer/postCreateCommand.sh Dev Containerã§ã¯ããã¹ãOSäžã«ãããããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªãèªåçã«bindããŠã³ãããŠããŸããã€ãŸããã²ã¹ãOSã§ããã³ã³ããããèŠããæ¢åã®ãã¡ã€ã«ã ãã£ã¬ã¯ã ãªã®æš©éãrootã«ãªã£ãŠããŸããŸããDev Containerå
ã§äœ¿ããŠãŒã¶ãŒãrootã«ããŠãè¯ãã®ã§ãããå人çã«ã¯äŸãã³ã³ããå
ã§ãã£ããšããŠãæ®æ®µäœ¿ããããŠãŒã¶ãŒãšããŠrootã䜿ãããã¯ãããŸããããã®ãããªç¿æ
£ãæã€ããšã§äŸãã°ãäœãæªæã®ããã¢ãžã¥ãŒã«ã誀ã£ãŠã€ã³ã¹ããŒã«ããŠããŸã£ãæã®è¢«å®³ãå°ãã ãäœæžã§ããŸãã 䟿å©ãªäºã«Dev Containerãå
¬åŒã«æäŸããŠããã³ã³ããã€ã¡ãŒãžã§ã¯ã vscode ãŠãŒã¶ãŒãsudoersãšããŠç»é²ããŠããŸããåèïŒ common-debian.sh#L209-L213 ãã® ã·ã§ã«ã¹ã¯ãªãã ãåããã«ã¯ãdevcontainer. json ã«postCreateCommandãšããããŒã§ ã·ã§ã«ã¹ã¯ãªãã ã® ã¯ãŒã¯ã¹ããŒã¹ ããã® çžå¯Ÿãã¹ ãèšå®ããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json ãæžãæããã®ã§Dev Containerããªãã«ãããŸãããã ã³ã³ããäœææã«ãpostCreateCommand.shãåäœããã®ã確èªã§ããŸãã ããã¯äœææããåäœããªãã®ã§ãäŸãã°åã« VS Code ãåèµ·åããŠãåäœããŸãããã€ãŸãå°ã
ã³ã¹ãã®é«ãåŠçãèšè¿°ããŠãåŸ
ã€ã®ã¯äžåºŠã ãã«ãªããŸãã Python ã®ä»®æ³ç°å¢çšããªã¥ãŒã ã®ããŠã³ã 次ã¯ãuvãäœã Python ã®ä»®æ³ç°å¢ã«ã€ããŠèããŠã¿ãŸãããã èŠã¯ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªä»¥äžã«ãäœæããã .venv ãã£ã¬ã¯ã ãªãã©ãæ±ãããšããããšã§ãã åãããéžæè¢ãšããŠæåã®åè£ã¯ç¹ã«æ°ã«ãã .gitignore ãã¡ã€ã«ãž .venv ãã£ã¬ã¯ã ãªã远å ããããšããããæ¹ã§ãããããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªã¯ãDev Containerã«ãã£ãŠæ¢ã«bindããŠã³ããããŠããã®ã ããããã®ãŸãŸã«ããŠãããŠãããã»ã©å€§ããªåé¡ã¯ãããŸããããšã¯ããããã¹ãOSã§ã¯å®è¡ äžèœ ãªå®è¡ãã€ããªãçŽæ¥ ãã¡ã€ã«ã·ã¹ãã äžã«çŸããã®ã¯ãããŸãæ°æã¡ããã¯ãããŸããã æ¬¡ã®éžæè¢ã¯ã .venv ãã£ã¬ã¯ã ãªãç¹å¥æ±ãããŠvolumeããŠã³ãããããšã§ããããã«ãã£ãŠããã¹ãOSäžã« Python ã®ä»®æ³ç°å¢ãçŽæ¥çŸããªããªããŸããå ããŠãvolumeããŠã³ãã¯bindããŠã³ããããããI/Oã®ããã©ãŒãã³ã¹ãå°ãã ãæ¹åããŸãããŸããgitãªã©ã®æ§æç®¡çããŒã«ããæç€ºçã«é€å€ããªããŠããã®ãå©ç¹ã§ãã volumeããŠã³ããDev Containerã«è¿œå ããã«ã¯ãdevcontainer. json ã«mountsãšããé
ç®ã®èšå®ã远å ããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json ã§ã¯ãèšå®å€ã®äžã« ${ ã§å§ãŸã } ã§çµããéšåããããšããã®äžã倿°ãšããŠç¹å¥æ±ãããŸãã mountsã®å€ã ããåãåºããŠããã®ãããã§ãã "source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume" ããã§ã¯ã devcontainerId ãš containerWorkspaceFolder ãšãã倿°ãå±éãããŠDockerã®èµ·åãªãã·ã§ã³ã«æž¡ãããŸããããããã«ã©ããªå€ãå
¥ã£ãŠããã®ãã¯ãå
¬åŒã®ããã¥ã¢ã«ã確èªããŠãã ããã https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson ãããžã§ã¯ãããŒã«ã«ã«ä»®æ³ç°å¢ãäœã 次ã¯ããããã Python ä»®æ³ç°å¢ã®æ§ç¯ã§ãããšèšã£ãŠãuvã䜿ã£ãŠããã®ã§æ¥µããŠç°¡åã§ãã å
ã»ã©äœã£ããpostCreateCommand.sh ã«uvçšã®ã³ãã³ãã2è¡ã远å ããã ãã§ãã #!/bin/sh # postCreateCommand.sh echo "START Install" sudo chown -R vscode:vscode . uv venv --allow-existing uv sync echo "FINISH Install" ãŸãã uv venv --allow-existing ã³ãã³ããå®è¡ããŠä»®æ³ç°å¢ãäœããŸãã --allow-existing ã¯æ¢ã«.venv ãã£ã¬ã¯ã ãªãååšããå Žåã«ã¯ããã®ãŸãŸäœ¿ãããã«ãããªãã·ã§ã³ã§ãã uv venv ã³ãã³ããå®è¡ãããæã«æ¢åã®.venv ãã£ã¬ã¯ã ãªãååšãããšåé€ããŠåçæããããšããã®ã§ãããä»å㯠.venv ãã£ã¬ã¯ã ãªã¯ããªã¥ãŒã ããŠã³ãããŠããã®ã§åé€ã§ããã«ãšã©ãŒã«ãªããŸãã ããããã§ uv venv --allow-existing --python 3.11 ã®ããã«æ¢ã«ã€ã³ã¹ããŒã«æžã¿ã® Python ãšã¯éã£ãããŒãžã§ã³ã® Python ãæå®ãããšèªåçã«å®è¡ãã€ããªãããŠã³ããŒãããŠããŠãããŸãã æ¬¡ã®ã uv sync ã³ãã³ããå®è¡ãããšpyproject.tomlã«åºã¥ããŠå¿
èŠãªã©ã€ãã©ãªãããŒã«ãèªåçã«ã€ã³ã¿ãŒãããããããŠã³ããŒãããŠãããŸãã åäœã確èªããããã«ã ã·ã§ã«ã¹ã¯ãªãã ã®å€æŽãçµãã£ããDev Containerããªãã«ãããŸãããã ããã Python ãåããã ããããã¯ããããã Python ã®ã³ãŒããåãããŸãã ã¯ãŒã¯ã¹ããŒã¹ ã®ã«ãŒã ãã£ã¬ã¯ã ãªã«ã¯ uv init ãäœæãã hello.py ãšãããã¡ã€ã«ãããã¯ãã§ãã def main (): print ( "Hello from devcontainer-python!" ) if __name__ == "__main__" : main() ç¹å¥ãã®ãªãã³ãŒãã§ããããã®ãã¡ã€ã«ãéããç¶æ
ã§ VS Code ã®å³äžãããã«æ³šç®ããŠãã ããã ãã®ãããªè¡šç€ºã«ãªã£ãŠãããªãã Python ã®ä»®æ³ç°å¢ã«é
眮ãããŠãã ã€ã³ã¿ãŒããªã¿ ãŒã䜿ãããŠããŸãã ããã§ãªãå Žåã¯èµ€ãå²ã£ãå
åŽãã¯ãªãã¯ããŠãã ããããããããšã ã€ã³ã¿ãŒããªã¿ ãŒãéžæãããã€ã¢ãã°ã衚瀺ãããã®ã§ã .venv/bin/python ãšãããã¹ã® ã€ã³ã¿ãŒããªã¿ ãŒãã¯ãªãã¯ããããšã§éžæããŠãã ããã ãããžã§ã¯ãçŽäžã®.venv ãã£ã¬ã¯ã ãªã VS Code ã«æ£ããèªèãããç¶æ
ã«ãªããšãã¿ãŒããã«ãèµ·åããéãèªåçã«activate ã¹ã¯ãªãã ãå®è¡ããŠãããŸãã ããã§uvã§ç®¡çããŠããã¢ãžã¥ãŒã«ãå®è¡ãã€ããªãå®è¡ãããããã«ãªããŸããã ãã®ç¶æ
ãç¶æããããã«ãdevcontainer. json ã« python.defaultInterpreterPath ãšããèšå®ã« ".venv/bin/python" ã远å ããŸãã èšå®è¿œå åŸã®devcontainer. json ã¯ä»¥äžã®ããã«ãªããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent " ] } } } devcontainer. json ãæžãæãããDev Containerããªãã«ãããŸãããã ãã©ãŒããã¿ãšLinterã®å°å
¥ ã³ãŒããå®è¡ã§ããããã«ãªã£ãã®ã§ã次ã¯ãã©ãŒããã¿ãšLinterãå°å
¥ããŸããããã¿ãŒããã«ã VS Code ããèµ·åããŠä»¥äžã®ã³ãã³ããå®è¡ããŸãã uv add ruff --dev ããã«ãã£ãŠã Python çšã®ã³ãŒããã©ãŒããã¿ãã€ãLinterã§ããRuffãã€ã³ã¹ããŒã«ãããŸãã ruff ãããã VS Code ãšé£æºããããã®èšå®ãšæ¡åŒµã远å ããŸãããã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff " ] } } } ããã§ã¯ã 以äžã®ãããªå€æŽãå ããŠããŸãã [python] ã«èšå®ãèšè¿°ããããšã§ãã¡ã€ã«ä¿åæã«ãã©ãŒããããLintãããã«äŒŽãèªåè£æ£ãè¡ãããããã«ããŸããã charliermarsh.ruff ã VS Code æ¡åŒµãšããŠè¿œå ããŸããã Ruffã¯Blackãšã®äºææ§ã®ããã«ã³ãŒããæãè¿ãéã®åºæºãšããæåæ°ã 88 ãšç°æ§ã«å°ããã®ã§ããã ãã¯èšå®ã倿ŽããŸãã Ruffã®èšå®ã¯ãpyproject.tomlã«èšèŒããŸãã [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "ruff>=0.7.1", ] [tool.ruff] line-length = 200 ruffã®èšå®ã现ãã調æŽããããšã¯å°ãªããšæããŸãããããã¥ã¢ã«ã¯ãã¡ãã§ãã Configuring Ruff devcontainer. json ãæžãæããã®ã§Dev Containerããªãã«ãã§ãããã pytestã®å°å
¥ ã³ãŒããå¿«é©ã«æžããããã«ãªã£ãŠããã®ã§ã次ã¯ãã¹ãã£ã³ã° ãã¬ãŒã ã¯ãŒã¯ ã®å°å
¥ã§ãã Python ã«ã¯å€ãã®ãã¹ãã£ã³ã° ãã¬ãŒã ã¯ãŒã¯ ããããŸãããç§ãäžçªæ°ã«å
¥ã£ãŠããã®ã¯pytestã§ãã uvã䜿ã£ãŠpytestãå°å
¥ããŸãããã以äžã®ã³ãã³ããå®è¡ããŸãã uv add pytest --dev ã¢ãžã¥ãŒã«ã远å ããã VS Code ã®èšå®ã倿ŽããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " python.testing.pytestArgs ": [ " tests ", " --capture=tee-sys ", " -vv " ] , " python.testing.pytestEnabled ": true , " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff " ] } } } ããã§è¿œå ããã®ã¯ã以äžã®äºã€ã§ãã python.testing.pytestEnabled ã trueã«ããããšã§ VS Code ãpytestã䜿ã£ãŠãã¹ãã³ãŒããæ€çŽ¢ããŸãã python.testing.pytestArgs ã«ã¯pytestèµ·åæã®ãªãã·ã§ã³ãäžã€èšå®ããŠããŸãã æåã® tests ã¯ãã® ãã£ã¬ã¯ã ãªå
ã«ãããã¹ãã³ãŒããå®è¡ãããšããæå³ã§ãã æ¬¡ã® --capture=tee-sys ã¯ããã¹ãã³ãŒãå
ã§æšæºåºåãããå
容ãpytestããã£ããã£ããŠã¿ãŒããã«ã«åºåããŠãããŸãã æåŸã® -vv ãä»ããããšã§ãpytestããã£ããã£ããåºåãéäžã§åããã«å
šãŠåºåããŸãã ãã¹ãã³ãŒãã®è¿œå ãã¹ãã³ãŒãã远å ããŠåãããŠã¿ãŸãããã ã¯ãŒã¯ã¹ããŒã¹ ã®ã«ãŒã ãã£ã¬ã¯ã ãªã« tests ãšãã ãã£ã¬ã¯ã ãªãäœã£ãŠããã®äžã«test_sample.pyãšãããã¡ã€ã«åã§ä»¥äžã®å
容ãä¿åããŸãã # content of test_sample.py def inc (x): return x + 1 def test_answer (): assert inc( 3 ) == 5 äžããããæ°åã«1ãå ç®ãã颿°ãšãããããã¹ããã颿°ã§ãã ãã¹ããã¿ãŒããã«ããåãã ãŸãã¯ããã¹ããã¿ãŒããã«ã§åãããŠã¿ãŸãããã以äžã®ã³ãã³ããå®è¡ããŸãã pytest æ¡ã®å® ã¢ãµãŒã·ã§ã³ ãæ£ãããªãã®ã§ãã¹ãã¯å€±æããŸãã ãã¹ãã GUI ããåãã æ¬¡ã¯ã VS Code ãããã¹ããåãããŠã¿ãŸãããã å·ŠåŽã®ã¡ãã¥ãŒãããã©ã¹ã³ã®ãããªã¢ã€ã³ã³ãã¯ãªãã¯ããŠTESTINGãã¥ãŒã衚瀺ããäžã§å³åãã®äžè§ãã¯ãªãã¯ãããšãã¹ããå®è¡ã§ããŸãããããã¯ãåã«ãã¹ã颿°ã®è¿ãã«ããå³åãäžè§ã§ãæ§ããŸããã äºæ³éããã¹ãã¯å€±æããŸãã ãã¹ãããããã¬ããåãã æ¬¡ã¯ãã¹ãã ãããã° å®è¡ããŸããããã³ãŒããèŠãŠãã©ãããŠãã¹ãã倱æããã®ãåãããªãæã¯ããããã¬ã䜿ããšäŸ¿å©ã§ãã ãŸãã¯ã ãã¬ãŒã¯ãã€ã³ã ãèšå®ããŸãããšãã£ã¿ã®ã¬ãã¿ãŒéšåãã¯ãªãã¯ãããšèµ€ãããä»ããŠãããã ãã¬ãŒã¯ãã€ã³ã ã«ãªããŸãã TESTINGãã¥ãŒã®è«ã¢ã€ã³ã³ãã€ãããã¿ã³ãæŒããŠå®è¡ãããšãããã¬ãåäœããŸãã èšå®ããã ãã¬ãŒã¯ãã€ã³ã ã§æ¢ãŸããšå€æ°ã®äžèº«ã ã¹ã¿ãã¯ãã¬ãŒã¹ ã確èªã§ããŸããã ããã§å¿«é©ã«ãã¹ããå®è¡ã§ããããã«ãªã£ãã®ã§ããœãããŠã§ã¢éçºç°å¢ãšããŠã¯ååã ãšèšãããããããŸããã é«å質ãªãœãããŠã§ã¢ãäœãããã«ãããã²ãšèžã匵ãããŠã¿ãŸãããã ãã¹ã ã«ãã¬ããž ãèšæž¬ãã ãã¹ã ã«ãã¬ããž ãåãããšã§ããã¹ãã³ãŒããæå³ãããšããã«ã¢ããªã±ãŒã·ã§ã³ã³ãŒãããã¹ãã§ããŠããã確èªã§ããããã«ããŸãããã ãã¹ã ã«ãã¬ããž ã¯100%ãç®æã㊠ãã¯ã€ãããã¯ã¹ãã¹ã ããã®ã«äœ¿ããšæ¥µããŠäžæ¯ãªæéãéããããšã«ãªããŸãã ãããã倧äœ75%ïœ85%ãç®éã«æå³ãããšããã«ã¢ããªã±ãŒã·ã§ã³ã³ãŒããåäœããŠãããã確èªããã®ã«äœ¿ããšéåžžã«äŸ¿å©ã§ããèªåã¯ä»æ§ãå®å
šã«ææ¡ããŠããã®ã ãšæã£ãŠãæãæŒãã¯å°ãªããããããã®ã§ãã pytest- cov ã®å°å
¥ pytestã«ã¯ã Coverage.py ã䜿ã£ãŠãã¹ã ã«ãã¬ããž ããšãã ãã©ã°ã€ã³ ãããã®ã§ããããå°å
¥ããŸãããã以äžã®ã³ãã³ããå®è¡ããŸãã uv add pytest- cov --dev ããã§pytestãå®è¡ããéã« ã«ãã¬ããž ãååŸãããªãã·ã§ã³ã䜿ããŸããäŸãã°ã以äžã®ã³ãã³ãã§ ã«ãã¬ããž ãååŸã§ããŸãã pytest -- cov =. -- cov -report html ãã®ã³ãã³ããå®è¡åŸã¯ãããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªã« htmlcov ãšãã ãã£ã¬ã¯ã ãªãäœæãããŠããã®äžã«htmlã§äœæããã ã«ãã¬ããž ã¬ããŒããæ ŒçŽãããŠããŸãã HTMLã§ãããªãã«ããããªè¡šç€ºããããŠããŠããœãããŠã§ã¢éçºãäž»ããæ¥åã§ãªã人ã«ãšã£ãŠã¯ååãªã¬ããŒãã ãšèšããŸãã ãã¹ã ã«ãã¬ããž ã®åŸ®èª¿æŽ ãã¹ã ã«ãã¬ããž ãåãå§ãããšã现ãã調æŽãå¿
èŠã«ãªã£ãŠãããŸãã ç§èªèº«ãããã»ã© Python ã«è©³ããããã§ã¯ãããŸãããã宿¡ä»¶ã§ã®å©çšãéããŠçºçãã埮調æŽãããã€ãã玹ä»ããŸãã å®è¡æãªãã·ã§ã³ã®èª¿æŽ pyproject.tomlã« [tool.coverage.run] ãšããé
ç®ã远å ããŸãã [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" èšå®é
ç®ã¯åã€ã§ãã branch ãæå¹åããããšã§ãã©ã³ã ã«ãã¬ããž ãååŸããããã«ããŠããŸããããã«ãã£ãŠ ã«ãã¬ããž ã®èšæž¬ã³ã¹ããè¥å¹²äžãããŸãã source ã« ã«ãã¬ããž ã®èšæž¬å¯Ÿè±¡ãšãªããã¡ã€ã«ãæ ŒçŽãããŠãã ãã£ã¬ã¯ã ãªãæå®ããŠããŸãã omit ã§ã¯ãéã« ã«ãã¬ããž ã®èšæž¬å¯Ÿè±¡ãšããªããã¡ã€ã«ãæ ŒçŽãããŠãã ãã£ã¬ã¯ã ãªãæå®ããŠããŸãã data_file ã§ã¯ã ã«ãã¬ããž èšæž¬ããŒã¿ã®ãã€ããªãã¡ã€ã«ãæ ŒçŽãã ãã£ã¬ã¯ã ãªããããžã§ã¯ãã®ã«ãŒã ãã£ã¬ã¯ã ãªãã.pytest_cache ãã£ã¬ã¯ã ãªå
ã«ç§»åããããšã§ãæ®æ®µã¯ãã®ååšãæ°ã«ããªãã§æžãããã«ããŠããŸãã ã«ãã¬ããž é€å€ã®èª¿æŽ ããã§ã¯ãpyproject.tomlã« [tool.coverage.report] ãšããé
ç®ã远å ããŠçްç²åºŠã®æ§æã¬ãã«ã§ ã«ãã¬ããž ååŸå¯Ÿè±¡ãã ãœãŒã¹ã³ãŒã ãé€å€ããŠããŸãã [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] ããããã®è©³çްã«ã€ããŠã¯ãããã§ã¯èª¬æããŸããã VS Code ã« ã«ãã¬ããž ã¬ããŒããçµ±åãã ã³ãŒããæžããŠãã ããã°ã©ã㌠ãšããŠã¯ã ã«ãã¬ããž ã¬ããŒãã¯æ®æ®µäœ¿ã£ãŠãããšãã£ã¿äžã«è¡šç€ºãããŠã»ãããã®ã§ããã¬ããŒããèŠãããã ãã«ãŠã£ã³ããŠãåãæ¿ããã®ã¯ããããããã§ããããã ãšããããã§ãæ¡åŒµã®å°å
¥ãšèšå®ã§ããã³ãŒã ã«ãã¬ããž ã衚瀺ãã VS Code æ¡åŒµã¯ããã€ãããã®ã§ãããç§ã詊ããç¯å²å
ã§ã¯ã Coverage Gutters ãæãæåŸ
éãã«åäœããŸããã Coverage Guttersã¯ãCoverage.pyãåºåãã XML ã®ã¬ããŒããå
¥åæ
å ±ãšããŠäœ¿ããŸãã®ã§ãpyproject.tomlã«ã¬ããŒãã®åºåå
ãèšå®ããŸãã [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] [tool.coverage.xml] output = ".pytest_cache/coverage.xml" æåŸã®äºè¡ã远å ããé
ç®ã§ããããã«ãã£ãŠãåºåã¬ããŒãã®ã¿ã€ããšã㊠XML ãæå®ããéã«ã.pytest_cache/coverage. xml ãžãã¡ã€ã«ãåºåãããŸãã æ¬¡ã¯ãdevcontainer. json ãä¿®æ£ããŸãã { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " python.testing.pytestArgs ": [ " tests ", " --capture=tee-sys ", " -vv " ] , " python.testing.pytestEnabled ": true , " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " coverage-gutters.showLineCoverage ": true , " coverage-gutters.showRulerCoverage ": true , " coverage-gutters.coverageFileNames ": [ " .pytest_cache/coverage.xml " ] , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff ", " ryanluker.vscode-coverage-gutters " ] } } } ããã§ã¯ãäžã€ã®èšå®é
ç®ã远å ãããšå
±ã«ã€ã³ã¹ããŒã«ããæ¡åŒµãšã㊠ryanluker.vscode-coverage-gutters ã远å ããŠããŸãã coverage-gutters.showLineCoverage ãæå¹åããããšã§ãè¡å
šäœã«è²ãä»ãããã«ãªããŸãã coverage-gutters.showRulerCoverage ãæå¹åããããšã§ãè¡ã®å·ŠåŽã«ããç®çãéšåã«è²ãä»ãããã«ãªããŸãã coverage-gutters.coverageFileNames ã«èšå®ããŠãããã¹ãšãpyproject.tomlãäžèŽããŠããããšã§ã ã«ãã¬ããž ã¬ããŒãã®çµæã ryanluker.vscode-coverage-gutters ãæ£ããåŠçã§ããŸãã devcontainer. json ãæžãæããã®ã§Dev Containerããªãã«ãããŸãããã VS Code ã«ããã ã«ãã¬ããž ã¬ããŒãã®ç¢ºèªæ¹æ³ ãŸãã¯ã以äžã®ã³ãã³ããå®è¡ã㊠ã«ãã¬ããž ã¬ããŒããäœæããŸãã pytest -- cov =. -- cov -report xml çžå€ããããã¹ãã¯å€±æããŠããŸããããããã .pytest_cache/coverage.xml ãšãããã¡ã€ã«ã¯åºåãããŠããã¯ãã§ããããã䜿ã£ãŠ ã«ãã¬ããž ã確èªããŠãããŸãããã VS Code ã®å·Šäžãããã«æ³šç®ããŠãã ããã ãWatch ãšãã衚瀺ãããã¯ãã§ãããããã¯ãªãã¯ãããš ã«ãã¬ããž ã¬ããŒãã®è¡šç€ºãæå¹åãããŸãã ã«ãã¬ããž ã¬ããŒãããšãã£ã¿äžã«è¡šç€ºããããšãã®ããã«ãªããŸãã ããã§ã ã«ãã¬ããž ã¬ããŒãã確èªããªããã³ãŒããæžããããã«ãªããŸããã ãããããã®èšäºãçµç€ã«å·®ãæãã£ãŠããŠããŸããããå°ãã ããä»ãåããã ããã ã¿ ã¹ã¯ã©ã³ ããŒã®å°å
¥ æåŸã¯ããããžã§ã¯ãæ§æãã¡ã€ã«ã§ããpyproject.tomlã«å®ååãããäœæ¥ãã¿ã¹ã¯ãšããŠèšè¿°ããæ¹æ³ã説æããŸãã é·ãã³ãã³ãã§ãæ§æãã¡ã€ã«å
ã«æžããŠããã°ã³ãã³ãã®å®è¡ãã¹ã¯ç¡ããªããŸãããŸãã GitHub Actionsã®ãããªCIã¯ãŒã¯ãããŒãæ§æããéã«ãããã§ã«ã¿ã¹ã¯ãå®çŸ©ãããŠããã°éåžžã«å°ãªã å·¥æ° ã§å®çŸã§ããŸãã æ§ã
ãªã¿ã¹ã¯å®çŸ©ããŒã«ããããŸãããç§ãæ°ã«å
¥ã£ãŠäœ¿ã£ãŠããã®ã¯ã Poe the Poet ã§ãã以äžã®ã³ãã³ããå®è¡ããŠã€ã³ã¹ããŒã«ããŸãããã uv add poethepoet --dev ã€ã³ã¹ããŒã«ãçµãã£ãããpyproject.tomlã«ãããŸã§æ§æããŠããã¿ã¹ã¯ãããã€ãæžããŠã¿ãŸãããã [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "poethepoet>=0.29.0", "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] [tool.coverage.xml] output = ".pytest_cache/coverage.xml" [tool.poe.tasks] lint = "ruff check ." test = "pytest" cover = "pytest --cov=. --cov-report xml" fmt = "ruff format . --check" build = ["fmt", "lint", "test"] [tool.poe.tasks] 以äžã«æžãããŠãããã®ãå®è¡å¯èœãªã¿ã¹ã¯ã§ããããã§ã¯ãäºã€ã®ã¿ã¹ã¯ãå®çŸ©ããŠããŸãã lint ã¿ã¹ã¯ã§ã¯ã Ruffã«ããã³ãŒãã®æ€æ»ãå®è¡ããŸãã testã¿ã¹ã¯ã§ã¯ãpytestãå®è¡ããŸãã coverã¿ã¹ã¯ã§ã¯ãã³ãŒã ã«ãã¬ããž ãååŸããªããpytestãå®è¡ããŸãã fmtã¿ã¹ã¯ã§ã¯ãRuffã«ããã³ãŒãã®æŽåœ¢ãå®è¡ããŸãã buildã¿ã¹ã¯ã§ã¯ã fmtã¿ã¹ã¯ãå®è¡ããåŸã«lintã¿ã¹ã¯ãå®è¡ãæåŸã«testã¿ã¹ã¯ãå®è¡ããŸãã ãããã®ã¿ã¹ã¯ã¯ã poe ã³ãã³ãããå®è¡ã§ããŸããäŸãã°ãlintã¿ã¹ã¯ãå®è¡ããéã«ã¯ã以äžã®ãããªã³ãã³ããå®è¡ããŸãã poe lint éåžžã«ç°¡åã§ãããtestã¿ã¹ã¯ã®ããã«å
éšçã«ã¯ã³ãã³ããåŒæ°ãªãã§å®è¡ããŠããã ãã§ãã¿ã¹ã¯ãšããŠå®çŸ©ããŠããã®ã¯ãããŒã«ã®ç§»è¡ã³ã¹ããäžããããã§ãã äŸãã°ãä»åã¯linterãšããŠRuffã䜿ã£ãŠããŸããã䜿ã蟌ãéçšã§åé¿ãããã®ãªãäžå
·åãèŠã€ããFlake8ã®ãããªå®çžŸã®ããããŒã«ã«åãæ»ãããšã¯ããããŸãããããã£ãæã«CIãCDã®ã¯ãŒã¯ãããŒã«å¯Ÿãã圱é¿ãã§ãããããæžãããŠç§»è¡ã§ããããã«ããã«ã¯ãã¿ ã¹ã¯ã©ã³ ããŒã«ããã³ãã³ãã®æœè±¡åãæå¹ã§ãã uvã§ã®ã¿ ã¹ã¯ã©ã³ ã㌠poethepoetã®ãããªè¿œå ã®ã©ã€ãã©ãªç¡ãã«uvã ãã§åäœããã¿ ã¹ã¯ã©ã³ ããŒã«ã€ããŠè°è«ããŠãããã±ããããããŸãã Using uv run as a task runner ãããå®è£
ãããã°ãpoethepoet ã®ã€ã³ã¹ããŒã«ã¯äžèŠã«ãªããããããŸãããã ãŸãšã ãããŸã§ãDev Containerã§ Python ã¢ããªã±ãŒã·ã§ã³ã®éçºç°å¢ãæ§ç¯ããæ¹æ³ã«ã€ããŠèª¬æããŠããŸããã ãããžã§ã¯ãã¡ã³ããŒãã§ãããããåãç°å¢ã§ã¢ããªã±ãŒã·ã§ã³éçºãè¡ãããšã¯ã浪費ããã å·¥æ° ãæžãããããžã§ã¯ããšããŠäŸ¡å€ã®ããäœæ¥ã«éäžããããã«å¿
èŠãªäºã§ãã ãã®èšäºã§ã¯ Python éçºç°å¢ãæ±ããŸããããåæ§ã®èãæ¹ã§TypeScriptããRustã Ruby ã Java ãšãã£ãä»ã®èšèªã®éçºç°å¢ãæ§ç¯ã§ããŸãã ãŸãã GitHub Codespacesã®ãã㪠ã¯ã©ãŠã éçºç°å¢ã®å©çšãé£ãªãã§ããŸãã äœã£ãŠããã¢ããªã±ãŒã·ã§ã³ã®çš®é¡ãããããžã§ã¯ãããŒã å
ã§ã®åœ¹å²æ¬¡ç¬¬ã§ã¯ãã©ãŠã¶äžã€åãç°å¢ããããã°ãœãããŠã§ã¢éçºã§ãããšããã®ã¯ãéåžžã«é
åçã§ãããã ãããŸã§èªãã§ããã ããçãããæ¬åœã«ãç²ãããŸã§ããããã®èšäºãèªãã çããããå©äŸ¿æ§ã®é«ãéçºç°å¢ã§æ¬æ¥ã®ãœãããŠã§ã¢éçºã«éäžã§ããããšãé¡ã£ãŠèšäºã®çµã³ãšããŸãã ãã®èšäºã§ç޹ä»ããŠããéçºç°å¢ã®æ§æãã¡ã€ã« æåŸã«äœã£ãæ§æãã¡ã€ã«ããŸãšããŠç޹ä»ããŸãã .devcontainer/devcontainer. json { " name ": " devcontainer-python ", " image ": " mcr.microsoft.com/devcontainers/python:3.12-bookworm ", " containerEnv ": { " TZ ": " Asia/Tokyo " } , " runArgs ": [ " --init " ] , " features ": { " ghcr.io/jsburckhardt/devcontainer-features/uv:1 ": {} } , " postCreateCommand ": " ./.devcontainer/postCreateCommand.sh ", " mounts ": [ " source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume " ] , " customizations ": { " vscode ": { " settings ": { " editor.renderWhitespace ": " all ", " python.defaultInterpreterPath ": " .venv/bin/python ", " python.testing.pytestArgs ": [ " tests ", " --capture=tee-sys ", " -vv " ] , " python.testing.pytestEnabled ": true , " [python] ": { " editor.defaultFormatter ": " charliermarsh.ruff ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit ", " source.organizeImports ": " explicit " } } , " coverage-gutters.showLineCoverage ": true , " coverage-gutters.showRulerCoverage ": true , " coverage-gutters.coverageFileNames ": [ " .pytest_cache/coverage.xml " ] , " [json][jsonc] ": { " editor.defaultFormatter ": " esbenp.prettier-vscode ", " editor.formatOnSave ": true , " editor.codeActionsOnSave ": { " source.fixAll ": " explicit " } } } , " extensions ": [ " esbenp.prettier-vscode ", " ms-python.python ", " njpwerner.autodocstring ", " KevinRose.vsc-python-indent ", " charliermarsh.ruff ", " ryanluker.vscode-coverage-gutters " ] } } } .devcontainer/postCreateCommand.sh æ¹è¡ã³ãŒãã¯LFã§ããå®è¡æš©éã®ä»äžãå¿ããã«ã #!/bin/sh # postCreateCommand.sh echo "START Install" sudo chown -R vscode:vscode . uv venv --allow-existing uv sync echo "FINISH Install" .gitignore æ¹è¡ã³ãŒãã¯LFã§ãã # Python-generated files __pycache__/ *.py[oc] build/ dist/ wheels/ *.egg-info # Virtual environments .venv *_cache pyproject.toml [project] name = "devcontainer-python" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [dependency-groups] dev = [ "poethepoet>=0.29.0", "pytest-cov>=5.0.0", "pytest>=8.3.3", "ruff>=0.7.1", ] [tool.ruff] line-length = 200 [tool.coverage.run] branch = true source = ["tests"] omit = ["tests/fixtures/*"] data_file = ".pytest_cache/.coverage" [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "def __str__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] [tool.coverage.xml] output = ".pytest_cache/coverage.xml" [tool.poe.tasks] lint = "ruff check ." test = "pytest" cover = "pytest --cov=. --cov-report xml" fmt = "ruff format . --check" build = ["fmt", "lint", "test"] å·çïŒ @sato.taichi ãã¬ãã¥ãŒïŒ @mizuno.kazuhiro ïŒ Shodo ã§å·çãããŸãã ïŒ