Skip to content

Instantly share code, notes, and snippets.

@taichi
Last active April 20, 2023 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taichi/83bdfbeeceb80ebe26c66f5166023fa7 to your computer and use it in GitHub Desktop.
Save taichi/83bdfbeeceb80ebe26c66f5166023fa7 to your computer and use it in GitHub Desktop.
Modern Python development environment on top of Dev Container
{
"name": "python3",
"image": "mcr.microsoft.com/devcontainers/python:3.11-bullseye",
"containerEnv": {
"TZ": "Asia/Tokyo"
},
"features": {
"ghcr.io/devcontainers-contrib/features/poetry:2": {}
},
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"workspaceFolder": "/workspace",
"mounts": [
"source=venv-${devcontainerId},target=${containerWorkspaceFolder}/.venv,type=volume"
],
"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
"forwardPorts": [8000, 8080],
"customizations": {
"vscode": {
"settings": {
"python.defaultInterpreterPath": ".venv/bin/python",
"[python]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
}
},
"python.formatting.provider": "black",
"python.testing.pytestArgs": ["tests"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"evenBetterToml.schema.associations": {
"pyproject.toml": ".vscode/pyproject.schema.json"
},
"coverage-gutters.showLineCoverage": true,
"coverage-gutters.showRulerCoverage": true,
"coverage-gutters.coverageFileNames": [".pytest_cache/coverage.xml"]
},
"extensions": [
"ms-python.python",
"njpwerner.autodocstring",
"KevinRose.vsc-python-indent",
"ms-azuretools.vscode-docker",
"tamasfe.even-better-toml",
"charliermarsh.ruff",
"esbenp.prettier-vscode",
"ryanluker.vscode-coverage-gutters"
]
}
},
"runArgs": ["--init"]
}
#!/bin/sh
# postCreateCommand.sh
echo "START Install"
sudo chown -R vscode:vscode .
echo '\n\
alias ls="ls --color=auto"\n\
alias ll="ls -al --color=auto"\n\
' >> ~/.bashrc
poetry config virtualenvs.in-project true
poetry install
echo "FINISH Install"
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"poetry-authors": {
"type": "array",
"description": "List of authors that contributed to the package. This is typically the main maintainers, not the full list.",
"items": {
"type": "string",
"pattern": "^(?:\\S+?\\s)+?(?:<(?:[a-z\\d!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z\\d!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z\\d](?:[a-z\\d-]*[a-z\\d])?\\.)+[a-z\\d](?:[a-z\\d-]*[a-z\\d])?|\\[(?:(?:25[0-5]|2[0-4][\\d]|[01]?[\\d][\\d]?)\\.){3}(?:25[0-5]|2[0-4][\\d]|[01]?[\\d][\\d]?|[a-z\\d-]*[a-z\\d]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])>)?$"
}
},
"poetry-maintainers": {
"type": "array",
"description": "List of maintainers, other than the original author(s), that upkeep the package.",
"items": {
"type": "string"
}
},
"poetry-dependencies": {
"type": "object",
"patternProperties": {
"^[a-zA-Z-_.0-9]+$": {
"oneOf": [
{
"$ref": "#/definitions/poetry-dependency"
},
{
"$ref": "#/definitions/poetry-long-dependency"
},
{
"$ref": "#/definitions/poetry-git-dependency"
},
{
"$ref": "#/definitions/poetry-file-dependency"
},
{
"$ref": "#/definitions/poetry-path-dependency"
},
{
"$ref": "#/definitions/poetry-url-dependency"
},
{
"$ref": "#/definitions/poetry-multiple-constraints-dependency"
}
]
}
}
},
"poetry-dependency": {
"type": "string",
"description": "The constraint of the dependency.",
"pattern": "v?(?:(?:(?:[0-9]+)!)?(?:[0-9]+(?:\\.[0-9]+)*)(?:[-_\\.]?(?:(a|b|c|rc|alpha|beta|pre|preview))[-_\\.]?(?:[0-9]+)?)?(?:(?:-(?:[0-9]+))|(?:[-_\\.]?(?:post|rev|r)[-_\\.]?(?:[0-9]+)?))?(?:[-_\\.]?(?:dev)[-_\\.]?(?:[0-9]+)?)?)(?:\\+(?:[a-z0-9]+(?:[-_\\.][a-z0-9]+)*))?"
},
"poetry-long-dependency": {
"type": "object",
"required": ["version"],
"additionalProperties": false,
"properties": {
"version": {
"type": "string",
"description": "The constraint of the dependency.",
"pattern": "v?(?:(?:(?:[0-9]+)!)?(?:[0-9]+(?:\\.[0-9]+)*)(?:[-_\\.]?(?:(a|b|c|rc|alpha|beta|pre|preview))[-_\\.]?(?:[0-9]+)?)?(?:(?:-(?:[0-9]+))|(?:[-_\\.]?(?:post|rev|r)[-_\\.]?(?:[0-9]+)?))?(?:[-_\\.]?(?:dev)[-_\\.]?(?:[0-9]+)?)?)(?:\\+(?:[a-z0-9]+(?:[-_\\.][a-z0-9]+)*))?"
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
},
"platform": {
"type": "string",
"description": "The platform(s) for which the dependency should be installed."
},
"markers": {
"type": "string",
"description": "The PEP 508 compliant environment markers for which the dependency should be installed."
},
"allow-prereleases": {
"type": "boolean",
"description": "Whether the dependency allows prereleases or not."
},
"allows-prereleases": {
"type": "boolean",
"description": "Whether the dependency allows prereleases or not."
},
"optional": {
"type": "boolean",
"description": "Whether the dependency is optional or not."
},
"extras": {
"type": "array",
"description": "The required extras for this dependency.",
"items": {
"type": "string"
}
},
"source": {
"type": "string",
"description": "The exclusive source used to search for this dependency."
}
}
},
"poetry-git-dependency": {
"type": "object",
"required": ["git"],
"additionalProperties": false,
"properties": {
"git": {
"type": "string",
"description": "The url of the git repository.",
"format": "uri"
},
"branch": {
"type": "string",
"description": "The branch to checkout."
},
"tag": {
"type": "string",
"description": "The tag to checkout."
},
"rev": {
"type": "string",
"description": "The revision to checkout."
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
},
"platform": {
"type": "string",
"description": "The platform(s) for which the dependency should be installed."
},
"markers": {
"type": "string",
"description": "The PEP 508 compliant environment markers for which the dependency should be installed."
},
"allow-prereleases": {
"type": "boolean",
"description": "Whether the dependency allows prereleases or not."
},
"allows-prereleases": {
"type": "boolean",
"description": "Whether the dependency allows prereleases or not."
},
"optional": {
"type": "boolean",
"description": "Whether the dependency is optional or not."
},
"extras": {
"type": "array",
"description": "The required extras for this dependency.",
"items": {
"type": "string"
}
}
}
},
"poetry-file-dependency": {
"type": "object",
"required": ["file"],
"additionalProperties": false,
"properties": {
"file": {
"type": "string",
"description": "The path to the file."
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
},
"platform": {
"type": "string",
"description": "The platform(s) for which the dependency should be installed."
},
"markers": {
"type": "string",
"description": "The PEP 508 compliant environment markers for which the dependency should be installed."
},
"optional": {
"type": "boolean",
"description": "Whether the dependency is optional or not."
},
"extras": {
"type": "array",
"description": "The required extras for this dependency.",
"items": {
"type": "string"
}
}
}
},
"poetry-path-dependency": {
"type": "object",
"required": ["path"],
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"description": "The path to the dependency."
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
},
"platform": {
"type": "string",
"description": "The platform(s) for which the dependency should be installed."
},
"markers": {
"type": "string",
"description": "The PEP 508 compliant environment markers for which the dependency should be installed."
},
"optional": {
"type": "boolean",
"description": "Whether the dependency is optional or not."
},
"extras": {
"type": "array",
"description": "The required extras for this dependency.",
"items": {
"type": "string"
}
},
"develop": {
"type": "boolean",
"description": "Whether to install the dependency in development mode."
}
}
},
"poetry-url-dependency": {
"type": "object",
"required": ["url"],
"additionalProperties": false,
"properties": {
"url": {
"type": "string",
"description": "The url to the file."
},
"python": {
"type": "string",
"description": "The python versions for which the dependency should be installed."
},
"platform": {
"type": "string",
"description": "The platform(s) for which the dependency should be installed."
},
"markers": {
"type": "string",
"description": "The PEP 508 compliant environment markers for which the dependency should be installed."
},
"optional": {
"type": "boolean",
"description": "Whether the dependency is optional or not."
},
"extras": {
"type": "array",
"description": "The required extras for this dependency.",
"items": {
"type": "string"
}
}
}
},
"poetry-multiple-constraints-dependency": {
"type": "array",
"minItems": 1,
"items": {
"oneOf": [
{
"$ref": "#/definitions/poetry-dependency"
},
{
"$ref": "#/definitions/poetry-long-dependency"
},
{
"$ref": "#/definitions/poetry-git-dependency"
},
{
"$ref": "#/definitions/poetry-file-dependency"
},
{
"$ref": "#/definitions/poetry-path-dependency"
},
{
"$ref": "#/definitions/poetry-url-dependency"
}
]
}
},
"poetry-scripts": {
"type": "object",
"patternProperties": {
"^[a-zA-Z-_.0-9]+$": {
"oneOf": [
{
"$ref": "#/definitions/poetry-script"
},
{
"$ref": "#/definitions/poetry-extra-script"
}
]
}
}
},
"poetry-script": {
"type": "string",
"description": "A simple script pointing to a callable object."
},
"poetry-extra-script": {
"type": "object",
"description": "A script that should be installed only if extras are activated.",
"additionalProperties": false,
"properties": {
"callable": {
"$ref": "#/definitions/poetry-script"
},
"extras": {
"type": "array",
"description": "The required extras for this script.",
"items": {
"type": "string"
}
}
}
},
"poetry-repository": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "The name of the repository"
},
"url": {
"type": "string",
"description": "The url of the repository",
"format": "uri"
},
"default": {
"type": "boolean",
"description": "Make this repository the default (disable PyPI)"
},
"secondary": {
"type": "boolean",
"description": "Declare this repository as secondary, i.e. it will only be looked up last for packages."
}
}
},
"BuildSystem": {
"title": "Build System",
"type": "object",
"evenBetterToml": {
"links": {
"key": "https://www.python.org/dev/peps/pep-0518/#build-system-table"
}
},
"description": "Build-related data.\n",
"required": ["requires"],
"properties": {
"requires": {
"description": "A list of strings representing [PEP 508](https://www.python.org/dev/peps/pep-0508) dependencies required to execute the build system.\n",
"type": "array",
"items": {
"type": "string"
},
"evenBetterToml": {
"links": {
"key": "https://www.python.org/dev/peps/pep-0518/#build-system-table"
}
}
},
"build-backend": {
"description": "The build backend for the package.\n",
"type": "string",
"evenBetterToml": {
"links": {
"key": "https://www.python.org/dev/peps/pep-0517/"
}
}
}
}
}
},
"type": "object",
"properties": {
"build-system": {
"$ref": "#/definitions/BuildSystem"
},
"tool": {
"type": "object",
"description": "A table for tool configurations.\n\nEvery tool that is used by the project can have its own sub-table for its configuration.\n",
"additionalProperties": true,
"evenBetterToml": {
"links": {
"key": "https://www.python.org/dev/peps/pep-0518/#id28"
}
},
"properties": {
"poetry": {
"name": "Package",
"type": "object",
"additionalProperties": false,
"required": ["name", "version", "description"],
"properties": {
"name": {
"type": "string",
"description": "Package name."
},
"version": {
"type": "string",
"description": "Package version.",
"pattern": "v?(?:(?:(?:[0-9]+)!)?(?:[0-9]+(?:\\.[0-9]+)*)(?:[-_\\.]?(?:(a|b|c|rc|alpha|beta|pre|preview))[-_\\.]?(?:[0-9]+)?)?(?:(?:-(?:[0-9]+))|(?:[-_\\.]?(?:post|rev|r)[-_\\.]?(?:[0-9]+)?))?(?:[-_\\.]?(?:dev)[-_\\.]?(?:[0-9]+)?)?)(?:\\+(?:[a-z0-9]+(?:[-_\\.][a-z0-9]+)*))?"
},
"description": {
"type": "string",
"description": "Short package description."
},
"keywords": {
"type": "array",
"items": {
"type": "string",
"description": "A tag/keyword that this package relates to."
}
},
"homepage": {
"type": "string",
"description": "Homepage URL for the project.",
"format": "uri"
},
"repository": {
"type": "string",
"description": "Repository URL for the project.",
"format": "uri"
},
"documentation": {
"type": "string",
"description": "Documentation URL for the project.",
"format": "uri"
},
"license": {
"type": "string",
"description": "License name."
},
"authors": {
"$ref": "#/definitions/poetry-authors"
},
"maintainers": {
"$ref": "#/definitions/poetry-maintainers"
},
"readme": {
"type": "string",
"description": "The path to the README file"
},
"classifiers": {
"type": "array",
"description": "A list of trove classifers."
},
"packages": {
"type": "array",
"description": "A list of packages to include in the final distribution.",
"items": {
"type": "object",
"description": "Information about where the package resides.",
"additionalProperties": false,
"required": ["include"],
"properties": {
"include": {
"type": "string",
"description": "What to include in the package."
},
"from": {
"type": "string",
"description": "Where the source directory of the package resides."
},
"format": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "The format(s) for which the package must be included."
}
}
}
},
"include": {
"type": "array",
"description": "A list of files and folders to include."
},
"exclude": {
"type": "array",
"description": "A list of files and folders to exclude."
},
"dependencies": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that are required to run this package.",
"required": ["python"],
"properties": {
"python": {
"type": "string",
"description": "The Python versions the package is compatible with."
}
},
"$ref": "#/definitions/poetry-dependencies",
"additionalProperties": false
},
"dev-dependencies": {
"type": "object",
"description": "This is a hash of package name (keys) and version constraints (values) that this package requires for developing it (testing tools and such).",
"$ref": "#/definitions/poetry-dependencies",
"additionalProperties": false
},
"extras": {
"type": "object",
"patternProperties": {
"^[a-zA-Z-_.0-9]+$": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"build": {
"type": "string",
"description": "The file used to build extensions."
},
"source": {
"type": "array",
"description": "A set of additional repositories where packages can be found.",
"additionalProperties": {
"$ref": "#/definitions/poetry-repository"
},
"items": {
"$ref": "#/definitions/poetry-repository"
}
},
"scripts": {
"type": "object",
"description": "A hash of scripts to be installed.",
"items": {
"type": "string"
}
},
"plugins": {
"type": "object",
"description": "A hash of hashes representing plugins",
"patternProperties": {
"^[a-zA-Z-_.0-9]+$": {
"type": "object",
"patternProperties": {
"^[a-zA-Z-_.0-9]+$": {
"type": "string"
}
}
}
}
},
"urls": {
"type": "object",
"patternProperties": {
"^.+$": {
"type": "string",
"description": "The full url of the custom url."
}
}
}
}
}
}
}
},
"additionalProperties": true
}
[tool.poetry]
name = "myproject"
version = "0.1.0"
description = ""
authors = ["taichi <ta1@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
[tool.poetry.group.dev.dependencies]
pytest = "^7.3.1"
ruff = "^0.0.261"
black = "^23.3.0"
pytest-cov = "^4.0.0"
poethepoet = "^0.19.0"
[tool.poetry.scripts]
mycmd = "myproject.main:main"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
line-length = 200
[tool.black]
line-length = 200
[tool.pytest.ini_options]
addopts = "--cov=myproject --cov-report xml"
testpaths = ["tests"]
[tool.coverage.run]
branch = true
source = ["myproject", "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 myproject"
test = "pytest"
fmt = "black myproject --check"
build = ["fmt", "lint", "test"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment