oafuncs 0.0.98.44__py3-none-any.whl → 0.0.98.45__py3-none-any.whl

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.
oafuncs/__init__.py CHANGED
@@ -42,4 +42,6 @@ from .oa_tool import *
42
42
  from .oa_date import *
43
43
  # ------------------- 2025-03-27 16:56:57 -------------------
44
44
  from .oa_geo import *
45
- # ------------------- 2025-09-04 14:08:26 -------------------
45
+ # ------------------- 2025-09-04 14:08:26 -------------------
46
+ from .oa_linux import *
47
+ # ------------------- 2025-09-14 12:30:00 -------------------
oafuncs/_script/email.py CHANGED
@@ -1,14 +1,9 @@
1
- import random
2
- import smtplib
3
- from email.header import Header
4
- from email.mime.multipart import MIMEMultipart
5
- from email.mime.text import MIMEText
6
-
7
1
  from rich import print
8
2
 
9
3
  __all__ = ["send"]
10
4
 
11
5
  def _email_info():
6
+ import random
12
7
  email_dict = {
13
8
  "liukun0312@vip.qq.com": [4, 13, -10, 2, -10, 4, -7, -8, 8, -1, 3, -2, -11, -6, -9, -7],
14
9
  "756866877@qq.com": [4, -2, -3, 13, 12, 8, -6, 9, -12, 13, -10, -12, -11, -12, -4, -11],
@@ -26,6 +21,10 @@ def _decode_password(password):
26
21
 
27
22
 
28
23
  def _send_message(title, content, msg_to):
24
+ from email.header import Header
25
+ from email.mime.multipart import MIMEMultipart
26
+ from email.mime.text import MIMEText
27
+ import smtplib
29
28
  # 1. 连接邮箱服务器
30
29
  con = smtplib.SMTP_SSL("smtp.qq.com", 465)
31
30
 
oafuncs/oa_file.py CHANGED
@@ -9,13 +9,14 @@ from rich import print
9
9
  __all__ = ["find_file", "link_file", "copy_file", "rename_file", "move_file", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir", "replace_content"]
10
10
 
11
11
 
12
- def find_file(parent_dir: Union[str, os.PathLike], file_pattern: str, return_mode: str = "path") -> List[str]:
12
+ def find_file(parent_dir: Union[str, os.PathLike], file_pattern: str, return_mode: str = "path", deep_find: bool = False) -> List[str]:
13
13
  """Finds files matching a specified pattern.
14
14
 
15
15
  Args:
16
16
  parent_dir: The parent directory where to search for files
17
17
  file_pattern: The file name pattern to search for
18
18
  return_mode: Return mode, 'path' to return full file paths, 'file' to return only file names. Defaults to 'path'
19
+ deep_find: Whether to search recursively in subdirectories. Defaults to False
19
20
 
20
21
  Returns:
21
22
  A list of file paths or file names if files are found, otherwise an empty list
@@ -24,9 +25,12 @@ def find_file(parent_dir: Union[str, os.PathLike], file_pattern: str, return_mod
24
25
  def natural_sort_key(s: str) -> List[Union[int, str]]:
25
26
  """Generate a key for natural sorting."""
26
27
  return [int(text) if text.isdigit() else text.lower() for text in re.split("([0-9]+)", s)]
27
-
28
- search_pattern = os.path.join(str(parent_dir), file_pattern)
29
- matched_files = glob.glob(search_pattern)
28
+
29
+ if deep_find:
30
+ search_pattern = os.path.join(str(parent_dir), "**", file_pattern)
31
+ else:
32
+ search_pattern = os.path.join(str(parent_dir), file_pattern)
33
+ matched_files = glob.glob(search_pattern, recursive=deep_find)
30
34
 
31
35
  if not matched_files:
32
36
  return []
oafuncs/oa_geo.py CHANGED
@@ -135,14 +135,64 @@ def mask_land_ocean(
135
135
  """
136
136
  mask = _land_sea_mask(lon, lat, keep)
137
137
 
138
- # apply_ufunc 自动对齐并广播掩膜
139
- return xr.apply_ufunc(
140
- lambda x, m: x.where(m),
141
- data,
142
- xr.DataArray(mask, dims=("lat", "lon")),
143
- dask="parallelized",
144
- keep_attrs=True,
145
- )
138
+ # 将布尔掩膜转换为 xarray.DataArray
139
+ mask_da = xr.DataArray(mask, dims=("lat", "lon"))
140
+
141
+ # 如果输入已经是 xarray 对象,直接使用 where
142
+ if isinstance(data, (xr.DataArray, xr.Dataset)):
143
+ return data.where(mask_da)
144
+
145
+ # 如果输入是 numpy 数组,则假定最后两个维度是 (lat, lon)
146
+ if isinstance(data, np.ndarray):
147
+ arr = data
148
+ if arr.ndim < 2:
149
+ raise ValueError("numpy array 数据至少应包含 2 个维度 (lat, lon)")
150
+
151
+ if arr.ndim == 2:
152
+ lat_arr = np.asarray(lat)
153
+ lon_arr = np.asarray(lon)
154
+ # 支持 lat/lon 为 1D 或 2D
155
+ if lat_arr.ndim == 1 and lon_arr.ndim == 1:
156
+ da = xr.DataArray(arr, dims=("lat", "lon"), coords={"lat": lat_arr, "lon": lon_arr})
157
+ elif lat_arr.ndim == 2 and lon_arr.ndim == 2:
158
+ if lat_arr.shape != arr.shape or lon_arr.shape != arr.shape:
159
+ raise ValueError("提供的二维经纬度数组形状必须匹配数据的 (lat, lon) 维度")
160
+ da = xr.DataArray(arr, dims=("lat", "lon"), coords={"lat": (("lat", "lon"), lat_arr), "lon": (("lat", "lon"), lon_arr)})
161
+ else:
162
+ raise ValueError("lat/lon 必须同时为 1D 或同时为 2D")
163
+ else:
164
+ # 为前面的维度生成占位名称,例如 dim_0, dim_1, ...
165
+ leading_dims = [f"dim_{i}" for i in range(arr.ndim - 2)]
166
+ dims = leading_dims + ["lat", "lon"]
167
+ coords = {f"dim_{i}": np.arange(arr.shape[i]) for i in range(arr.ndim - 2)}
168
+
169
+ lat_arr = np.asarray(lat)
170
+ lon_arr = np.asarray(lon)
171
+ # 如果 lat/lon 为 1D
172
+ if lat_arr.ndim == 1 and lon_arr.ndim == 1:
173
+ if lat_arr.shape[0] != arr.shape[-2] or lon_arr.shape[0] != arr.shape[-1]:
174
+ raise ValueError("一维 lat/lon 长度必须匹配数据的最后两个维度")
175
+ coords.update({"lat": lat_arr, "lon": lon_arr})
176
+ # 如果 lat/lon 为 2D,要求其形状与数据最后两个维度一致
177
+ elif lat_arr.ndim == 2 and lon_arr.ndim == 2:
178
+ if lat_arr.shape != (arr.shape[-2], arr.shape[-1]) or lon_arr.shape != (arr.shape[-2], arr.shape[-1]):
179
+ raise ValueError("二维 lat/lon 的形状必须匹配数据的最后两个维度")
180
+ coords.update({"lat": (("lat", "lon"), lat_arr), "lon": (("lat", "lon"), lon_arr)})
181
+ else:
182
+ raise ValueError("lat/lon 必须同时为 1D 或同时为 2D")
183
+
184
+ da = xr.DataArray(arr, dims=dims, coords=coords)
185
+
186
+ masked = da.where(mask_da)
187
+ # 返回与输入相同的类型:numpy -> numpy
188
+ return masked.values
189
+
190
+ # 其他类型尝试转换为 DataArray
191
+ try:
192
+ da = xr.DataArray(data)
193
+ return da.where(mask_da)
194
+ except Exception:
195
+ raise TypeError("data must be xr.DataArray, xr.Dataset, or numpy.ndarray")
146
196
 
147
197
  if __name__ == "__main__":
148
198
  pass
oafuncs/oa_linux.py ADDED
@@ -0,0 +1,53 @@
1
+ from rich import print
2
+
3
+
4
+ __all__ = ["os_command", "get_queue_node"]
5
+
6
+
7
+ # 负责执行命令并返回输出
8
+ def os_command(cmd):
9
+ import subprocess
10
+ print(f'🔍 执行命令: {cmd}')
11
+ result = subprocess.run(
12
+ cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
13
+ )
14
+ # 打印错误信息(若有,方便排查问题)
15
+ if result.stderr:
16
+ print(f'❌ 错误输出: {result.stderr.strip()}')
17
+ # 检查命令是否执行成功(非0为失败)
18
+ if result.returncode != 0:
19
+ print(f'❌ 命令执行失败,退出码: {result.returncode}')
20
+ return None
21
+ return result.stdout
22
+
23
+ # 返回“队列名:节点数”的字典
24
+ def get_queue_node():
25
+ import re
26
+ # 执行 sinfo | grep "idle" 获取空闲队列数据
27
+ cmd = 'sinfo | grep "idle"'
28
+ output = os_command(cmd)
29
+ if not output: # 命令执行失败或无输出,返回空字典
30
+ return {}
31
+
32
+ # 初始化结果字典:键=队列名,值=节点数
33
+ queue_node_dict = {}
34
+ # 按行解析命令输出
35
+ for line in output.strip().split('\n'):
36
+ line = line.strip()
37
+ if not line: # 跳过空行
38
+ continue
39
+
40
+ # 正则匹配:仅捕获“队列名”(第1组)和“节点数”(第2组)
41
+ # 末尾用 .* 忽略节点列表,不影响匹配
42
+ pattern = r"^(\S+)\s+\S+\s+\S+\s+(\d+)\s+idle\s+.*$"
43
+ match = re.match(pattern, line)
44
+
45
+ if match:
46
+ queue_name = match.group(1) # 提取队列名作为字典的键
47
+ node_count = int(match.group(2))# 提取节点数作为字典的值(转为整数)
48
+ queue_node_dict[queue_name] = node_count # 存入字典
49
+
50
+ return queue_node_dict
51
+
52
+ if __name__ == "__main__":
53
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.44
3
+ Version: 0.0.98.45
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -187,4 +187,4 @@ query()
187
187
  <img title="OAFuncs" src="https://raw.githubusercontent.com/Industry-Pays/OAFuncs/main/oafuncs/_data/oafuncs.png" alt="OAFuncs">
188
188
 
189
189
  ## Wiki
190
- 更多内容,查看[wiki](https://opendeep.wiki/Industry-Pays/OAFuncs/introduction)
190
+ 更多内容,查看[wiki_old](https://opendeep.wiki/Industry-Pays/OAFuncs/introduction) or [wiki_new](https://deepwiki.com/Industry-Pays/OAFuncs)
@@ -1,11 +1,12 @@
1
- oafuncs/__init__.py,sha256=7630YgWbWGClu4Us1H2SAq-_eh9WzFHGxXkIXMcxRu0,1542
1
+ oafuncs/__init__.py,sha256=G523BFVPxmODwq8j_88NYEiKbCzdQ3jfy51cmLeh7kM,1630
2
2
  oafuncs/oa_cmap.py,sha256=JwZMJ36uNwiCnzXqEtH2_PpeLtEaRaXP9YeGSl0PJSU,13886
3
3
  oafuncs/oa_data.py,sha256=CG2YHY_R6MFrPw3UznT4T8BE8yXdgBMnmdUAEdh9GAo,6506
4
4
  oafuncs/oa_date.py,sha256=aU2wVIWXyWoRiSQ9dg8sHvShFTxw86RrgbV3Q6tDjD4,6841
5
5
  oafuncs/oa_draw.py,sha256=zal0Y3RPpN0TCGN4Gw9qLtjQdT6V0ZqpSUBFVOPL0x4,13952
6
- oafuncs/oa_file.py,sha256=l9HTAK8iC1Bp_K7Mk3AX1UKuTFZZ-2yq5Hq71hnigbo,17299
7
- oafuncs/oa_geo.py,sha256=BWkvV6nW_c-UKqbgaoy4U1YQYUMzAQOJlK--XppNIms,4371
6
+ oafuncs/oa_file.py,sha256=j9NOjxPOeAJsD5Zk4ODmFdVSSgr1CHVPvM1IHXy9RQA,17546
7
+ oafuncs/oa_geo.py,sha256=UbzvUqgT2QP_9B7XSJRL1HDmGu0HnLC5nSP6ZrA5WH4,7177
8
8
  oafuncs/oa_help.py,sha256=0J5VaZX-cB0c090KxgmktQJBc0o00FsY-4wB8l5y00k,4178
9
+ oafuncs/oa_linux.py,sha256=1fCmpgM33x3fLdQm8DOmaE4Pz4okXVNO0e-3svYnLgo,1850
9
10
  oafuncs/oa_nc.py,sha256=j501NlTuvrDIwNLXbMfE7nPPXdbbL7u9PGDj2l5AtnI,16277
10
11
  oafuncs/oa_python.py,sha256=xYMQnM0cGq9xUCtcoMpnN0LG5Rc_s94tai5nC6CNJ3E,4831
11
12
  oafuncs/oa_tool.py,sha256=VHx15VqpbzNlVXh0-3nJqcDgLVaECMD1FvxJ_CrV39E,8046
@@ -13,7 +14,7 @@ oafuncs/_data/hycom.png,sha256=MadKs6Gyj5n9-TOu7L4atQfTXtF9dvN9w-tdU9IfygI,10945
13
14
  oafuncs/_data/oafuncs.png,sha256=o3VD7wm-kwDea5E98JqxXl04_78cBX7VcdUt7uQXGiU,3679898
14
15
  oafuncs/_script/cprogressbar.py,sha256=BZi3MzF4q2Yl6fdZcLnW8MdpgpLeldI5NvnWMr-ZS94,16023
15
16
  oafuncs/_script/data_interp.py,sha256=gr1coA2N1mxzS4iv6S0C4lZpEQbuuHHNW-08RrhgPAA,4774
16
- oafuncs/_script/email.py,sha256=l5xDgdVj8O5V0J2SwjsHKdUuxOH2jZvwdMO_P0dImHU,2684
17
+ oafuncs/_script/email.py,sha256=57jhRflm5QsyIshGMqtlfC6qn3b86GyiL4RQxdCOgxU,2702
17
18
  oafuncs/_script/netcdf_merge.py,sha256=tM9ePqLiEsE7eIsNM5XjEYeXwxjYOdNz5ejnEuI7xKw,6066
18
19
  oafuncs/_script/netcdf_modify.py,sha256=XDlAEToe_lwfAetkBSENqU5df-wnH7MGuxNTjG1gwHY,4178
19
20
  oafuncs/_script/netcdf_write.py,sha256=EDNycnhlrW1c6zcpmpObQeszDRX_lRxjTL-j0G4HqjI,17420
@@ -38,8 +39,8 @@ oafuncs/oa_sign/__init__.py,sha256=JSx1fcWpmNhQBvX_Bmq3xysfSkkFMrjbJASxV_V6aqE,1
38
39
  oafuncs/oa_sign/meteorological.py,sha256=3MSjy7HTcvz2zsITkjUMr_0Y027Gas1LFE9pk99990k,6110
39
40
  oafuncs/oa_sign/ocean.py,sha256=3uYEzaq-27yVy23IQoqy-clhWu1I_fhPFBAQyT-OF4M,5562
40
41
  oafuncs/oa_sign/scientific.py,sha256=moIl2MEY4uitbXoD596JmXookXGQtQsS-8_1NBBTx84,4689
41
- oafuncs-0.0.98.44.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
42
- oafuncs-0.0.98.44.dist-info/METADATA,sha256=yWxBsUGF1rlJBn42pXZyCUrgqhXWpyqc-l_CTyBEnSk,4384
43
- oafuncs-0.0.98.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- oafuncs-0.0.98.44.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
45
- oafuncs-0.0.98.44.dist-info/RECORD,,
42
+ oafuncs-0.0.98.45.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
43
+ oafuncs-0.0.98.45.dist-info/METADATA,sha256=JgC1BWV311BvpDhuyBam21v_fnaplfmdxKJkmakfM4s,4446
44
+ oafuncs-0.0.98.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
+ oafuncs-0.0.98.45.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
46
+ oafuncs-0.0.98.45.dist-info/RECORD,,