oafuncs 0.0.97.4__py3-none-any.whl → 0.0.97.6__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.
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env python
2
+ # coding=utf-8
3
+ """
4
+ Author: Liu Kun && 16031215@qq.com
5
+ Date: 2025-03-21 10:02:32
6
+ LastEditors: Liu Kun && 16031215@qq.com
7
+ LastEditTime: 2025-03-21 10:02:33
8
+ FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\_script\\replace_file_concent.py
9
+ Description:
10
+ EditPlatform: vscode
11
+ ComputerInfo: XPS 15 9510
12
+ SystemInfo: Windows 11
13
+ Python Version: 3.12
14
+ """
15
+
16
+ import datetime
17
+ import os
18
+ import re
19
+ from pathlib import Path
20
+
21
+ from rich import print
22
+
23
+
24
+ def _prepare_file_operation(source_file, target_dir, new_name=None):
25
+ """
26
+ 准备文件操作的公共逻辑
27
+
28
+ 参数:
29
+ source_file: 源文件路径
30
+ target_dir: 目标目录路径
31
+ new_name: 新文件名,如果为None则使用原文件名
32
+
33
+ 返回:
34
+ target_file: 目标文件路径
35
+ """
36
+ os.makedirs(target_dir, exist_ok=True)
37
+ if new_name is None:
38
+ return os.path.join(target_dir, os.path.basename(source_file))
39
+ else:
40
+ return os.path.join(target_dir, new_name)
41
+
42
+
43
+ def replace_config_values(source_file, target_dir, param_dict, new_name=None):
44
+ """
45
+ 批量修改配置参数并保存到新路径(适用于等号赋值格式的参数)
46
+
47
+ 参数:
48
+ source_file: 源文件路径
49
+ target_dir: 目标目录路径
50
+ param_dict: 要修改的参数字典 {参数名: 新值}
51
+ new_name: 新文件名,如果为None则使用原文件名
52
+
53
+ 返回:
54
+ set: 成功修改的参数集合
55
+ """
56
+ try:
57
+ target_file = _prepare_file_operation(source_file, target_dir, new_name)
58
+
59
+ with open(source_file, "r") as f:
60
+ lines = f.readlines()
61
+
62
+ modified = set()
63
+ for i in range(len(lines)):
64
+ line = lines[i]
65
+ stripped = line.lstrip()
66
+
67
+ # 跳过注释行和空行
68
+ if stripped.startswith(("!", "#", ";", "%")) or not stripped.strip():
69
+ continue
70
+
71
+ # 匹配所有参数
72
+ for param, new_val in param_dict.items():
73
+ # 构造动态正则表达式
74
+ pattern = re.compile(r'^(\s*{})(\s*=\s*)([\'"]?)(.*?)(\3)(\s*(!.*)?)$'.format(re.escape(param)), flags=re.IGNORECASE)
75
+
76
+ match = pattern.match(line.rstrip("\n"))
77
+ if match and param not in modified:
78
+ # 构造新行(保留原始格式)
79
+ new_line = f"{match.group(1)}{match.group(2)}{match.group(3)}{new_val}{match.group(5)}{match.group(6) or ''}\n"
80
+ lines[i] = new_line
81
+ modified.add(param)
82
+ break # 每行最多处理一个参数
83
+
84
+ with open(target_file, "w") as f:
85
+ f.writelines(lines)
86
+
87
+ print(f"[green]已将参数替换到新文件:{target_file}[/green]")
88
+ return modified
89
+ except Exception as e:
90
+ print(f"[red]替换参数时出错:{str(e)}[/red]")
91
+ return set()
92
+
93
+
94
+ def replace_direct_content(source_file, target_dir, content_dict, key_value=False, new_name=None):
95
+ """
96
+ 直接替换文件中的指定内容并保存到新路径
97
+
98
+ 参数:
99
+ source_file: 源文件路径
100
+ target_dir: 目标目录路径
101
+ content_dict: 要替换的内容字典 {旧内容: 新内容}
102
+ key_value: 是否按键值对方式替换参数
103
+ new_name: 新文件名,如果为None则使用原文件名
104
+
105
+ 返回:
106
+ bool: 替换是否成功
107
+ """
108
+ try:
109
+ if key_value:
110
+ return len(replace_config_values(source_file, target_dir, content_dict, new_name)) > 0
111
+
112
+ target_file = _prepare_file_operation(source_file, target_dir, new_name)
113
+
114
+ with open(source_file, "r") as f:
115
+ content = f.read()
116
+
117
+ # 直接替换指定内容
118
+ for old_content, new_content in content_dict.items():
119
+ content = content.replace(old_content, new_content)
120
+
121
+ with open(target_file, "w") as f:
122
+ f.write(content)
123
+
124
+ print(f"[green]已将内容替换到新文件:{target_file}[/green]")
125
+ return True
126
+ except Exception as e:
127
+ print(f"[red]替换内容时出错:{str(e)}[/red]")
128
+ return False
129
+
130
+
131
+ if __name__ == "__main__":
132
+ control_file = Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file")
133
+ target_dir = r"/data/hejx/liukun/Work/Model/cas_esm/run"
134
+
135
+ force_time = 2023072900
136
+ ini_time = datetime.datetime.strptime(str(force_time), "%Y%m%d%H")
137
+ oisst_time = ini_time - datetime.timedelta(days=1) # 需要在前一天
138
+
139
+ replace_config_values(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "atm_in", target_dir=target_dir, param_dict={"ncdata": f"/data/hejx/liukun/Work/Model/cas_esm/data/IAP_ncep2_181x360_{ini_time.strftime('%Y%m%d')}_00_00_L35.nc"})
140
+
141
+ replace_direct_content(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "docn.stream.txt", target_dir=target_dir, content_dict={"oisst.forecast.20230727.nc": f"oisst.forecast.{oisst_time.strftime('%Y%m%d')}.nc"})
142
+
143
+ replace_config_values(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "drv_in", target_dir=target_dir, param_dict={"start_ymd": f"{ini_time.strftime('%Y%m%d')}"})
144
+
145
+ replace_config_values(source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "ice_in", target_dir=target_dir, param_dict={"stream_fldfilename": f"/data/hejx/liukun/Work/Model/cas_esm/data/oisst.forecast.{ini_time.strftime('%Y%m%d')}.nc"})
146
+
147
+ replace_config_values(
148
+ source_file=Path(r"/data/hejx/liukun/Work/Model/cas_esm/data/control_file") / "lnd_in",
149
+ target_dir=target_dir,
150
+ param_dict={"fini": f"/data/hejx/liukun/Work/Model/cas_esm/run_p1x1/colm-spinup-colm-restart-{ini_time.strftime('%Y-%m-%d')}-00000", "fsbc": f"/data/hejx/liukun/Work/Model/cas_esm/run_p1x1/colm-spinup-colm-restart-{ini_time.strftime('%Y-%m-%d')}-00000-sbc"},
151
+ )
oafuncs/oa_date.py ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python
2
+ # coding=utf-8
3
+ """
4
+ Author: Liu Kun && 16031215@qq.com
5
+ Date: 2025-03-27 16:56:57
6
+ LastEditors: Liu Kun && 16031215@qq.com
7
+ LastEditTime: 2025-03-27 16:56:57
8
+ FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_date.py
9
+ Description:
10
+ EditPlatform: vscode
11
+ ComputerInfo: XPS 15 9510
12
+ SystemInfo: Windows 11
13
+ Python Version: 3.12
14
+ """
15
+
16
+ import calendar
17
+ import datetime
18
+
19
+ __all__ = ["get_days_in_month", "generate_hour_list", "adjust_time"]
20
+
21
+
22
+ def get_days_in_month(year, month):
23
+ return calendar.monthrange(year, month)[1]
24
+
25
+
26
+ def generate_hour_list(start_date, end_date, interval_hours=6):
27
+ """
28
+ Generate a list of datetime strings with a specified interval in hours.
29
+
30
+ Args:
31
+ start_date (str): Start date in the format "%Y%m%d%H".
32
+ end_date (str): End date in the format "%Y%m%d%H".
33
+ interval_hours (int): Interval in hours between each datetime.
34
+
35
+ Returns:
36
+ list: List of datetime strings in the format "%Y%m%d%H".
37
+ """
38
+ date_s = datetime.datetime.strptime(start_date, "%Y%m%d%H")
39
+ date_e = datetime.datetime.strptime(end_date, "%Y%m%d%H")
40
+ date_list = []
41
+ while date_s <= date_e:
42
+ date_list.append(date_s.strftime("%Y%m%d%H"))
43
+ date_s += datetime.timedelta(hours=interval_hours)
44
+ return date_list
45
+
46
+
47
+ def adjust_time(initial_time, amount, time_unit="hours", output_format=None):
48
+ """
49
+ Adjust a given initial time by adding a specified amount of time.
50
+
51
+ Args:
52
+ initial_time (str): Initial time in the format "yyyymmdd" to "yyyymmddHHMMSS".
53
+ Missing parts are assumed to be "0".
54
+ amount (int): The amount of time to add.
55
+ time_unit (str): The unit of time to add ("seconds", "minutes", "hours", "days").
56
+ output_format (str, optional): Custom output format for the adjusted time. Defaults to None.
57
+
58
+ Returns:
59
+ str: The adjusted time as a string, formatted according to the output_format or time unit.
60
+ """
61
+ # Normalize the input time to "yyyymmddHHMMSS" format
62
+ time_format = "%Y%m%d%H%M%S"
63
+ initial_time = initial_time.ljust(14, "0")
64
+ time_obj = datetime.datetime.strptime(initial_time, time_format)
65
+
66
+ # Add the specified amount of time
67
+ if time_unit == "seconds":
68
+ time_obj += datetime.timedelta(seconds=amount)
69
+ elif time_unit == "minutes":
70
+ time_obj += datetime.timedelta(minutes=amount)
71
+ elif time_unit == "hours":
72
+ time_obj += datetime.timedelta(hours=amount)
73
+ elif time_unit == "days":
74
+ time_obj += datetime.timedelta(days=amount)
75
+ else:
76
+ raise ValueError("Invalid time unit. Use 'seconds', 'minutes', 'hours', or 'days'.")
77
+
78
+ # Determine the output format
79
+ if output_format:
80
+ return time_obj.strftime(output_format)
81
+ else:
82
+ if time_unit == "seconds":
83
+ default_format = "%Y%m%d%H%M%S"
84
+ elif time_unit == "minutes":
85
+ default_format = "%Y%m%d%H%M"
86
+ elif time_unit == "hours":
87
+ default_format = "%Y%m%d%H"
88
+ elif time_unit == "days":
89
+ default_format = "%Y%m%d"
90
+ return time_obj.strftime(default_format)
@@ -35,8 +35,8 @@ import xarray as xr
35
35
  from rich import print
