ion-CSP 2.2.0__tar.gz → 2.2.1__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 (106) hide show
  1. {ion_csp-2.2.0 → ion_csp-2.2.1}/CHANGELOG.md +14 -0
  2. {ion_csp-2.2.0 → ion_csp-2.2.1}/PKG-INFO +2 -2
  3. {ion_csp-2.2.0 → ion_csp-2.2.1}/pyproject.toml +1 -1
  4. {ion_csp-2.2.0 → ion_csp-2.2.1}/requirements.txt +1 -1
  5. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/__init__.py +2 -2
  6. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/convert_SMILES.py +5 -3
  7. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/mlp_opt.py +14 -9
  8. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/read_mlp_density.py +2 -2
  9. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/main_EE.py +2 -0
  10. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/task_manager.py +3 -1
  11. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP.egg-info/PKG-INFO +2 -2
  12. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP.egg-info/SOURCES.txt +0 -1
  13. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP.egg-info/requires.txt +1 -1
  14. ion_csp-2.2.0/tests/test1_task_manager1.py +0 -121
  15. {ion_csp-2.2.0 → ion_csp-2.2.1}/.dockerignore +0 -0
  16. {ion_csp-2.2.0 → ion_csp-2.2.1}/.github/workflows/conda-env-build.yml +0 -0
  17. {ion_csp-2.2.0 → ion_csp-2.2.1}/.github/workflows/pypi-publish.yml +0 -0
  18. {ion_csp-2.2.0 → ion_csp-2.2.1}/.gitignore +0 -0
  19. {ion_csp-2.2.0 → ion_csp-2.2.1}/.vscode/settings.json +0 -0
  20. {ion_csp-2.2.0 → ion_csp-2.2.1}/CODE_OF_CONDUCT.md +0 -0
  21. {ion_csp-2.2.0 → ion_csp-2.2.1}/CONTRIBUTING.md +0 -0
  22. {ion_csp-2.2.0 → ion_csp-2.2.1}/Dockerfile +0 -0
  23. {ion_csp-2.2.0 → ion_csp-2.2.1}/LICENSE +0 -0
  24. {ion_csp-2.2.0 → ion_csp-2.2.1}/MANIFEST.in +0 -0
  25. {ion_csp-2.2.0 → ion_csp-2.2.1}/README.md +0 -0
  26. {ion_csp-2.2.0 → ion_csp-2.2.1}/SECURITY.md +0 -0
  27. {ion_csp-2.2.0 → ion_csp-2.2.1}/config/complete_config.yaml +0 -0
  28. {ion_csp-2.2.0 → ion_csp-2.2.1}/config/simple_config.yaml +0 -0
  29. {ion_csp-2.2.0 → ion_csp-2.2.1}/config/usage.md +0 -0
  30. {ion_csp-2.2.0 → ion_csp-2.2.1}/docs/example_usage_CSP.py +0 -0
  31. {ion_csp-2.2.0 → ion_csp-2.2.1}/docs/example_usage_EE.py +0 -0
  32. {ion_csp-2.2.0 → ion_csp-2.2.1}/docs/index.md +0 -0
  33. {ion_csp-2.2.0 → ion_csp-2.2.1}/docs/usage.md +0 -0
  34. {ion_csp-2.2.0 → ion_csp-2.2.1}/environment.yml +0 -0
  35. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/example_1/config.yaml +0 -0
  36. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/example_1/ions.csv +0 -0
  37. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/example_2/combo_1/ACEGUL.gjf +0 -0
  38. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/example_2/combo_1/AGIDOM.gjf +0 -0
  39. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/example_2/combo_1/config.yaml +0 -0
  40. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/example_usage.py +0 -0
  41. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/server/example_local_machine.yaml +0 -0
  42. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/server/example_local_resources.yaml +0 -0
  43. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/server/example_remote_machine.yaml +0 -0
  44. {ion_csp-2.2.0 → ion_csp-2.2.1}/examples/server/example_remote_resources.yaml +0 -0
  45. {ion_csp-2.2.0 → ion_csp-2.2.1}/makefile +0 -0
  46. {ion_csp-2.2.0 → ion_csp-2.2.1}/model/model.pt +0 -0
  47. {ion_csp-2.2.0 → ion_csp-2.2.1}/model/options/README.md +0 -0
  48. {ion_csp-2.2.0 → ion_csp-2.2.1}/model/options/model.ckpt-4000000.pt +0 -0
  49. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/INCAR_0 +0 -0
  50. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/INCAR_1 +0 -0
  51. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/INCAR_2 +0 -0
  52. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/INCAR_3 +0 -0
  53. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/POTCAR_C +0 -0
  54. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/POTCAR_H +0 -0
  55. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/POTCAR_N +0 -0
  56. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/POTCAR_O +0 -0
  57. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/g16_sub.sh +0 -0
  58. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/sub_final.sh +0 -0
  59. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/sub_ori.sh +0 -0
  60. {ion_csp-2.2.0 → ion_csp-2.2.1}/param/sub_supple.sh +0 -0
  61. {ion_csp-2.2.0 → ion_csp-2.2.1}/scripts/CLI.py +0 -0
  62. {ion_csp-2.2.0 → ion_csp-2.2.1}/scripts/CLI.sh +0 -0
  63. {ion_csp-2.2.0 → ion_csp-2.2.1}/scripts/main_CSP.sh +0 -0
  64. {ion_csp-2.2.0 → ion_csp-2.2.1}/scripts/main_EE.sh +0 -0
  65. {ion_csp-2.2.0 → ion_csp-2.2.1}/scripts/update_changelog.py +0 -0
  66. {ion_csp-2.2.0 → ion_csp-2.2.1}/setup.cfg +0 -0
  67. {ion_csp-2.2.0 → ion_csp-2.2.1}/setup.py +0 -0
  68. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/__init__.py +0 -0
  69. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/__main__.py +0 -0
  70. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/empirical_estimate.py +0 -0
  71. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/gen_opt.py +0 -0
  72. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/identify_molecules.py +0 -0
  73. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/log_and_time.py +0 -0
  74. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/model/model.pt +0 -0
  75. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/model/options/README.md +0 -0
  76. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/model/options/model.ckpt-4000000.pt +0 -0
  77. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/INCAR_0 +0 -0
  78. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/INCAR_1 +0 -0
  79. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/INCAR_2 +0 -0
  80. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/INCAR_3 +0 -0
  81. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/POTCAR_C +0 -0
  82. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/POTCAR_H +0 -0
  83. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/POTCAR_N +0 -0
  84. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/POTCAR_O +0 -0
  85. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/g16_sub.sh +0 -0
  86. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/sub_final.sh +0 -0
  87. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/sub_ori.sh +0 -0
  88. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/param/sub_supple.sh +0 -0
  89. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/__init__.py +0 -0
  90. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/main_CSP.py +0 -0
  91. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/run_convert_SMILES.py +0 -0
  92. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/run_empirical_estimate.py +0 -0
  93. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/run_gen_opt.py +0 -0
  94. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/run_read_mlp_density.py +0 -0
  95. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/run_upload_download.py +0 -0
  96. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/run/run_vasp_processing.py +0 -0
  97. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/steps_opt_monitor.sh +0 -0
  98. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/upload_download.py +0 -0
  99. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP/vasp_processing.py +0 -0
  100. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP.egg-info/dependency_links.txt +0 -0
  101. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP.egg-info/entry_points.txt +0 -0
  102. {ion_csp-2.2.0 → ion_csp-2.2.1}/src/ion_CSP.egg-info/top_level.txt +0 -0
  103. {ion_csp-2.2.0 → ion_csp-2.2.1}/tests/__init__.py +0 -0
  104. {ion_csp-2.2.0 → ion_csp-2.2.1}/tests/test_log_and_time.py +0 -0
  105. {ion_csp-2.2.0 → ion_csp-2.2.1}/tests/test_task_manager.py +0 -0
  106. {ion_csp-2.2.0 → ion_csp-2.2.1}/uv.lock +0 -0
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## Latest Changes
4
4
 
