oafuncs 0.0.98.36__tar.gz → 0.0.98.38__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {oafuncs-0.0.98.36/oafuncs.egg-info → oafuncs-0.0.98.38}/PKG-INFO +1 -1
  2. oafuncs-0.0.98.38/oafuncs/_script/parallel.py +269 -0
  3. oafuncs-0.0.98.36/oafuncs/_script/parallel.py → oafuncs-0.0.98.38/oafuncs/_script/parallel_bak.py +1 -1
  4. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_tool.py +1 -1
  5. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38/oafuncs.egg-info}/PKG-INFO +1 -1
  6. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs.egg-info/SOURCES.txt +1 -1
  7. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/setup.py +1 -1
  8. oafuncs-0.0.98.36/oafuncs/_script/parallel_test.py +0 -14
  9. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/LICENSE.txt +0 -0
  10. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/MANIFEST.in +0 -0
  11. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/README.md +0 -0
  12. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/__init__.py +0 -0
  13. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_data/hycom.png +0 -0
  14. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_data/oafuncs.png +0 -0
  15. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/cprogressbar.py +0 -0
  16. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/data_interp.py +0 -0
  17. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/email.py +0 -0
  18. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/netcdf_merge.py +0 -0
  19. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/netcdf_modify.py +0 -0
  20. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/netcdf_write.py +0 -0
  21. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/plot_dataset.py +0 -0
  22. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/_script/replace_file_content.py +0 -0
  23. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_cmap.py +0 -0
  24. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_data.py +0 -0
  25. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_date.py +0 -0
  26. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/User_Agent-list.txt +0 -0
  27. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/__init__.py +0 -0
  28. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/hycom_3hourly.py +0 -0
  29. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/idm.py +0 -0
  30. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/literature.py +0 -0
  31. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/read_proxy.py +0 -0
  32. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/test_ua.py +0 -0
  33. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_down/user_agent.py +0 -0
  34. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_draw.py +0 -0
  35. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_file.py +0 -0
  36. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_help.py +0 -0
  37. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_model/__init__.py +0 -0
  38. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_model/roms/__init__.py +0 -0
  39. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_model/roms/test.py +0 -0
  40. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_model/wrf/__init__.py +0 -0
  41. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_model/wrf/little_r.py +0 -0
  42. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_nc.py +0 -0
  43. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_python.py +0 -0
  44. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_sign/__init__.py +0 -0
  45. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_sign/meteorological.py +0 -0
  46. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_sign/ocean.py +0 -0
  47. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs/oa_sign/scientific.py +0 -0
  48. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs.egg-info/dependency_links.txt +0 -0
  49. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs.egg-info/requires.txt +0 -0
  50. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/oafuncs.egg-info/top_level.txt +0 -0
  51. {oafuncs-0.0.98.36 → oafuncs-0.0.98.38}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.36