36
36
  from rich.progress import Progress
37
37
 
38
- from oafuncs.oa_down.idm import downloader as idm_downloader
39
- from oafuncs.oa_down.user_agent import get_ua
38
+ from oafuncs._oa_down.idm import downloader as idm_downloader
39
+ from oafuncs._oa_down.user_agent import get_ua
40
40
  from oafuncs.oa_file import file_size, mean_size
41
41
  from oafuncs.oa_nc import check as check_nc
42
42
  from oafuncs.oa_nc import modify as modify_nc
@@ -34,8 +34,8 @@ import xarray as xr
34
34
  from rich import print
35
35
  from rich.progress import Progress
36
36
 
37
- from oafuncs.oa_down.idm import downloader as idm_downloader
38
- from oafuncs.oa_down.user_agent import get_ua
37
+ from oafuncs._oa_down.idm import downloader as idm_downloader
38
+ from oafuncs._oa_down.user_agent import get_ua
39
39
  from oafuncs.oa_file import file_size, mean_size
40
40
  from oafuncs.oa_nc import check as check_nc
41
41
  from oafuncs.oa_nc import modify as modify_nc
@@ -22,7 +22,7 @@ import pandas as pd
22
22
  import requests
23
23
  from rich import print
24
24
  from rich.progress import track