5
+ ### 1754cc7 (2025-06-30)
6
+
7
+ Update version number to 2.2.1, fix file copying logic in main_EE workflow, update mlp_opt.py to use MatterSimCalculator to develop calculation efficiency.
8
+
9
+ ### d6abfb5 (2025-06-28)
10
+
11
+ Optimize the environment variable settings in mlp_opt.py to disable numpy's multithreading and ensure the stability of multi-threaded computation.
12
+
13
+ ## V2.2.0
14
+
15
+ ### 18b9eba (2025-06-28)
16
+
17
+ Optimize the file copying logic in vasp_processing to ensure that copying operations are only performed when the file exists; Update sorting parameters to support 'NC_ratio' and modify the relevant CSV file names accordingly.
18
+
5
19
  ### 14ffac2 (2025-06-27)
6
20
 
7
21
  Update version number to 2.2.0, optimize file replication logic in VASP processing, add error handling to ensure errors are recorded when files do not exist, modify README and CHANGELOG to reflect the latest changes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ion_CSP
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Crystal Structure Design Software Based on Molecular/Ionic Configuration.
5
5
  Home-page: https://github.com/bagabaga007/ion_CSP
6
6
  Author: Ze Yang
@@ -14,7 +14,7 @@ Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: ase==3.23.0
16
16
  Requires-Dist: scipy==1.15.2
