Skip to content

Commit

Permalink
Merge pull request #1 from tamtam-fitness/init
Browse files Browse the repository at this point in the history
Init setting
  • Loading branch information
tamtam-fitness authored Aug 1, 2023
2 parents 1935309 + b59a50b commit 1dabe66
Show file tree
Hide file tree
Showing 24 changed files with 1,119 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[report]
skip_covered = True

omit =
src/main.py
src/common/config.py
19 changes: 19 additions & 0 deletions .github/workflows/py-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: py-ci

on:
push:

jobs:
py-ci:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: setup up
run: docker compose up -d
- name: make lint
run: make lint
- name: make format
run: make format
- name: run tests
run: make test
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM python:3.10-alpine
RUN pip install poetry --without dev
COPY pyproject.toml poetry.lock ./app/
WORKDIR /app
RUN poetry export --without-hashes --output requirements.txt
RUN pip install -r requirements.txt
COPY . ./app/
6 changes: 6 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM python:3.10-buster

COPY ./ ./app/
WORKDIR /app
RUN pip install poetry
RUN poetry install
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.PHONY: setup, enter_container, test, lint, format

setup:
docker compose down
docker compose up -d --build

enter_container:
docker exec -it python_app bash

test:
docker exec python_app python3 -m poetry run pytest tests --cov=/var/task --cov-report term-missing

lint:
docker exec python_app poetry run mypy --explicit-package-bases .
docker exec python_app poetry run ruff check .
docker exec python_app poetry run black --check .

format:
docker exec python_app poetry run ruff check . --fix --exit-non-zero-on-fix
docker exec python_app poetry run black .
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
# python-template-based-on-docker
# python-template-based-on-docker

## Prerequisites