25
- from oafuncs.oa_down.user_agent import get_ua
25
+ from oafuncs._oa_down.user_agent import get_ua
26
26
  from oafuncs.oa_file import remove
27
27
  from oafuncs.oa_data import ensure_list
28
28
 
oafuncs/oa_draw.py CHANGED
@@ -76,26 +76,51 @@ def fig_minus(ax_x=None, ax_y=None, cbar=None, decimal=None, add_space=False):
76
76
 
77
77
 
78
78
  # ** 将生成图片/已有图片制作成动图
79
- def gif(image_list: list, gif_name: str, duration=0.2): # 制作动图,默认间隔0.2
79
+ def gif(image_list: list, gif_name: str, duration=200, resize=None): # 制作动图,默认间隔0.2
80
80
  """
81
81
  Description
82
82
  Make gif from images
83
83
  Parameters
84
84
  image_list : list, list of images
85
85
  gif_name : str, name of gif
86
- duration : float, duration of each frame
86
+ duration : float, duration of each frame, units: ms
87
+ resize : tuple, (width, height) to resize images, if None, use first image size
87
88
  Returns
88
89
  None
89
90
  Example
90
91
  gif(["1.png", "2.png"], "test.gif", duration=0.2)
91
92
  """
92
93
  import imageio.v2 as imageio