17
- Requires-Dist: deepmd-kit==3.0.1
17
+ Requires-Dist: deepmd-kit>=3.0.1
18
18
  Requires-Dist: torch==2.5.0
19
19
  Requires-Dist: dpdispatcher==0.6.7
20
20
  Requires-Dist: numpy==1.26.4
@@ -20,7 +20,7 @@ requires-python = ">=3.11"
20
20
  dependencies = [
21
21
  "ase==3.23.0",
22
22
  "scipy==1.15.2",
23
- "deepmd-kit==3.0.1",
23
+ "deepmd-kit>=3.0.1",
24
24
  "torch==2.5.0",
25
25
  "dpdispatcher==0.6.7",
26
26
  "numpy==1.26.4",
@@ -1,6 +1,6 @@
1
1
  ase==3.23.0
2
2
  scipy==1.15.2
3
- deepmd-kit==3.0.1
3
+ deepmd-kit>=3.0.1
4
4
  torch==2.5.0
5
5
  dpdispatcher>=0.6.7
6
6
  numpy==1.26.4
@@ -1,8 +1,8 @@
1
1
  __author__ = "Ze Yang"
2
2
  __contact__ = "yangze1995007@163.com"
3
3
  __license__ = "MIT"
4
- __version__ = "2.2.0"
5
- __date__ = "2025-06-27"
4
+ __version__ = "2.2.1"
5
+ __date__ = "2025-06-30"
6
6
 
7
7
 
8
8
  try:
@@ -131,7 +131,7 @@ class SmilesProcessing:
131
131
  f"{self.converted_dir}/charge_{charge}"
132
132
  )
133
133
  os.makedirs(charge_dir, exist_ok=True)
134
- # 通过SMILE_to函数依次处理SMILES码
134
+ # 通过_convert_SMILES函数依次处理SMILES码
135
135
  for _, row in group.iterrows():