3
+ Version: 0.0.98.38
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -0,0 +1,269 @@
1
+ import logging
2
+ import multiprocessing as mp
3
+ import platform
4
+ import threading
5
+ import time
6
+ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
7
+ from typing import Any, Callable, Dict, List, Optional, Tuple
8
+
9
+
10
+ import psutil
11
+
12
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
13
+
14
+ __all__ = ["ParallelExecutor"]
15
+
16
+
17
+ class ParallelExecutor:
18
+
19
+ def __init__(
20
+ self,
21
+ max_workers: Optional[int] = None,
22
+ chunk_size: Optional[int] = None,
23
+ mem_per_process: float = 3.0, # GB
24
+ timeout_per_task: int = 3600,
25
+ max_retries: int = 3,
26
+ ):
27
+ self.platform = self._detect_platform()
28
+ self.mem_per_process = mem_per_process
29
+ self.timeout_per_task = timeout_per_task
30
+ self.max_retries = max_retries
31
+ self.running = True
32
+ self.task_history = []
33
+ self._executor = None
34
+ self._shutdown_called = False
35
+
36
+ self.mode, default_workers = self._determine_optimal_settings()
37
+ self.max_workers = max_workers or default_workers
38
+ self.chunk_size = chunk_size or self._get_default_chunk_size()
39
+
40
+ self._init_platform_settings()
41
+ self._start_resource_monitor()
42
+
43
+ logging.info(f"Initialized {self.__class__.__name__} on {self.platform} (mode={self.mode}, workers={self.max_workers})")
44
+
45
+ def _detect_platform(self) -> str:
46
+ system = platform.system().lower()
47
+ if system == "linux":
48
+ return "wsl" if "microsoft" in platform.release().lower() else "linux"
49
+ return system
50
+
51
+ def _init_platform_settings(self):
52
+ if self.platform in ["linux", "wsl"]:
53
+ self.mp_context = mp.get_context("fork")
54
+ elif self.platform == "windows":
55
+ mp.set_start_method("spawn", force=True)
56
+ self.mp_context = mp.get_context("spawn")
57
+ else:
58
+ self.mp_context = None
59
+
60
+ def _determine_optimal_settings(self) -> Tuple[str, int]:
61
+ logical_cores = psutil.cpu_count(logical=True) or 1
62
+ available_mem = psutil.virtual_memory().available / 1024**3 # GB
63
+
64
+ mem_limit = max(1, int(available_mem / self.mem_per_process))
65
+ return ("process", min(logical_cores, mem_limit))
66
+
67
+ def _get_default_chunk_size(self) -> int:
68
+ return max(10, 100 // (psutil.cpu_count() or 1))
69
+
70
+ def _start_resource_monitor(self):
71
+ def monitor():
72
+ threshold = self.mem_per_process * 1024**3
73
+ while self.running:
74
+ try:
75
+ if psutil.virtual_memory().available < threshold:
76
+ self._scale_down_workers()
77
+ time.sleep(1)
78
+ except Exception as e:
79
+ logging.error(f"Resource monitor error: {e}")
80
+
81
+ threading.Thread(target=monitor, daemon=True).start()
82
+
83
+ def _scale_down_workers(self):
84
+ if self.max_workers > 1:
85
+ new_count = self.max_workers - 1
86
+ logging.warning(f"Scaling down workers from {self.max_workers} to {new_count}")
87
+ self.max_workers = new_count
88
+ self._restart_executor()
89
+
90
+ def _restart_executor(self):
91
+ if self._executor:
92
+ self._executor.shutdown(wait=False)
93
+ self._executor = None
94
+
95
+ def _get_executor(self):
96
+ if not self._executor:
97
+ Executor = ThreadPoolExecutor if self.mode == "thread" else ProcessPoolExecutor
98
+ self._executor = Executor(max_workers=self.max_workers, mp_context=self.mp_context if self.mode == "process" else None)
99
+ return self._executor
100
+
101
+ def run(self, func: Callable, params: List[Tuple], chunk_size: Optional[int] = None) -> List[Any]:
102
+ chunk_size = chunk_size or self.chunk_size
103
+ try:
104
+ for retry in range(self.max_retries + 1):
105
+ try:
106
+ start_time = time.monotonic()
107
+ results = self._execute_batch(func, params, chunk_size)
108
+ self._update_settings(time.monotonic() - start_time, len(params))
109
+ return results
110
+ except Exception as e:
111
+ logging.error(f"Attempt {retry + 1} failed: {e}")
112
+ self._handle_failure()
113
+ raise RuntimeError(f"Failed after {self.max_retries} retries")
114
+ finally:
115
+ # 仅关闭当前 executor,保留资源监控等运行状态
116
+ if self._executor:
117
+ try:
118
+ self._executor.shutdown(wait=True)
119
+ except Exception as e:
120
+ logging.error(f"Executor shutdown error: {e}")
121
+ finally:
122
+ self._executor = None
123
+
124
+ def _execute_batch(self, func: Callable, params: List[Tuple], chunk_size: int) -> List[Any]:
125
+ from oafuncs.oa_tool import pbar
126
+ if not params:
127
+ return []
128
+
129
+ if len(params) > chunk_size * 2:
130
+ return self._chunked_execution(func, params, chunk_size)
131
+
132
+ results = [None] * len(params)
133
+
134
+ # 创建进度条 - 使用 range 作为占位符,手动控制进度
135
+ progress_bar = pbar(
136
+ iterable=range(len(params)), # 使用 range 作为占位符
137
+ description="Parallel Tasks",
138
+ total=len(params),
139
+ completed=0,
140
+ next_line=False,
141
+ )
142
+ # 手动开始任务
143
+ progress_bar.task.start()
144
+
145
+ with self._get_executor() as executor:
146
+ futures = {executor.submit(func, *args): idx for idx, args in enumerate(params)}
147
+
148
+ for future in as_completed(futures):
149
+ idx = futures[future]
150
+ try:
151
+ results[idx] = future.result(timeout=self.timeout_per_task)
152
+ except Exception as e:
153
+ results[idx] = self._handle_error(e, func, params[idx])
154
+
155
+ # 实时更新进度条
156
+ progress_bar.update(1)
157
+ progress_bar.refresh()
158
+ # 完成后换行
159
+ print()
160
+ return results
161
+
162
+ def _chunked_execution(self, func: Callable, params: List[Tuple], chunk_size: int) -> List[Any]:
163
+ from oafuncs.oa_tool import pbar
164
+
165
+ results = []
166
+ chunk_count = (len(params) + chunk_size - 1) // chunk_size
167
+
168
+ # 为分块执行创建进度条
169
+ progress_bar = pbar(
170
+ iterable=range(chunk_count), # 使用 range 作为占位符
171
+ description="Parallel Chunks",
172
+ total=chunk_count,
173
+ completed=0,
174
+ next_line=False,
175
+ )
176
+ progress_bar.task.start()
177
+
178
+ with self._get_executor() as executor:
179
+ futures = []
180
+ for i in range(0, len(params), chunk_size):
181
+ chunk = params[i : i + chunk_size]
182
+ futures.append(executor.submit(self._process_chunk, func, chunk))
183
+
184
+ for future in as_completed(futures):
185
+ try:
186
+ results.extend(future.result(timeout=self.timeout_per_task))
187
+ except Exception as e:
188
+ logging.error(f"Chunk failed: {e}")
189
+ results.extend([None] * chunk_size)
190
+
191
+ # 更新分块进度
192
+ progress_bar.update(1)
193
+ progress_bar.refresh()
194
+ # 完成后换行
195
+ print()
196
+ return results
197
+
198
+ @staticmethod
199
+ def _process_chunk(func: Callable, chunk: List[Tuple]) -> List[Any]:
200
+ return [func(*args) for args in chunk]
201
+
202
+ def _update_settings(self, duration: float, task_count: int):
203
+ self.task_history.append((duration, task_count))
204
+ self.chunk_size = max(5, min(100, self.chunk_size + (1 if duration < 5 else -1)))
205
+
206
+ def _handle_error(self, error: Exception, func: Callable, args: Tuple) -> Any:
207
+ if isinstance(error, TimeoutError):
208
+ logging.warning(f"Timeout processing {func.__name__}{args}")
209
+ elif isinstance(error, MemoryError):
210
+ logging.warning("Memory error detected")
211
+ self._scale_down_workers()
212
+ else:
213
+ logging.error(f"Error processing {func.__name__}{args}: {str(error)}")
214
+ return None
215
+
216
+ def _handle_failure(self):
217
+ if self.max_workers > 2:
218
+ self.max_workers = max(1, self.max_workers // 2)
219
+ self._restart_executor()
220
+
221
+ def shutdown(self):
222
+ if self._shutdown_called:
223
+ return
224
+ self._shutdown_called = True
225
+ self.running = False
226
+ # 基类不再打印日志,由子类统一处理
227
+ if self._executor:
228
+ try:
229
+ self._executor.shutdown(wait=True)
230
+ except Exception as e:
231
+ logging.error(f"Shutdown error: {e}")
232
+ finally:
233
+ self._executor = None
234
+
235
+ def __enter__(self):
236
+ return self
237
+
238
+ def __exit__(self, *exc_info):
239
+ self.shutdown()
240
+
241
+ def get_stats(self) -> Dict[str, Any]:
242
+ stats = {
243
+ "platform": self.platform,
244
+ "mode": self.mode,
245
+ "workers": self.max_workers,
246
+ "chunk_size": self.chunk_size,
247
+ "total_tasks": sum(count for _, count in self.task_history),
248
+ }
249
+ if self.task_history:
250
+ total_time = sum(time for time, _ in self.task_history)
251
+ stats["avg_task_throughput"] = stats["total_tasks"] / total_time if total_time else 0
252
+ return stats
253
+
254
+
255
+ def _test_func(a, b):
256
+ time.sleep(0.01)
257
+ return a + b
258
+
259
+
260
+ if __name__ == "__main__":
261
+ params = [(i, i * 2) for i in range(1000)]
262
+
263
+ with ParallelExecutor() as executor:
264
+ results = executor.run(_test_func, params)
265
+
266
+ # print("Results:", results)
267
+
268
+ print(f"Processed {len(results)} tasks")
269
+ print("Execution stats:", executor.get_stats())
@@ -20,7 +20,7 @@ class ParallelExecutor:
20
20
  self,
21
21
  max_workers: Optional[int] = None,
22
22
  chunk_size: Optional[int] = None,
23
- mem_per_process: float = 1.0, # GB
23
+ mem_per_process: float = 3.0, # GB
24
24
  timeout_per_task: int = 3600,
25
25
  max_retries: int = 3,
26
26
  ):
@@ -37,7 +37,7 @@ class PEx(ParallelExecutor):
37
37
  self,
38
38
  max_workers: Optional[int] = None,
39
39
  chunk_size: Optional[int] = None,
40
- mem_per_process: float = 1.5, # 调大默认内存限制
40
+ mem_per_process: float = 3.0, # 调大默认内存限制
41
41
  timeout_per_task: int = 7200, # 延长默认超时时间
42
42
  max_retries: int = 5, # 增加默认重试次数
43
43
  progress_callback: Optional[Callable[[int, int], None]] = None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.36
3
+ Version: 0.0.98.38
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -26,7 +26,7 @@ oafuncs/_script/netcdf_merge.py
26
26
  oafuncs/_script/netcdf_modify.py
27
27
  oafuncs/_script/netcdf_write.py
28
28
  oafuncs/_script/parallel.py
29
- oafuncs/_script/parallel_test.py
29
+ oafuncs/_script/parallel_bak.py
30
30
  oafuncs/_script/plot_dataset.py
31
31
  oafuncs/_script/replace_file_content.py
32
32
  oafuncs/oa_down/User_Agent-list.txt
@@ -18,7 +18,7 @@ URL = "https://github.com/Industry-Pays/OAFuncs"
18
18
  EMAIL = "liukun0312@stu.ouc.edu.cn"
19
19
  AUTHOR = "Kun Liu"
20
20
  REQUIRES_PYTHON = ">=3.10.0" # 2025/03/13
21
- VERSION = "0.0.98.36"
21
+ VERSION = "0.0.98.38"
22
22
 
23
23
  # What packages are required for this module to be executed?
24
24
  REQUIRED = [
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env python
2
- # coding=utf-8
3
- """
4
- Author: Liu Kun && 16031215@qq.com
5
- Date: 2025-04-08 16:18:49
6
- LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2025-04-08 16:18:50
8
- FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\_script\\parallel_test.py
9
- Description:
10
- EditPlatform: vscode
11
- ComputerInfo: XPS 15 9510
12
- SystemInfo: Windows 11
13
- Python Version: 3.12
14
- """
File without changes
File without changes
File without changes
File without changes