94
+ import numpy as np
95
+ from PIL import Image
93
96
 
94
97
  frames = []
98
+
99
+ # 获取目标尺寸
100
+ if resize is None and image_list:
101
+ # 使用第一张图片的尺寸作为标准
102
+ with Image.open(image_list[0]) as img:
103
+ resize = img.size
104
+
105
+ # 读取并调整所有图片的尺寸
95
106
  for image_name in image_list:
96
- frames.append(imageio.imread(image_name))
97
- imageio.mimsave(gif_name, frames, format="GIF", duration=duration)
98
- print("Gif制作完成!")
107
+ with Image.open(image_name) as img:
108
+ if resize:
109
+ img = img.resize(resize, Image.LANCZOS)
110
+ frames.append(np.array(img))
111
+
112
+ # 修改此处:明确使用 duration 值,并将其作为每帧的持续时间(以秒为单位)
113
+ # 某些版本的 imageio 可能需要以毫秒为单位,或者使用 fps 参数
114
+ try:
115
+ # 先尝试直接使用 duration 参数(以秒为单位)
116
+ imageio.mimsave(gif_name, frames, format="GIF", duration=duration)
117
+ except Exception as e:
118
+ print(f"尝试使用fps参数替代duration: {e}")
119
+ # 如果失败,尝试使用 fps 参数(fps = 1/duration)
120
+ fps = 1.0 / duration if duration > 0 else 5.0
121
+ imageio.mimsave(gif_name, frames, format="GIF", fps=fps)
122
+
123
+ print(f"Gif制作完成!尺寸: {resize}, 帧间隔: {duration}毫秒")
99
124
  return
100
125
 
101
126
 