136
136
  result_code, basename = self._convert_SMILES(
137
137
  dir=charge_dir,
@@ -238,8 +238,10 @@ class SmilesProcessing:
238
238
  for folder in folders:
239
239
  folder_dir = os.path.join(self.converted_dir, folder)
240
240
  if not os.path.exists(folder_dir):
241
- logging.error(f'Provided folder {folder} is not in the directory {folder_dir}')
242
- continue
241
+ folder_dir = os.path.join(self.base_dir, folder)
242
+ if not os.path.exists(folder_dir):
243
+ logging.error(f'Provided folder {folder} is not either in the work directory or the converted directory.\n')
244
+ continue
243
245
  # 获取文件夹中所有以 .gjf 结尾的文件
244
246
  gjf_files = [
245
247
  f for f in os.listdir(folder_dir) if f.endswith(".gjf")
@@ -1,23 +1,27 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
3
  import os
4
+ os.environ["MKL_NUM_THREADS"] = "1"
5
+ os.environ["NUMEXPR_NUM_THREADS"] = "1"
6
+ os.environ["OMP_NUM_THREADS"] = "1"
7
+
4
8
  import time
5
9
  import numpy as np
6
10
  import multiprocessing
7
11
  from ase.io.vasp import read_vasp
8
12
  from ase.optimize import LBFGS
9
- from ase.constraints import UnitCellFilter
13
+ # from ase.constraints import UnitCellFilter
14
+ from ase.filters import ExpCellFilter
10
15
  from deepmd.calculator import DP
16
+ # from mattersim.forcefield import MatterSimCalculator
11
17
 
12
18
  # 根据脚本位置确定model.pt文件的位置, 减少错误发生
13
19
  base_dir = os.path.dirname(__file__)
14
20
  relative_path = './model.pt'
15
21
  file_path = os.path.join(base_dir, relative_path)
16
22
  calc = DP(file_path)
17
- """
18
- structure optimization with DP model and ASE
19
- PSTRESS and fmax should exist in input.dat
20
- """
23
+ # calc = MatterSimCalculator(device="cuda")
24
+
21
25
 
22
26
  def get_element_num(elements):
23
27
  """
@@ -51,7 +55,7 @@ def write_CONTCAR(element, ele, lat, pos, index):
51
55
  pos: atomic positions in direct coordinates
52
56
  index: index for the output file"""
53
57
  f = open(f'{base_dir}/CONTCAR_'+str(index),'w')
54
- f.write('ASE-DPKit-Optimization\n')
58
+ f.write('ASE-MLP-Optimization\n')
55
59
  f.write('1.0\n')
56
60
  for i in range(3):
57
61
  f.write('%15.10f %15.10f %15.10f\n' % tuple(lat[i]))
@@ -139,7 +143,7 @@ def get_indexes():
139
143
 
140
144
  def run_opt(index: int):
141
145
  """
142
- Using the ASE&DP to Optimize Configures
146
+ Using the ASE & MLP to Optimize Configures
143
147
  :params
144
148
  index: index of the POSCAR file to be optimized
145
149
  """
@@ -147,7 +151,7 @@ def run_opt(index: int):
147
151
  os.system(f'mv {base_dir}/OUTCAR {base_dir}/OUTCAR-last')
148
152
  fmax, pstress = 0.03, 0
149
153
 
150
- print('Start to Optimize Structures by DP----------')
154
+ print('Start to Optimize Structures by MLP----------')
151
155
 
152
156
  Opt_Step = 2000
153
157
  start = time.time()
@@ -158,7 +162,8 @@ def run_opt(index: int):
158
162
  aim_stress = 1.0 * pstress * 0.01 * 0.6242 / 10.0
159
163
  atoms = read_vasp('POSCAR_'+str(index))
160
164
  atoms.calc = calc
161
- ucf = UnitCellFilter(atoms, scalar_pressure=aim_stress)
165
+ # ucf = UnitCellFilter(atoms, scalar_pressure=aim_stress)
166
+ ucf = ExpCellFilter(atoms, scalar_pressure=aim_stress)
162
167
  # optimization
163
168
  opt = LBFGS(ucf)
164
169
  opt.run(fmax=fmax,steps=Opt_Step)
@@ -8,7 +8,7 @@ from ion_CSP.identify_molecules import identify_molecules, molecules_information
8
8
 
9
9
  class ReadMlpDensity:
10
10
 
11
- def __init__(self, work_dir:str):
11
+ def __init__(self, work_dir:str, folder:str = '2_mlp_optimized'):
12
12
  """
13
13
  This class is designed to read and process MLP optimized files, specifically CONTCAR files, to calculate and sort their densities.
14
14
  The class also provides functionality to process these files using phonopy for symmetry analysis and primitive cell generation.
@@ -20,7 +20,7 @@ class ReadMlpDensity:
20
20
  self.base_dir = work_dir
21
21
  os.chdir(self.base_dir)
22
22
  # 寻找同一目录下的2_mlp_optimized文件夹
23
- self.folder_dir = os.path.join(self.base_dir, '2_mlp_optimized')
23
+ self.folder_dir = os.path.join(self.base_dir, folder)
24
24
  self.max_density_dir = os.path.join(self.folder_dir, 'max_density')
25
25
  self.primitive_cell_dir = os.path.join(self.folder_dir, 'primitive_cell')
26
26
  print(f"Processing MLP CONTCARs in {self.folder_dir}")
@@ -109,6 +109,8 @@ def combination_task(work_dir, config):
109
109
  elif config["empirical_estimate"]["sort_by"] == "nitrogen":
110
110
  # 最终将预测的离子晶体氮含量以及对应的组分输出到 .csv 文件并根据氮含量从大到小排序
111
111
  combination.nitrogen_content_estimate()
112
+ elif config["empirical_estimate"]["sort_by"] == "NC_ratio":
113
+ combination.carbon_nitrogen_ratio_estimate()
112
114
  # 基于排序依据 sort_by 对应的 .csv 文件创建 combo_n 文件夹,并复制相应的 .gjf 结构文件。
113
115
  if config["empirical_estimate"]["make_combo_dir"]:
114
116
  combination.make_combo_dir(
@@ -219,7 +219,9 @@ class TaskManager:
219
219
  def task_runner(self, module: str, work_dir: str):
220
220
  """任务执行器 - Task execution handler"""
221
221
  work_dir = Path(work_dir)
222
- work_dir.mkdir(exist_ok=True)
222
+ if not os.path.exists(work_dir):
223
+ print(f"Work directory {work_dir} does not exist")
224
+ return
223
225
 
224
226
  console_log = work_dir / f"main_{module}_console.log"
225
227
  pid_file = work_dir / "pid.txt"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ion_CSP
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Crystal Structure Design Software Based on Molecular/Ionic Configuration.
5
5
  Home-page: https://github.com/bagabaga007/ion_CSP
6
6
  Author: Ze Yang
@@ -14,7 +14,7 @@ Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: ase==3.23.0
16
16
  Requires-Dist: scipy==1.15.2
17
- Requires-Dist: deepmd-kit==3.0.1
17
+ Requires-Dist: deepmd-kit>=3.0.1
18
18
  Requires-Dist: torch==2.5.0
19
19
  Requires-Dist: dpdispatcher==0.6.7
20
20
  Requires-Dist: numpy==1.26.4
@@ -100,6 +100,5 @@ src/ion_CSP/run/run_read_mlp_density.py
100
100
  src/ion_CSP/run/run_upload_download.py
101
101
  src/ion_CSP/run/run_vasp_processing.py
102
102
  tests/__init__.py
103
- tests/test1_task_manager1.py
104
103
  tests/test_log_and_time.py
105
104
  tests/test_task_manager.py
@@ -1,6 +1,6 @@
1
1
  ase==3.23.0
2
2
  scipy==1.15.2
3
- deepmd-kit==3.0.1
3
+ deepmd-kit>=3.0.1
4
4
  torch==2.5.0
5
5
  dpdispatcher==0.6.7
6
6
  numpy==1.26.4
@@ -1,121 +0,0 @@
1
- import os
2
- import time
3
- import pytest
4
- from pathlib import Path
5
- from unittest.mock import patch, MagicMock, Mock
6
- from ion_CSP.task_manager import TaskManager
7
-
8
-
9
- @pytest.fixture(scope="session", autouse=True)
10
- def set_working_directory():
11
- """设置工作目录为项目根目录"""
12
- project_root = Path(__file__).resolve().parent # 假设测试文件在 tests 目录下
13
- os.chdir(project_root)
14
- yield
15
-
16
- @pytest.fixture
17
- def task_manager(tmp_path):
18
- # 使用临时目录替代工作目录,避免污染真实文件系统
19
- tm = TaskManager()
20
- tm.workspace = tmp_path
21
- tm.log_dir = tmp_path / tm.log_base
22
- tm.log_dir.mkdir(parents=True, exist_ok=True)
23
- return tm
24
-
25
-
26
- def test_task_runner_creates_log_and_pid(monkeypatch, tmp_path, task_manager):
27
- # 模拟 subprocess.Popen,避免真正启动子进程
28
- mock_process = MagicMock()
29
- mock_process.pid = 9999
30
-
31
- def fake_popen(*args, **kwargs):
32
- return mock_process
33
-
34
- monkeypatch.setattr("subprocess.Popen", fake_popen)
35
-
36
- # 调用 task_runner
37
- work_dir = tmp_path / "work"
38
- task_manager.task_runner("CSP", str(work_dir))
39
-
40
- # 检查日志目录下是否生成了符号链接(symlink)
41
- symlink_path = task_manager.log_dir / f"CSP_{mock_process.pid}.log"
42
- assert symlink_path.exists()
43
- assert symlink_path.is_symlink()
44
-
45
-
46
- def test_get_related_tasks_basic(task_manager, monkeypatch):
47
- """测试标准日志文件处理"""
48
- # 验证 log_dir 是 Path 对象
49
- assert isinstance(task_manager.log_dir, Path), "log_dir 必须是 pathlib.Path 对象"
50
- log_dir = task_manager.log_dir
51
- log_dir.mkdir(parents=True, exist_ok=True)
52
- print(log_dir)
53
- # 模拟的时间戳
54
- now = time.time()
55
-
56
- # 创建模拟的 stat 对象
57
- mock_stats = {
58
- 8: Mock(st_mtime=now - 8000),
59
- 5: Mock(st_mtime=now - 5000),
60
- 3: Mock(st_mtime=now - 3000),
61
- 1: Mock(st_mtime=now - 1000),
62
- }
63
-
64
- # 模拟 os.stat 行为
65
- def mock_os_stat(path, **kwargs):
66
- try:
67
- base_name = path.name.split(".")[0]
68
- pid = int(base_name.split("_")[-1])
69
- return mock_stats.get(pid, Mock(st_mtime=0))
70
- except (IndexError, ValueError):
71
- return Mock(st_mtime=0)
72
-
73
- monkeypatch.setattr("os.stat", mock_os_stat)
74
- monkeypatch.setattr(task_manager, "_is_pid_running", lambda _: True)
75
-
76
- # 创建日志文件
77
- for pid in [8, 5, 3, 1]:
78
- log_file = log_dir / f"CSP_{pid:04d}.log"
79
- log_file.touch()
80
-
81
- # 调用方法
82
- tasks = task_manager.get_related_tasks()
83
- print(tasks)
84
- # 断言
85
- assert len(tasks) == 4
86
- for task in tasks:
87
- assert task["pid"] in [8, 5, 3, 1]
88
- assert task["status"] == "Running"
89
-
90
-
91
- def test_main_menu_navigation(monkeypatch, task_manager):
92
- """测试主菜单导航"""
93
- monkeypatch.setattr("builtins.input", MagicMock(side_effect=["3", "q"]))
94
- with patch("sys.stdout") as mock_stdout:
95
- task_manager.main_menu()
96
- output = mock_stdout.getvalue()
97
- assert "View Logs" in output
98
- assert "Terminate Tasks" in output
99
-
100
-
101
- # def test_get_related_tasks_process_status(task_manager, monkeypatch):
102
- # """测试进程状态判断"""
103
- # test_file = task_manager.log_dir / "CSP_0001.log"
104
- # test_file.touch()
105
-
106
- # # 模拟 os.stat
107
- # mock_stat = Mock(st_mtime=time.time())
108
- # monkeypatch.setattr("os.stat", lambda _: mock_stat)
109
-
110
- # # 模拟进程状态
111
- # monkeypatch.setattr(task_manager, "_is_pid_running", lambda pid: pid % 2 == 0)
112
-
113
- # # 执行测试
114
- # tasks = task_manager.get_related_tasks()
115
-
116
- # # 验证结果
117
- # assert len(tasks) == 1
118
- # assert tasks[0]["status"] == "Terminated" # PID=1 为奇数,应标记为终止
119
-
120
-
121
- # 你可以继续添加针对 _safe_kill、_is_pid_running 等方法的单元测试
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes