Skip to content

Commit d7be4f9

Browse files
committed
add utils
1 parent 61fa0aa commit d7be4f9

10 files changed

Lines changed: 211 additions & 0 deletions

File tree

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies = [
2525
"aiohttp>=3.9.5",
2626
"tenacity>=8.4.1",
2727
"playwright>=1.44.0",
28+
"toml>=0.10.2",
2829
]
2930

3031
[tool.hatch.build.targets.wheel]
@@ -40,6 +41,8 @@ dev-dependencies = [
4041
"pytest>=8.3.3",
4142
"ruff>=0.6.9",
4243
"tox>=4.21.2",
44+
"types-pyyaml>=6.0.12.20250402",
45+
"types-toml>=0.10.8.20240310",
4346
]
4447

4548
[tool.ruff]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import asyncio
2+
from typing import Any, Callable
3+
4+
5+
def sync_to_async_func(sync_func: Callable) -> Callable:
6+
"""
7+
同期関数を非同期関数として使えるように変換する
8+
"""
9+
10+
async def wrapper(*args: Any, **kwargs: Any) -> Any:
11+
return await asyncio.to_thread(sync_func, *args, **kwargs)
12+
13+
wrapper.__name__ = sync_func.__name__
14+
wrapper.__doc__ = sync_func.__doc__
15+
return wrapper
16+
17+
18+
def async_to_sync_func(async_func: Callable) -> Callable:
19+
"""
20+
非同期関数を同期関数として使えるように変換する
21+
"""
22+
23+
def wrapper(*args: Any, **kwargs: Any) -> Any:
24+
return asyncio.run(async_func(*args, **kwargs))
25+
26+
wrapper.__name__ = async_func.__name__
27+
wrapper.__doc__ = async_func.__doc__
28+
return wrapper
29+
30+
31+
async def run_async_function_with_semaphore(
32+
async_func: Callable, concurrency_sema: asyncio.Semaphore | None, *args: Any, **kwargs: Any
33+
) -> Any:
34+
"""
35+
指定した関数 func を、セマフォで同時実行数を制限して呼び出す関数。
36+
concurrency_sema が None の場合は制限しない。
37+
"""
38+
if concurrency_sema is not None:
39+
async with concurrency_sema:
40+
return await async_func(*args, **kwargs)
41+
else:
42+
return await async_func(*args, **kwargs)

src/project/common/utils/file/__init__.py

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from pathlib import Path
2+
from typing import Any
3+
4+
from project.common.utils.file.json import load_json
5+
from project.common.utils.file.toml import load_toml
6+
from project.common.utils.file.yaml import load_yaml
7+
8+
9+
def load_config(path: str | Path) -> dict[str, Any]:
10+
"""
11+
Load configuration from a file (JSON, YAML, or TOML).
12+
"""
13+
ext = Path(path).suffix.lower()
14+
if ext == '.json':
15+
return load_json(path)
16+
elif ext in ('.yaml', '.yml'):
17+
return load_yaml(path)
18+
elif ext == '.toml':
19+
return load_toml(path)
20+
else:
21+
raise ValueError(f'Unsupported config file format: {ext}')
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import json
2+
from pathlib import Path
3+
4+
5+
def load_json(path: str | Path) -> dict | list:
6+
with Path(path).open(mode='r', encoding='utf-8') as fin:
7+
data = json.load(fin)
8+
return data
9+
10+
11+
def save_as_indented_json(
12+
data: dict | list,
13+
path: str | Path,
14+
parents: bool = True,
15+
exist_ok: bool = True,
16+
) -> None:
17+
path = Path(path)
18+
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
19+
with path.open(mode='w', encoding='utf-8') as fout:
20+
json.dump(data, fout, ensure_ascii=False, indent=4, separators=(',', ': '))
21+
return
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pathlib import Path
2+
3+
import jsonlines
4+
5+
6+
def load_jsonlines(path: str | Path) -> list[dict]:
7+
data_list = []
8+
with jsonlines.open(str(path)) as reader:
9+
for data in reader:
10+
data_list.append(data)
11+
return data_list
12+
13+
14+
def save_as_jsonlines(
15+
data: list[dict],
16+
path: str | Path,
17+
parents: bool = True,
18+
exist_ok: bool = True,
19+
) -> None:
20+
path = Path(path)
21+
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
22+
with jsonlines.open(str(path), mode='w') as writer:
23+
for datum in data:
24+
writer.write(datum)
25+
return
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from pathlib import Path
2+
3+
import toml
4+
5+
6+
def load_toml(path: str | Path) -> dict:
7+
with Path(path).open(mode='r', encoding='utf-8') as fin:
8+
data = toml.load(fin)
9+
return data
10+
11+
12+
def save_as_toml(
13+
data: dict,
14+
path: str | Path,
15+
parents: bool = True,
16+
exist_ok: bool = True,
17+
) -> None:
18+
path = Path(path)
19+
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
20+
with path.open(mode='w', encoding='utf-8') as fout:
21+
toml.dump(data, fout)
22+
return
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from pathlib import Path
2+
3+
import yaml
4+
5+
6+
def load_yaml(path: str | Path) -> dict | list:
7+
with Path(path).open(mode='r', encoding='utf-8') as fin:
8+
data = yaml.safe_load(fin)
9+
return data
10+
11+
12+
def save_as_indented_yaml(
13+
data: dict | list,
14+
path: str | Path,
15+
parents: bool = True,
16+
exist_ok: bool = True,
17+
) -> None:
18+
path = Path(path)
19+
path.parent.mkdir(parents=parents, exist_ok=exist_ok)
20+
with path.open(mode='w', encoding='utf-8') as fout:
21+
yaml.dump(data, fout, allow_unicode=True, indent=4, default_flow_style=False)
22+
return
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import importlib
2+
import inspect
3+
import sys
4+
from pathlib import Path
5+
from typing import Callable
6+
7+
8+
def import_function(function_file_path: str, function_name: str | None = None) -> Callable:
9+
function_file_path = Path(function_file_path).resolve()
10+
function_name = function_name or function_file_path.stem
11+
12+
project_root = Path.cwd()
13+
if str(project_root) not in sys.path:
14+
sys.path.append(str(project_root))
15+
16+
module_path = '.'.join(function_file_path.relative_to(project_root).with_suffix('').parts)
17+
module = importlib.import_module(module_path)
18+
return getattr(module, function_name)
19+
20+
21+
def get_imported_function_path(obj: Callable) -> str:
22+
return inspect.getfile(obj)

uv.lock

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)