- Tool to run Docker like Docker Desktop
- I highly recommend to use [OrbStack](https://github.com/orbstack/orbstack)

## Apply template to your project
```
git clone https://github.com/tamtam-fitness/python-template-based-on-docker.git <new-project>
cd <new-project>
rm -rf .git
```

## Run Container

To start development, you are supposed to run the following command:
```bash
make setup
```

## Development Commands

### Enter into container
```bash
make enter_container
```

### Lint

```bash
make lint
```
### Format

```bash
make format
```

### Test

If you want to run all tests, you can run the following command:
```bash
make test
```

If you want to run the specific test, you can run the following command:
```bash
make enter_container

poetry shell

poe test tests/{file or directory you want to test}
```
Empty file added __init__.py
Empty file.
16 changes: 16 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3'

services:
python_app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- ./:/app
container_name: python_app
working_dir: /app
environment:
- PYTHONPATH=/app
- ENV=local
tty: true
stdin_open: true
55 changes: 55 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[mypy]
# ref: https://github.com/takashi-yoneya/fastapi-mybest-template/blob/master/mypy.ini
python_version = 3.10
platform = linux
plugins = pydantic.mypy

# Import discovery
namespace_packages=True
ignore_missing_imports = True
# follow_imports=error
# follow_imports_for_stubs=True
no_site_packages = True
no_silence_site_packages=True

# Disallow dynamic typing
# disallow_any_unimported=True
# disallow_any_expr=True
# disallow_any_decorated=True
# disallow_any_explicit=True
disallow_any_generics=True
# disallow_subclassing_any=True

# Untyped definitions and calls
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
# disallow_untyped_decorators = True

# None and Optional handling
no_implicit_optional = True

# Configuring warnings
warn_redundant_casts = True
warn_unused_ignores = True
# warn_return_any = True
warn_unreachable = True

# Miscellaneous strictness flags
strict_equality = True

# Configuring error messages
show_error_context = True
show_column_numbers = True
show_error_codes = True
# pretty = True

# Miscellaneous
warn_unused_configs = True

[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True
warn_untyped_fields = True
738 changes: 738 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
[tool.poetry]
name = "python-template-based-on-docker"
version = "0.1.0"
description = "python template based on docker"
authors = ["your_name"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
pyyaml = "^6.0"
# v2 ref: https://docs.pydantic.dev/latest/migration/
pydantic = "^2.1.1"
pydantic-settings = "^2.0.2"
python-json-logger = "^2.0.7"
sentry-sdk = "^1.29.0"

[tool.poetry.group.dev.dependencies]
black = "^23.3.0"
pytest = "^7.3.1"
pytest-cov = "^4.0.0"
pytest-mock = "^3.10.0"
mypy = "^1.2.0"
ruff = "^0.0.265"
pytest-env = "^0.8.1"
poethepoet = "^0.21.1"

[tool.poe.tasks]
test = "pytest $1 --cov=/app --cov-report term-missing"

[tool.ruff]
# Same as Black.
select = ["ALL"]
ignore = [
"G004", # `logging-f-string`
"PLC1901", # compare-to-empty-string
"PLR2004", # magic-value-comparison
"ANN101", # missing-type-self
"ANN102", # missing-type-cls
"ANN002", # missing-type-args
"ANN003", # missing-type-kwargs
"ANN401", # any-type
"ERA", # commented-out-code
"ARG002", # unused-method-argument
"INP001", # implicit-namespace-package
"PGH004", # blanket-noqa
"B008", # Dependsで使用するため
"A002", # builtin-argument-shadowing
"A003", # builtin-attribute-shadowing
"PLR0913", # too-many-arguments
"RSE", # flake8-raise
"D", # pydocstyle
"C90", # mccabe
"T20", # flake8-print
"SLF", # flake8-self
"BLE", # flake8-blind-except
"FBT", # flake8-boolean-trap
"TRY", # tryceratops
"COM", # flake8-commas
"S", # flake8-bandit
"EM",#flake8-errmsg
"EXE", # flake8-executable
"ICN", # flake8-import-conventions
"RET",#flake8-return
"SIM",#flake8-simplify
"TCH", # flake8-type-checking
"PTH", #pathlibを使わないコードが多いので、除外する
"ISC", #flake8-implicit-str-concat
"N", # pep8-naming
"PT", # flake8-pytest-style
"PGH003"
]

line-length = 115

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

# Assume Python 3.10.
target-version = "py310"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
env =
ENV=test
Empty file added src/__init__.py
Empty file.
12 changes: 12 additions & 0 deletions src/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import logging
import os

from .config import read_yaml
from .logger import init_logger

env = os.environ["ENV"]
settings = read_yaml(f"{os.getcwd()}/src/common/yaml_configs/{env}.yaml")

# loggerの初期化
init_logger(settings.LOGGER_CONFIG_PATH)
logger = logging.getLogger(__name__)
21 changes: 21 additions & 0 deletions src/common/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os

import yaml
from pydantic import HttpUrl
from pydantic_settings import BaseSettings


class Settings(BaseSettings):
SENTRY_DSN: HttpUrl | None = None
ENV: str | None = None
LOGGER_CONFIG_PATH: str = f"{os.getcwd()}/src/common/logger/logging_config.yaml"

class Config:
case_sensitive = True


def read_yaml(file_path: str) -> Settings:
with open(file_path) as stream:
config = yaml.safe_load(stream)

return Settings(**config)
3 changes: 3 additions & 0 deletions src/common/logger/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .logger import init_logger

__all__ = ["init_logger"]
9 changes: 9 additions & 0 deletions src/common/logger/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from logging.config import dictConfig

import yaml


def init_logger(filepath: str) -> None:
with open(filepath) as f:
config = yaml.safe_load(f)
dictConfig(config)
26 changes: 26 additions & 0 deletions src/common/logger/logging_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: 1
disable_existing_loggers: false

formatters:
json:
format: "%(asctime)s %(name)s %(levelname)s %(message)s %(filename)s %(module)s %(funcName)s %(lineno)d"
class: pythonjsonlogger.jsonlogger.JsonFormatter
normal:
format: "[%(asctime)s - %(levelname)s - %(filename)s(func:%(funcName)s, line:%(lineno)d)] %(message)s"

handlers:
console:
class: logging.StreamHandler
level: INFO
formatter: normal # json形式にしたい場合はjsonと記述する
stream: ext://sys.stdout

loggers:
src:
level: INFO
handlers: [console]
propagate: false

root:
level: INFO
handlers: [console]
2 changes: 2 additions & 0 deletions src/common/yaml_configs/local.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ENV: "local"
SENTRY_DSN:
2 changes: 2 additions & 0 deletions src/common/yaml_configs/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ENV: 'test'
SENTRY_DSN:
19 changes: 19 additions & 0 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import logging

import sentry_sdk
from common import settings
from sentry_sdk.integrations.logging import LoggingIntegration

from src.module.temp_class import TempClass

sentry_logging = LoggingIntegration(level=logging.INFO, event_level=logging.ERROR)
if settings.ENV not in ["local", "test"]:
sentry_sdk.init(
dsn=settings.SENTRY_DSN,
integrations=[sentry_logging],
environment=settings.ENV,
traces_sample_rate=1.0,
)

if __name__ == "__main__":
print(TempClass().add_temp("hello_world_"))
Empty file added src/module/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions src/module/temp_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# please delete module dir because of meaningless code
from src.common import logger


class TempClass:
def add_temp(self, input_str: str) -> str:
logger.info("temp")
return input_str + "temp"
Loading

0 comments on commit 1dabe66

Please sign in to comment.