@@ -171,8 +196,8 @@ def add_cartopy(ax, lon=None, lat=None, projection=ccrs.PlateCarree(), gridlines
171
196
 
172
197
  # set extent
173
198
  if lon is not None and lat is not None:
174
- lon_min, lon_max = lon.min(), lon.max()
175
- lat_min, lat_max = lat.min(), lat.max()
199
+ lon_min, lon_max = np.nanmin(lon), np.nanmax(lon)
200
+ lat_min, lat_max = np.nanmin(lat), np.nanmax(lat)
176
201
  ax.set_extent([lon_min, lon_max, lat_min, lat_max], crs=projection)
177
202
 
178
203
 
oafuncs/oa_file.py CHANGED
@@ -17,9 +17,11 @@ import glob
17
17
  import os
18
18
  import re
19
19
  import shutil
20
+
21
+
20
22
  from rich import print
21
23
 
22
- __all__ = ["find_file", "link_file", "copy_file", "rename_file", "make_folder", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir"]
24
+ __all__ = ["find_file", "link_file", "copy_file", "rename_file", "make_folder", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir", "replace_content", "move_file"]
23
25
 
24
26
 
25
27
  # ** 查找文件,支持通配符
@@ -155,6 +157,55 @@ def copy_file(src_pattern, dst):
155
157
  print(f"Copy and rename file or directory: {src_file} -> {dst_file}")
156
158
 
157
159
 
160
+
161
+ # ** 移动文件或目录,支持通配符
162
+ def move_file(src_pattern, dst):
163
+ """
164
+ # 描述:移动文件或目录,支持通配符
165
+ # 使用示例
166
+ # move_file(r'/data/hejx/liukun/era5/*', r'/data/hejx/liukun/Test/')
167
+ # move_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test/py.o')
168
+ # move_file(r'/data/hejx/liukun/era5/py.o*', r'/data/hejx/liukun/Test')
169
+ param {*} src_pattern # 源文件或目录
170
+ param {*} dst # 目标文件或目录
171
+ """
172
+ src_pattern = str(src_pattern)
173
+ # 使用glob.glob来处理可能包含通配符的src
174
+ src_files = glob.glob(src_pattern)
175
+ if not src_files:
176
+ raise FileNotFoundError("File does not exist: {}".format(src_pattern))
177
+
178
+ # 判断dst是路径还是包含文件名的路径
179
+ if os.path.isdir(dst):
180
+ # 如果dst是路径,则保持源文件的文件名
181
+ dst_dir = dst
182
+ for src_file in src_files:
183
+ src_file_basename = os.path.basename(src_file)
184
+ dst_file = os.path.join(dst_dir, src_file_basename)
185
+ if os.path.exists(dst_file):
186
+ if os.path.isdir(dst_file):
187
+ shutil.rmtree(dst_file)
188
+ else:
189
+ os.remove(dst_file)
190
+ shutil.move(src_file, dst_file)
191
+ print(f"Move file or directory: {src_file} -> {dst_file}")
192
+ else:
193
+ # 如果dst包含文件名,则移动后重命名
194
+ dst_dir = os.path.dirname(dst)
195
+ os.makedirs(dst_dir, exist_ok=True)
196
+ # 只处理第一个匹配的文件
197
+ src_file = src_files[0]
198
+ dst_file = dst
199
+ if os.path.exists(dst_file):
200
+ if os.path.isdir(dst_file):
201
+ shutil.rmtree(dst_file)
202
+ else:
203
+ os.remove(dst_file)
204
+ shutil.move(src_file, dst_file)
205
+ print(f"Move and rename file or directory: {src_file} -> {dst_file}")
206
+
207
+
208
+
158
209
  # ** 重命名文件,支持通配符
159
210
  def rename_file(directory, old_str, new_str):
160
211
  """
@@ -217,13 +268,13 @@ def make_dir(directory):
217
268
  """
218
269
  Description:
219
270
  Create a directory if it does not exist
220
-
271
+
221
272
  Parameters:
222
273
  directory: The directory path to create
223
-
274
+
224
275
  Returns:
225
276
  None
226
-
277
+
227
278
  Example:
228
279
  make_dir(r"E:\\Data\\2024\\09\\17\\var1")
229
280
  """
@@ -355,7 +406,7 @@ def file_size(file_path, unit="KB"):
355
406
  if not os.path.exists(file_path):
356
407
  # return "文件不存在"
357
408
  # print(f"文件不存在: {file_path}\n返回0.0")
358
- print(f'File does not exist: {file_path}\nReturn 0.0')
409
+ print(f"File does not exist: {file_path}\nReturn 0.0")
359
410
  return 0.0
360
411
 
361
412
  # 获取文件大小(字节)
@@ -378,25 +429,25 @@ def file_size(file_path, unit="KB"):
378
429
 
379
430
 
380
431
  # ** 计算文件夹下指定相关文件的平均大小
381
- def mean_size(parent_path,fname,max_num=None,unit="KB"):
432
+ def mean_size(parent_path, fname, max_num=None, unit="KB"):
382
433
  """
383
434
  Description:
384
435
  Calculate the average size of the specified related files in the folder
385
-
436
+
386
437
  Parameters:
387
438
  parent_path: The parent path where the files are located
388
439
  fname: The file name pattern to search for
389
440
  max_num: The maximum number of files to search for
390
441
  unit: The unit of the file size, default is "KB"
391
-
442
+
392
443
  Returns:
393
444
  The average size
394
445
  """
395
446
  flist = find_file(parent_path, fname)
396
447
  if flist:
397
448
  if max_num:
398
- flist = flist[:int(max_num)]
399
- size_list = [file_size(f,unit) for f in flist if file_size(f,unit) > 0]
449
+ flist = flist[: int(max_num)]
450
+ size_list = [file_size(f, unit) for f in flist if file_size(f, unit) > 0]
400
451
  if size_list:
401
452
  return sum(size_list) / len(size_list)
402
453
  else:
@@ -405,6 +456,25 @@ def mean_size(parent_path,fname,max_num=None,unit="KB"):
405
456
  return 0.0
406
457
 
407
458
 
459
+ def replace_content(source_file, content_dict, key_value=False, target_dir=None, new_name=None):
460
+ """
461
+ 直接替换文件中的指定内容并保存到新路径
462
+
463
+ 参数:
464
+ source_file: 源文件路径
465
+ target_dir: 目标目录路径
466
+ content_dict: 要替换的内容字典 {旧内容: 新内容}
467
+ key_value: 是否按键值对方式替换参数
468
+
469
+ 返回:
470
+ bool: 替换是否成功
471
+ """
472
+ from ._script.replace_file_concent import replace_direct_content
473
+ if target_dir is None:
474
+ target_dir = os.path.dirname(source_file)
475
+ replace_direct_content(source_file, target_dir, content_dict, key_value=key_value, new_name=new_name)
476
+
477
+
408
478
  if __name__ == "__main__":
409
479
  # newpath = make_folder('D:/Data/2024/09/17/', 'var1', clear=1)
410
480
  # print(newpath)
oafuncs/oa_nc.py CHANGED
@@ -20,8 +20,6 @@ import numpy as np
20
20
  import xarray as xr
21
21
  from rich import print
22
22
 
23
- from ._script.plot_dataset import func_plot_dataset
24
-
25
23
  __all__ = ["get_var", "extract", "save", "merge", "modify", "rename", "check", "convert_longitude", "isel", "draw"]
26
24
 
27
25
 
@@ -280,15 +278,21 @@ def _modify_var(nc_file_path, variable_name, new_value):
280
278
  """
281
279
  try:
282
280
  # Open the NetCDF file
283
- dataset = nc.Dataset(nc_file_path, "r+")
284
- # Get the variable to be modified
285
- variable = dataset.variables[variable_name]
286
- # Modify the value of the variable
287
- variable[:] = new_value
288
- dataset.close()
289
- print(f"Successfully modified variable {variable_name} in {nc_file_path}.")
281
+ with nc.Dataset(nc_file_path, "r+") as dataset:
282
+ # Check if the variable exists
283
+ if variable_name not in dataset.variables:
284
+ raise ValueError(f"Variable '{variable_name}' not found in the NetCDF file.")
285
+ # Get the variable to be modified
286
+ variable = dataset.variables[variable_name]
287
+ # Check if the shape of the new value matches the variable's shape
288
+ if variable.shape != new_value.shape:
289
+ raise ValueError(f"Shape mismatch: Variable '{variable_name}' has shape {variable.shape}, "
290
+ f"but new value has shape {new_value.shape}.")
291
+ # Modify the value of the variable
292
+ variable[:] = new_value
293
+ print(f"Successfully modified variable '{variable_name}' in '{nc_file_path}'.")
290
294
  except Exception as e:
291
- print(f"An error occurred while modifying variable {variable_name} in {nc_file_path}: {e}")
295
+ print(f"An error occurred while modifying variable '{variable_name}' in '{nc_file_path}': {e}")
292
296
 
293
297
 
294
298
  def _modify_attr(nc_file_path, variable_name, attribute_name, attribute_value):
@@ -306,21 +310,18 @@ def _modify_attr(nc_file_path, variable_name, attribute_name, attribute_value):
306
310
  modify_attr('test.nc', 'temperature', 'long_name', 'Temperature in Celsius')
307
311
  """
308
312
  try:
309
- ds = nc.Dataset(nc_file_path, "r+")
310
- if variable_name not in ds.variables:
311
- raise ValueError(f"Variable '{variable_name}' not found in the NetCDF file.")
312
-
313
- variable = ds.variables[variable_name]
314
- if attribute_name in variable.ncattrs():
315
- print(f"Warning: Attribute '{attribute_name}' already exists. Replacing it.")
313
+ with nc.Dataset(nc_file_path, "r+") as ds:
314
+ # Check if the variable exists
315
+ if variable_name not in ds.variables:
316
+ raise ValueError(f"Variable '{variable_name}' not found in the NetCDF file.")
317
+ # Get the variable
318
+ variable = ds.variables[variable_name]
319
+ # Add or modify the attribute
316
320
  variable.setncattr(attribute_name, attribute_value)
317
- else:
318
- print(f"Adding attribute '{attribute_name}'...")
319
- variable.setncattr(attribute_name, attribute_value)
320
-
321
- ds.close()
321
+ print(f"Successfully modified attribute '{attribute_name}' of variable '{variable_name}' in '{nc_file_path}'.")
322
322
  except Exception as e:
323
- raise RuntimeError(f"An error occurred: {e}")
323
+ print(f"[red]Error:[/red] Failed to modify attribute '{attribute_name}' of variable '{variable_name}' "
324
+ f"in file '{nc_file_path}'. [bold]Details:[/bold] {e}")
324
325
 
325
326
 
326
327
  def modify(nc_file, var_name, attr_name=None, new_value=None):
@@ -338,10 +339,13 @@ def modify(nc_file, var_name, attr_name=None, new_value=None):
338
339
  modify('test.nc', 'temperature', 'long_name', 'Temperature in Celsius')
339
340
  modify('test.nc', 'temperature', None, np.random.rand(100, 50))
340
341
  """
341
- if attr_name is None:
342
- _modify_var(nc_file, var_name, new_value)
343
- else:
344
- _modify_attr(nc_file, var_name, attr_name, new_value)
342
+ try:
343
+ if attr_name is None:
344
+ _modify_var(nc_file, var_name, new_value)
345
+ else:
346
+ _modify_attr(nc_file, var_name, attr_name, new_value)
347
+ except Exception as e:
348
+ print(f"An error occurred while modifying '{var_name}' in '{nc_file}': {e}")
345
349
 
346
350
 
347
351
  def rename(ncfile_path, old_name, new_name):
@@ -499,6 +503,7 @@ def draw(output_dir=None, dataset=None, ncfile=None, xyzt_dims=("longitude", "la
499
503
  Example:
500
504
  draw(ncfile, output_dir, x_dim="longitude", y_dim="latitude", z_dim="level", t_dim="time", fixed_colorscale=False)
501
505
  """
506
+ from ._script.plot_dataset import func_plot_dataset
502
507
  if output_dir is None:
503
508
  output_dir = str(os.getcwd())
504
509
  if isinstance(xyzt_dims, (list, tuple)):
@@ -1,19 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  # coding=utf-8
3
- """
4
- Author: Liu Kun && 16031215@qq.com
5
- Date: 2024-11-21 09:48:00
6
- LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2025-01-11 20:09:09
8
- FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_tool\\__init__.py
9
- Description:
10
- EditPlatform: vscode
11
- ComputerInfo: XPS 15 9510
12
- SystemInfo: Windows 11
13
- Python Version: 3.12
14
- """
3
+
15
4
 
16
5
  # 会导致OAFuncs直接导入所有函数,不符合模块化设计
17
6
  from .email import *
18
7
  from .parallel import *
19
- from .time import *