Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_poetry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

strategy:
matrix:
python-version: [3.10.11]
python-version: [3.12]

steps:
- name: Checkout repository
Expand Down
31 changes: 17 additions & 14 deletions docker/cpu/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM ubuntu:22.04 AS base
FROM ubuntu:24.04 AS base

ARG PYTHON_VERSION=3.10
ARG PYTHON_VERSION=3.12

ENV DEBIAN_FRONTEND=noninteractive
ENV WORKDIR /app/
Expand Down Expand Up @@ -28,18 +28,21 @@ ENV LANG en_US.UTF-8
ENV LANGUAGE en_US

# install Python
RUN apt-get update && apt-get -yV upgrade && DEBIAN_FRONTEND=noninteractive apt-get -yV install \
build-essential libssl-dev libffi-dev \
python${PYTHON_VERSION} python${PYTHON_VERSION}-distutils python${PYTHON_VERSION}-dev \
&& ln -s /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python3 \
&& ln -s /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

## install pip
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \
&& python3 get-pip.py \
&& pip3 --no-cache-dir install --upgrade pip
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
software-properties-common \
&& add-apt-repository ppa:deadsnakes/ppa -y \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential libssl-dev libffi-dev \
python${PYTHON_VERSION} \
python${PYTHON_VERSION}-dev \
python3-pip \
# python${PYTHON_VERSION}-distutils \ # for python3.12
&& ln -s /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python3 \
&& ln -s /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python \
&& apt-get purge -y --auto-remove software-properties-common \
&& rm -rf /var/lib/apt/lists/*

## install Poetry
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
Expand Down
17 changes: 10 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "project"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.10"
requires-python = ">=3.12"
dependencies = [
"python-dotenv>=1.0.0",
"setuptools>=69.0.3",
Expand All @@ -12,19 +12,19 @@ dependencies = [
"selenium>=4.16.0",
"fastapi>=0.108.0",
"uvicorn>=0.25.0",
"matplotlib>=3.5.1",
"matplotlib>=3.9.0",
"pandas>=1.4.2",
"seaborn>=0.11.2",
"japanize-matplotlib>=1.1.3",
"numpy>=1.22.3",
"jupyterlab>=3.3.4",
"numpy>=2.2.0",
"tqdm>=4.64.0",
"scikit-learn>=1.1.1",
"scikit-learn>=1.6.0",
"openpyxl>=3.1.2",
"polars>=0.20.31",
"aiohttp>=3.9.5",
"tenacity>=8.4.1",
"playwright>=1.44.0",
"toml>=0.10.2",
]

[tool.hatch.build.targets.wheel]
Expand All @@ -33,17 +33,20 @@ packages = ["src/project", "src"]
[tool.uv]
dev-dependencies = [
"jupyterlab>=3.3.4",
"marimo>=0.13.3",
"mypy>=1.11.2",
"nbstripout>=0.7.1",
"pre-commit>=4.0.1",
"pytest>=8.3.3",
"ruff>=0.6.9",
"tox>=4.21.2",
"types-pyyaml>=6.0.12.20250402",
"types-toml>=0.10.8.20240310",
]

[tool.ruff]
line-length = 119
target-version = "py310"
target-version = "py312"
exclude = [".git", ".venv", "__pycache__", "data", "dist", "misc", "notebooks", "prof", "tmp", "workspacea", ".tox"]

[tool.ruff.format]
Expand All @@ -68,7 +71,7 @@ split-on-trailing-comma = true
keep-runtime-typing = true

[tool.mypy]
python_version="3.10"
python_version="3.12"
files = "src"
ignore_missing_imports = true
disallow_untyped_defs = true
Expand Down
42 changes: 42 additions & 0 deletions src/project/common/utils/async_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import asyncio
from typing import Any, Callable


def sync_to_async_func(sync_func: Callable) -> Callable:
"""
同期関数を非同期関数として使えるように変換する
"""

async def wrapper(*args: Any, **kwargs: Any) -> Any:
return await asyncio.to_thread(sync_func, *args, **kwargs)

wrapper.__name__ = sync_func.__name__
wrapper.__doc__ = sync_func.__doc__
return wrapper


def async_to_sync_func(async_func: Callable) -> Callable:
"""
非同期関数を同期関数として使えるように変換する
"""

def wrapper(*args: Any, **kwargs: Any) -> Any:
return asyncio.run(async_func(*args, **kwargs))

wrapper.__name__ = async_func.__name__
wrapper.__doc__ = async_func.__doc__
return wrapper


async def run_async_function_with_semaphore(
async_func: Callable, concurrency_sema: asyncio.Semaphore | None, *args: Any, **kwargs: Any
) -> Any:
"""
指定した関数 func を、セマフォで同時実行数を制限して呼び出す関数。
concurrency_sema が None の場合は制限しない。
"""
if concurrency_sema is not None:
async with concurrency_sema:
return await async_func(*args, **kwargs)
else:
return await async_func(*args, **kwargs)
Empty file.
21 changes: 21 additions & 0 deletions src/project/common/utils/file/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pathlib import Path
from typing import Any

from project.common.utils.file.json import load_json
from project.common.utils.file.toml import load_toml
from project.common.utils.file.yaml import load_yaml


def load_config(path: str | Path) -> dict[str, Any]:
"""
Load configuration from a file (JSON, YAML, or TOML).
"""
ext = Path(path).suffix.lower()
if ext == '.json':
return load_json(path)
elif ext in ('.yaml', '.yml'):
return load_yaml(path)
elif ext == '.toml':
return load_toml(path)
else:
raise ValueError(f'Unsupported config file format: {ext}')
21 changes: 21 additions & 0 deletions src/project/common/utils/file/json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import json
from pathlib import Path


def load_json(path: str | Path) -> dict | list:
with Path(path).open(mode='r', encoding='utf-8') as fin:
data = json.load(fin)
return data


def save_as_indented_json(
data: dict | list,
path: str | Path,
parents: bool = True,
exist_ok: bool = True,
) -> None:
path = Path(path)
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
with path.open(mode='w', encoding='utf-8') as fout:
json.dump(data, fout, ensure_ascii=False, indent=4, separators=(',', ': '))
return
25 changes: 25 additions & 0 deletions src/project/common/utils/file/jsonlines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from pathlib import Path

import jsonlines


def load_jsonlines(path: str | Path) -> list[dict]:
data_list = []
with jsonlines.open(str(path)) as reader:
for data in reader:
data_list.append(data)
return data_list


def save_as_jsonlines(
data: list[dict],
path: str | Path,
parents: bool = True,
exist_ok: bool = True,
) -> None:
path = Path(path)
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
with jsonlines.open(str(path), mode='w') as writer:
for datum in data:
writer.write(datum)
return
22 changes: 22 additions & 0 deletions src/project/common/utils/file/toml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from pathlib import Path

import toml


def load_toml(path: str | Path) -> dict:
with Path(path).open(mode='r', encoding='utf-8') as fin:
data = toml.load(fin)
return data


def save_as_toml(
data: dict,
path: str | Path,
parents: bool = True,
exist_ok: bool = True,
) -> None:
path = Path(path)
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
with path.open(mode='w', encoding='utf-8') as fout:
toml.dump(data, fout)
return
22 changes: 22 additions & 0 deletions src/project/common/utils/file/yaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from pathlib import Path

import yaml


def load_yaml(path: str | Path) -> dict | list:
with Path(path).open(mode='r', encoding='utf-8') as fin:
data = yaml.safe_load(fin)
return data


def save_as_indented_yaml(
data: dict | list,
path: str | Path,
parents: bool = True,
exist_ok: bool = True,
) -> None:
path = Path(path)
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
with path.open(mode='w', encoding='utf-8') as fout:
yaml.dump(data, fout, allow_unicode=True, indent=4, default_flow_style=False)
return
22 changes: 22 additions & 0 deletions src/project/common/utils/import_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import importlib
import inspect
import sys
from pathlib import Path
from typing import Callable


def import_function(function_file_path: str, function_name: str | None = None) -> Callable:
function_file_path = Path(function_file_path).resolve()
function_name = function_name or function_file_path.stem

project_root = Path.cwd()
if str(project_root) not in sys.path:
sys.path.append(str(project_root))

module_path = '.'.join(function_file_path.relative_to(project_root).with_suffix('').parts)
module = importlib.import_module(module_path)
return getattr(module, function_name)


def get_imported_function_path(obj: Callable) -> str:
return inspect.getfile(obj)
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[tox]
envlist = py310, lint
envlist = py312, lint

[gh-actions]
python =
3.10: py310, lint
3.12: py312, lint

[testenv]
allowlist_externals =
Expand Down
Loading