# Funcing - Python向け簡単並列実行ライブラリ
Pythonの標準threadingを超簡略化&エラー安全にした薄型並列実行ツール
## 🚀 なぜFuncing?
Pythonの`threading`モジュールは強力ですが、複雑になりがちです。Funcingは**超簡単**なAPIで並列実行を可能にし、自動エラーハンドリングを提供します。
```python
from funcing import run_in_parallel
def task1():
return "Hello"
def task2():
return "World"
# たったこれだけ!一行で並列実行
result = run_in_parallel([task1, task2])
print(result.results) # ['Hello', 'World']
```
## ✨ 特徴
- **超簡単API**: `run_in_parallel([func1, func2])` だけ
- **エラー安全**: 自動例外処理とレポート機能
- **結果収集**: 全ての結果とエラーを一箇所で管理
- **タイムアウト対応**: 組み込みタイムアウト処理
- **依存関係ゼロ**: Python標準ライブラリのみ使用
- **包括的な統計**: 成功率、実行時間など詳細情報
## 📦 インストール
```bash
pip install funcing
```
## 🔥 クイックスタート
### 基本的な使い方
```python
from funcing import run_in_parallel
def fetch_data():
# 何らかの処理をシミュレート
import time
time.sleep(1)
return "データを取得しました"
def process_data():
import time
time.sleep(1)
return "データを処理しました"
def save_data():
import time
time.sleep(1)
return "データを保存しました"
# 全てのタスクを並列実行
result = run_in_parallel([fetch_data, process_data, save_data])
print(f"成功: {result.success_count}") # 成功: 3
print(f"実行時間: {result.total_time:.2f}秒") # 実行時間: ~1.00秒 (逐次実行なら3.00秒)
print(f"結果: {result.results}") # 結果: ['データを取得しました', 'データを処理しました', 'データを保存しました']
```
### エラーハンドリング
```python
from funcing import run_in_parallel
def working_task():
return "成功!"
def failing_task():
raise ValueError("何かがうまくいきませんでした!")
result = run_in_parallel([working_task, failing_task])
print(f"成功: {result.success_count}") # 成功: 1
print(f"エラー: {result.error_count}") # エラー: 1
print(f"成功率: {result.success_rate:.1f}%") # 成功率: 50.0%
print(f"全て成功: {result.all_successful}") # 全て成功: False
```
### 引数付き関数
```python
from funcing import run_with_args
def add(a, b):
return a + b
def multiply(a, b):
return a * b
def greet(name, greeting="こんにちは"):
return f"{greeting}、{name}さん!"
# 引数付き関数
pairs = [
(add, (1, 2)),
(multiply, (3, 4)),
(greet, ("太郎",), {"greeting": "やあ"})
]
result = run_with_args(pairs)
print(result.results) # [3, 12, 'やあ、太郎さん!']
```
### 高度なオプション
```python
from funcing import run_in_parallel
functions = [task1, task2, task3, task4, task5]
result = run_in_parallel(
functions,
timeout=30.0, # 最大30秒
max_workers=3, # 3つのスレッドのみ使用
return_exceptions=True # エラーを収集(例外を発生させない)
)
print(f"{result.total_time:.2f}秒で完了")
print(f"成功率: {result.success_rate:.1f}%")
```
## 📊 結果オブジェクト
`FuncingResult`オブジェクトは包括的な情報を提供します:
```python
result = run_in_parallel([func1, func2, func3])
# プロパティ
result.results # 成功した結果のリスト
result.errors # 例外のリスト
result.success_count # 成功した関数の数
result.error_count # 失敗した関数の数
result.total_time # 総実行時間
result.function_names # 実行された関数の名前
result.success_rate # 成功率(パーセント)
result.all_successful # エラーが無い場合True
```
## 🎯 使用例
- **Webスクレイピング**: 複数のURLを同時に取得
- **API呼び出し**: 複数のAPIリクエストを並列実行
- **ファイル処理**: 複数のファイルを同時に処理
- **データベース操作**: 独立したクエリを並列実行
- **データ検証**: 複数の入力を同時に検証
## 🛡️ エラー安全性
Funcingはデフォルトでエラー安全に設計されています:
- 個別の関数の失敗が全体の実行をクラッシュさせない
- 元の例外による詳細なエラー報告
- タイムアウト処理でハングを防止
- 自動的なリソースクリーンアップ
## 🔧 高度な機能
### カスタムスレッドプールサイズ
```python
# I/O集約的なタスクには多くのスレッドを使用
result = run_in_parallel(io_functions, max_workers=50)
# CPU集約的なタスクには少ないスレッドを使用
result = run_in_parallel(cpu_functions, max_workers=4)
```
### タイムアウト処理
```python
# 全体の実行にタイムアウトを設定
result = run_in_parallel(functions, timeout=10.0)
if result.errors:
print("いくつかの関数がタイムアウトしました!")
```
### 例外処理モード
```python
# 例外を収集(デフォルト)
result = run_in_parallel(functions, return_exceptions=True)
# 最初の例外で停止
try:
result = run_in_parallel(functions, return_exceptions=False)
except Exception as e:
print(f"実行失敗: {e}")
```
## 📜 ライセンス
MIT License - 詳細はLICENSEファイルをご覧ください。
Raw data
{
"_id": null,
"home_page": "https://github.com/tikipiya/funcing",
"name": "funcing",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "threading parallel async concurrent simple",
"author": "tikisan",
"author_email": "s2501082@sendai-nct.jp",
"download_url": "https://files.pythonhosted.org/packages/5c/35/c9d9191d4b57a94e2be519bdf1c084558d8d5071a518ce70b8d534cc3c4d/funcing-1.0.0.tar.gz",
"platform": null,
"description": "# Funcing - Python\u5411\u3051\u7c21\u5358\u4e26\u5217\u5b9f\u884c\u30e9\u30a4\u30d6\u30e9\u30ea\r\n\r\nPython\u306e\u6a19\u6e96threading\u3092\u8d85\u7c21\u7565\u5316\uff06\u30a8\u30e9\u30fc\u5b89\u5168\u306b\u3057\u305f\u8584\u578b\u4e26\u5217\u5b9f\u884c\u30c4\u30fc\u30eb\r\n\r\n## \ud83d\ude80 \u306a\u305cFuncing\uff1f\r\n\r\nPython\u306e`threading`\u30e2\u30b8\u30e5\u30fc\u30eb\u306f\u5f37\u529b\u3067\u3059\u304c\u3001\u8907\u96d1\u306b\u306a\u308a\u304c\u3061\u3067\u3059\u3002Funcing\u306f**\u8d85\u7c21\u5358**\u306aAPI\u3067\u4e26\u5217\u5b9f\u884c\u3092\u53ef\u80fd\u306b\u3057\u3001\u81ea\u52d5\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002\r\n\r\n```python\r\nfrom funcing import run_in_parallel\r\n\r\ndef task1():\r\n return \"Hello\"\r\n\r\ndef task2():\r\n return \"World\"\r\n\r\n# \u305f\u3063\u305f\u3053\u308c\u3060\u3051\uff01\u4e00\u884c\u3067\u4e26\u5217\u5b9f\u884c\r\nresult = run_in_parallel([task1, task2])\r\nprint(result.results) # ['Hello', 'World']\r\n```\r\n\r\n## \u2728 \u7279\u5fb4\r\n\r\n- **\u8d85\u7c21\u5358API**: `run_in_parallel([func1, func2])` \u3060\u3051\r\n- **\u30a8\u30e9\u30fc\u5b89\u5168**: \u81ea\u52d5\u4f8b\u5916\u51e6\u7406\u3068\u30ec\u30dd\u30fc\u30c8\u6a5f\u80fd\r\n- **\u7d50\u679c\u53ce\u96c6**: \u5168\u3066\u306e\u7d50\u679c\u3068\u30a8\u30e9\u30fc\u3092\u4e00\u7b87\u6240\u3067\u7ba1\u7406\r\n- **\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u5bfe\u5fdc**: \u7d44\u307f\u8fbc\u307f\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u51e6\u7406\r\n- **\u4f9d\u5b58\u95a2\u4fc2\u30bc\u30ed**: Python\u6a19\u6e96\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u307f\u4f7f\u7528\r\n- **\u5305\u62ec\u7684\u306a\u7d71\u8a08**: \u6210\u529f\u7387\u3001\u5b9f\u884c\u6642\u9593\u306a\u3069\u8a73\u7d30\u60c5\u5831\r\n\r\n## \ud83d\udce6 \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\r\n\r\n```bash\r\npip install funcing\r\n```\r\n\r\n## \ud83d\udd25 \u30af\u30a4\u30c3\u30af\u30b9\u30bf\u30fc\u30c8\r\n\r\n### \u57fa\u672c\u7684\u306a\u4f7f\u3044\u65b9\r\n\r\n```python\r\nfrom funcing import run_in_parallel\r\n\r\ndef fetch_data():\r\n # \u4f55\u3089\u304b\u306e\u51e6\u7406\u3092\u30b7\u30df\u30e5\u30ec\u30fc\u30c8\r\n import time\r\n time.sleep(1)\r\n return \"\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u3057\u307e\u3057\u305f\"\r\n\r\ndef process_data():\r\n import time\r\n time.sleep(1)\r\n return \"\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3057\u307e\u3057\u305f\"\r\n\r\ndef save_data():\r\n import time\r\n time.sleep(1)\r\n return \"\u30c7\u30fc\u30bf\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f\"\r\n\r\n# \u5168\u3066\u306e\u30bf\u30b9\u30af\u3092\u4e26\u5217\u5b9f\u884c\r\nresult = run_in_parallel([fetch_data, process_data, save_data])\r\n\r\nprint(f\"\u6210\u529f: {result.success_count}\") # \u6210\u529f: 3\r\nprint(f\"\u5b9f\u884c\u6642\u9593: {result.total_time:.2f}\u79d2\") # \u5b9f\u884c\u6642\u9593: ~1.00\u79d2 (\u9010\u6b21\u5b9f\u884c\u306a\u30893.00\u79d2)\r\nprint(f\"\u7d50\u679c: {result.results}\") # \u7d50\u679c: ['\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u3057\u307e\u3057\u305f', '\u30c7\u30fc\u30bf\u3092\u51e6\u7406\u3057\u307e\u3057\u305f', '\u30c7\u30fc\u30bf\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f']\r\n```\r\n\r\n### \u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\r\n\r\n```python\r\nfrom funcing import run_in_parallel\r\n\r\ndef working_task():\r\n return \"\u6210\u529f\uff01\"\r\n\r\ndef failing_task():\r\n raise ValueError(\"\u4f55\u304b\u304c\u3046\u307e\u304f\u3044\u304d\u307e\u305b\u3093\u3067\u3057\u305f\uff01\")\r\n\r\nresult = run_in_parallel([working_task, failing_task])\r\n\r\nprint(f\"\u6210\u529f: {result.success_count}\") # \u6210\u529f: 1\r\nprint(f\"\u30a8\u30e9\u30fc: {result.error_count}\") # \u30a8\u30e9\u30fc: 1\r\nprint(f\"\u6210\u529f\u7387: {result.success_rate:.1f}%\") # \u6210\u529f\u7387: 50.0%\r\nprint(f\"\u5168\u3066\u6210\u529f: {result.all_successful}\") # \u5168\u3066\u6210\u529f: False\r\n```\r\n\r\n### \u5f15\u6570\u4ed8\u304d\u95a2\u6570\r\n\r\n```python\r\nfrom funcing import run_with_args\r\n\r\ndef add(a, b):\r\n return a + b\r\n\r\ndef multiply(a, b):\r\n return a * b\r\n\r\ndef greet(name, greeting=\"\u3053\u3093\u306b\u3061\u306f\"):\r\n return f\"{greeting}\u3001{name}\u3055\u3093\uff01\"\r\n\r\n# \u5f15\u6570\u4ed8\u304d\u95a2\u6570\r\npairs = [\r\n (add, (1, 2)),\r\n (multiply, (3, 4)),\r\n (greet, (\"\u592a\u90ce\",), {\"greeting\": \"\u3084\u3042\"})\r\n]\r\n\r\nresult = run_with_args(pairs)\r\nprint(result.results) # [3, 12, '\u3084\u3042\u3001\u592a\u90ce\u3055\u3093\uff01']\r\n```\r\n\r\n### \u9ad8\u5ea6\u306a\u30aa\u30d7\u30b7\u30e7\u30f3\r\n\r\n```python\r\nfrom funcing import run_in_parallel\r\n\r\nfunctions = [task1, task2, task3, task4, task5]\r\n\r\nresult = run_in_parallel(\r\n functions,\r\n timeout=30.0, # \u6700\u592730\u79d2\r\n max_workers=3, # 3\u3064\u306e\u30b9\u30ec\u30c3\u30c9\u306e\u307f\u4f7f\u7528\r\n return_exceptions=True # \u30a8\u30e9\u30fc\u3092\u53ce\u96c6\uff08\u4f8b\u5916\u3092\u767a\u751f\u3055\u305b\u306a\u3044\uff09\r\n)\r\n\r\nprint(f\"{result.total_time:.2f}\u79d2\u3067\u5b8c\u4e86\")\r\nprint(f\"\u6210\u529f\u7387: {result.success_rate:.1f}%\")\r\n```\r\n\r\n## \ud83d\udcca \u7d50\u679c\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\r\n\r\n`FuncingResult`\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u5305\u62ec\u7684\u306a\u60c5\u5831\u3092\u63d0\u4f9b\u3057\u307e\u3059\uff1a\r\n\r\n```python\r\nresult = run_in_parallel([func1, func2, func3])\r\n\r\n# \u30d7\u30ed\u30d1\u30c6\u30a3\r\nresult.results # \u6210\u529f\u3057\u305f\u7d50\u679c\u306e\u30ea\u30b9\u30c8\r\nresult.errors # \u4f8b\u5916\u306e\u30ea\u30b9\u30c8\r\nresult.success_count # \u6210\u529f\u3057\u305f\u95a2\u6570\u306e\u6570\r\nresult.error_count # \u5931\u6557\u3057\u305f\u95a2\u6570\u306e\u6570\r\nresult.total_time # \u7dcf\u5b9f\u884c\u6642\u9593\r\nresult.function_names # \u5b9f\u884c\u3055\u308c\u305f\u95a2\u6570\u306e\u540d\u524d\r\nresult.success_rate # \u6210\u529f\u7387\uff08\u30d1\u30fc\u30bb\u30f3\u30c8\uff09\r\nresult.all_successful # \u30a8\u30e9\u30fc\u304c\u7121\u3044\u5834\u5408True\r\n```\r\n\r\n## \ud83c\udfaf \u4f7f\u7528\u4f8b\r\n\r\n- **Web\u30b9\u30af\u30ec\u30a4\u30d4\u30f3\u30b0**: \u8907\u6570\u306eURL\u3092\u540c\u6642\u306b\u53d6\u5f97\r\n- **API\u547c\u3073\u51fa\u3057**: \u8907\u6570\u306eAPI\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u4e26\u5217\u5b9f\u884c\r\n- **\u30d5\u30a1\u30a4\u30eb\u51e6\u7406**: \u8907\u6570\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u540c\u6642\u306b\u51e6\u7406\r\n- **\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c**: \u72ec\u7acb\u3057\u305f\u30af\u30a8\u30ea\u3092\u4e26\u5217\u5b9f\u884c\r\n- **\u30c7\u30fc\u30bf\u691c\u8a3c**: \u8907\u6570\u306e\u5165\u529b\u3092\u540c\u6642\u306b\u691c\u8a3c\r\n\r\n## \ud83d\udee1\ufe0f \u30a8\u30e9\u30fc\u5b89\u5168\u6027\r\n\r\nFuncing\u306f\u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u30a8\u30e9\u30fc\u5b89\u5168\u306b\u8a2d\u8a08\u3055\u308c\u3066\u3044\u307e\u3059\uff1a\r\n\r\n- \u500b\u5225\u306e\u95a2\u6570\u306e\u5931\u6557\u304c\u5168\u4f53\u306e\u5b9f\u884c\u3092\u30af\u30e9\u30c3\u30b7\u30e5\u3055\u305b\u306a\u3044\r\n- \u5143\u306e\u4f8b\u5916\u306b\u3088\u308b\u8a73\u7d30\u306a\u30a8\u30e9\u30fc\u5831\u544a\r\n- \u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u51e6\u7406\u3067\u30cf\u30f3\u30b0\u3092\u9632\u6b62\r\n- \u81ea\u52d5\u7684\u306a\u30ea\u30bd\u30fc\u30b9\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\r\n\r\n## \ud83d\udd27 \u9ad8\u5ea6\u306a\u6a5f\u80fd\r\n\r\n### \u30ab\u30b9\u30bf\u30e0\u30b9\u30ec\u30c3\u30c9\u30d7\u30fc\u30eb\u30b5\u30a4\u30ba\r\n\r\n```python\r\n# I/O\u96c6\u7d04\u7684\u306a\u30bf\u30b9\u30af\u306b\u306f\u591a\u304f\u306e\u30b9\u30ec\u30c3\u30c9\u3092\u4f7f\u7528\r\nresult = run_in_parallel(io_functions, max_workers=50)\r\n\r\n# CPU\u96c6\u7d04\u7684\u306a\u30bf\u30b9\u30af\u306b\u306f\u5c11\u306a\u3044\u30b9\u30ec\u30c3\u30c9\u3092\u4f7f\u7528\r\nresult = run_in_parallel(cpu_functions, max_workers=4)\r\n```\r\n\r\n### \u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u51e6\u7406\r\n\r\n```python\r\n# \u5168\u4f53\u306e\u5b9f\u884c\u306b\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3092\u8a2d\u5b9a\r\nresult = run_in_parallel(functions, timeout=10.0)\r\n\r\nif result.errors:\r\n print(\"\u3044\u304f\u3064\u304b\u306e\u95a2\u6570\u304c\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u3057\u307e\u3057\u305f\uff01\")\r\n```\r\n\r\n### \u4f8b\u5916\u51e6\u7406\u30e2\u30fc\u30c9\r\n\r\n```python\r\n# \u4f8b\u5916\u3092\u53ce\u96c6\uff08\u30c7\u30d5\u30a9\u30eb\u30c8\uff09\r\nresult = run_in_parallel(functions, return_exceptions=True)\r\n\r\n# \u6700\u521d\u306e\u4f8b\u5916\u3067\u505c\u6b62\r\ntry:\r\n result = run_in_parallel(functions, return_exceptions=False)\r\nexcept Exception as e:\r\n print(f\"\u5b9f\u884c\u5931\u6557: {e}\")\r\n```\r\n\r\n## \ud83d\udcdc \u30e9\u30a4\u30bb\u30f3\u30b9\r\n\r\nMIT License - \u8a73\u7d30\u306fLICENSE\u30d5\u30a1\u30a4\u30eb\u3092\u3054\u89a7\u304f\u3060\u3055\u3044\u3002\r\n",
"bugtrack_url": null,
"license": null,
"summary": "Simplified, error-safe threading for Python",
"version": "1.0.0",
"project_urls": {
"Bug Reports": "https://github.com/tikipiya/funcing/issues",
"Homepage": "https://github.com/tikipiya/funcing",
"Source": "https://github.com/tikipiya/funcing"
},
"split_keywords": [
"threading",
"parallel",
"async",
"concurrent",
"simple"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f96929a9a4839ab9c37124a449213fea240996d3efa5171d099a05e4457c31ef",
"md5": "1b431378814634345d26a260501710cd",
"sha256": "f9b73879b176d466760dfb38dab0222883e364eb4438c0a614a215244b02b641"
},
"downloads": -1,
"filename": "funcing-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "1b431378814634345d26a260501710cd",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 9134,
"upload_time": "2025-07-08T23:38:04",
"upload_time_iso_8601": "2025-07-08T23:38:04.675148Z",
"url": "https://files.pythonhosted.org/packages/f9/69/29a9a4839ab9c37124a449213fea240996d3efa5171d099a05e4457c31ef/funcing-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "5c35c9d9191d4b57a94e2be519bdf1c084558d8d5071a518ce70b8d534cc3c4d",
"md5": "6e8f21b2eeb546fc5fdac741309cc3eb",
"sha256": "67397e3153ef1b03a66792296f32a8e6409517bfbedaa67976c038b8184681bf"
},
"downloads": -1,
"filename": "funcing-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "6e8f21b2eeb546fc5fdac741309cc3eb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 8765,
"upload_time": "2025-07-08T23:38:05",
"upload_time_iso_8601": "2025-07-08T23:38:05.769079Z",
"url": "https://files.pythonhosted.org/packages/5c/35/c9d9191d4b57a94e2be519bdf1c084558d8d5071a518ce70b8d534cc3c4d/funcing-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-08 23:38:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "tikipiya",
"github_project": "funcing",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "pytest",
"specs": [
[
">=",
"6.0.0"
]
]
},
{
"name": "pytest-cov",
"specs": [
[
">=",
"2.0.0"
]
]
},
{
"name": "pytest-mock",
"specs": [
[
">=",
"3.0.0"
]
]
},
{
"name": "black",
"specs": [
[
">=",
"21.0.0"
]
]
},
{
"name": "flake8",
"specs": [
[
">=",
"3.8.0"
]
]
},
{
"name": "mypy",
"specs": [
[
">=",
"0.910"
]
]
}
],
"lcname": "funcing"
}