Skip to content

Commit e8a959a

Browse files
authored
Merge pull request #183 from TaekyungHeo/refactor-path-rebase
Refactor to Use pathlib.Path for Path-Related Variables
2 parents 35d1489 + 1f5b36e commit e8a959a

File tree

64 files changed

+627
-651
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+627
-651
lines changed

src/cloudai/__main__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ def handle_generate_report(test_scenario: TestScenario, output_dir: Path) -> Non
247247
output_dir (Path): The path to the output directory.
248248
"""
249249
logging.info("Generating report based on system and test scenario")
250-
generator = ReportGenerator(str(output_dir))
250+
generator = ReportGenerator(output_dir)
251251
generator.generate_report(test_scenario)
252252

253253
logging.info("Report generation completed.")
@@ -274,7 +274,7 @@ def main() -> None:
274274
system, tests, test_scenario = parser.parse(tests_dir, test_scenario_path)
275275

276276
if output_dir:
277-
system.output_path = str(output_dir.absolute())
277+
system.output_path = output_dir.absolute()
278278
system.update()
279279

280280
if args.mode in ["install", "uninstall"]:

src/cloudai/_core/base_job.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
from pathlib import Path
18+
1719
from .test import Test
1820

1921

@@ -24,17 +26,17 @@ class BaseJob:
2426
Attributes
2527
id (int): The unique identifier of the job.
2628
test (Test): The test instance associated with this job.
27-
output_path (str): The path where the job's output is stored.
29+
output_path (Path): The path where the job's output is stored.
2830
terminated_by_dependency (bool): Flag to indicate if the job was terminated due to a dependency.
2931
"""
3032

31-
def __init__(self, job_id: int, test: Test, output_path: str):
33+
def __init__(self, job_id: int, test: Test, output_path: Path):
3234
"""
3335
Initialize a BaseJob instance.
3436
3537
Args:
3638
job_id (int): The unique identifier of the job.
37-
output_path (str): The path where the job's output is stored.
39+
output_path (Path): The path where the job's output is stored.
3840
test (Test): The test instance associated with the job.
3941
"""
4042
self.id = job_id

src/cloudai/_core/base_runner.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
import asyncio
1818
import logging
19-
import os
2019
import signal
2120
import sys
2221
from abc import ABC, abstractmethod
2322
from asyncio import Task
2423
from datetime import datetime
24+
from pathlib import Path
2525
from types import FrameType
2626
from typing import Dict, List, Optional
2727

@@ -44,7 +44,7 @@ class BaseRunner(ABC):
4444
mode (str): The operation mode ('dry-run', 'run').
4545
system (System): The system schema object.
4646
test_scenario (TestScenario): The test scenario to run.
47-
output_path (str): Path to the output directory.
47+
output_path (Path): Path to the output directory.
4848
monitor_interval (int): Interval in seconds for monitoring jobs.
4949
jobs (List[BaseJob]): List to track jobs created by the runner.
5050
test_to_job_map (Dict[Test, BaseJob]): Mapping from tests to their jobs.
@@ -78,21 +78,21 @@ def __init__(
7878
self.shutting_down = False
7979
self.register_signal_handlers()
8080

81-
def setup_output_directory(self, base_output_path: str) -> str:
81+
def setup_output_directory(self, base_output_path: Path) -> Path:
8282
"""
8383
Set up and return the output directory path for the runner instance.
8484
8585
Args:
86-
base_output_path (str): The base output directory.
86+
base_output_path (Path): The base output directory.
8787
8888
Returns:
89-
str: The path to the output directory.
89+
Path: The path to the output directory.
9090
"""
91-
if not os.path.exists(base_output_path):
92-
os.makedirs(base_output_path)
91+
if not base_output_path.exists():
92+
base_output_path.mkdir()
9393
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
94-
output_subpath = os.path.join(base_output_path, f"{self.test_scenario.name}_{current_time}")
95-
os.makedirs(output_subpath)
94+
output_subpath = base_output_path / f"{self.test_scenario.name}_{current_time}"
95+
output_subpath.mkdir()
9696
return output_subpath
9797

9898
def register_signal_handlers(self):
@@ -242,9 +242,9 @@ def find_dependency_free_tests(self) -> List[Test]:
242242

243243
return dependency_free_tests
244244

245-
def get_job_output_path(self, test: Test) -> str:
245+
def get_job_output_path(self, test: Test) -> Path:
246246
"""
247-
Generate and ensures the existence of the output directory for a given test.
247+
Generate and ensure the existence of the output directory for a given test.
248248
249249
It constructs the path based on the test's section name and current iteration, creating the directories if they
250250
do not exist.
@@ -253,23 +253,24 @@ def get_job_output_path(self, test: Test) -> str:
253253
test (Test): The test instance for which to generate the output directory path.
254254
255255
Returns:
256-
str: The path to the job's output directory.
256+
Path: The path to the job's output directory.
257257
258258
Raises:
259259
ValueError: If the test's section name is None.
260260
FileNotFoundError: If the base output directory does not exist.
261261
PermissionError: If there is a permission issue creating the directories.
262262
"""
263-
job_output_path = ""
263+
if not self.output_path.exists():
264+
raise FileNotFoundError(f"Output directory {self.output_path} does not exist")
265+
266+
job_output_path = Path() # avoid reportPossiblyUnboundVariable from pyright
264267

265-
if not os.path.exists(self.output_path):
266-
raise FileNotFoundError(f"Output directory {self.output_path} " f"does not exist")
267268
try:
268269
assert test.section_name is not None, "test.section_name must not be None"
269-
test_output_path = os.path.join(self.output_path, test.section_name)
270-
os.makedirs(test_output_path, exist_ok=True)
271-
job_output_path = os.path.join(test_output_path, str(test.current_iteration))
272-
os.makedirs(job_output_path, exist_ok=True)
270+
test_output_path = self.output_path / test.section_name
271+
test_output_path.mkdir()
272+
job_output_path = test_output_path / str(test.current_iteration)
273+
job_output_path.mkdir()
273274
except PermissionError as e:
274275
raise PermissionError(f"Cannot create directory {job_output_path}: {e}") from e
275276

src/cloudai/_core/command_gen_strategy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# limitations under the License.
1616

1717
from abc import abstractmethod
18+
from pathlib import Path
1819
from typing import Dict, List
1920

2021
from .test_template_strategy import TestTemplateStrategy
@@ -34,7 +35,7 @@ def gen_exec_command(
3435
cmd_args: Dict[str, str],
3536
extra_env_vars: Dict[str, str],
3637
extra_cmd_args: str,
37-
output_path: str,
38+
output_path: Path,
3839
num_nodes: int,
3940
nodes: List[str],
4041
) -> str:
@@ -46,7 +47,7 @@ def gen_exec_command(
4647
cmd_args (Dict[str, str]): Command-line arguments for the test.
4748
extra_env_vars (Dict[str, str]): Additional environment variables.
4849
extra_cmd_args (str): Additional command-line arguments.
49-
output_path (str): Path to the output directory.
50+
output_path (Path): Path to the output directory.
5051
num_nodes (int): The number of nodes to be used for the test execution.
5152
nodes (List[str]): List of nodes for test execution, optional.
5253

src/cloudai/_core/grader.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import csv
1818
import logging
19-
import os
19+
from pathlib import Path
2020
from typing import Dict, List
2121

2222
from .test import Test
@@ -28,11 +28,11 @@ class Grader:
2828
Class responsible for grading the performance of tests within a test scenario and generating a report.
2929
3030
Attributes
31-
output_path (str): The path where the performance results are stored.
31+
output_path (Path): The path where the performance results are stored.
3232
logger (logging.Logger): Logger for the class, used to log messages related to the grading process.
3333
"""
3434

35-
def __init__(self, output_path: str) -> None:
35+
def __init__(self, output_path: Path) -> None:
3636
self.output_path = output_path
3737

3838
def grade(self, test_scenario: TestScenario) -> str:
@@ -57,7 +57,7 @@ def grade(self, test_scenario: TestScenario) -> str:
5757
if not section_name:
5858
logging.warning(f"Missing section name for test {test.name}")
5959
continue
60-
test_output_dir = os.path.join(self.output_path, section_name)
60+
test_output_dir = self.output_path / section_name
6161
perfs = self._get_perfs_from_subdirs(test_output_dir, test)
6262
avg_perf = sum(perfs) / len(perfs) if perfs else 0
6363
test_perfs[test.name] = perfs + [avg_perf]
@@ -69,24 +69,22 @@ def grade(self, test_scenario: TestScenario) -> str:
6969
self._save_report(report)
7070
return report
7171

72-
def _get_perfs_from_subdirs(self, directory_path: str, test: Test) -> List[float]:
72+
def _get_perfs_from_subdirs(self, directory_path: Path, test: Test) -> List[float]:
7373
"""
7474
Average performance values from subdirectories within a given path, according to the test's grading template.
7575
7676
Args:
77-
directory_path (str): Directory path.
77+
directory_path (Path): Directory path.
7878
test (Test): The test to grade.
7979
8080
Returns:
8181
List[float]: A list of performance values.
8282
"""
8383
perfs = []
84-
for subdir in os.listdir(directory_path):
85-
if subdir.isdigit():
86-
subdir_path = os.path.join(directory_path, subdir)
87-
if os.path.isdir(subdir_path):
88-
perf = test.test_template.grade(subdir_path, test.ideal_perf)
89-
perfs.append(perf)
84+
for subdir in directory_path.iterdir():
85+
if subdir.is_dir() and subdir.name.isdigit():
86+
perf = test.test_template.grade(subdir, test.ideal_perf)
87+
perfs.append(perf)
9088
return perfs
9189

9290
def _generate_report(self, test_perfs: Dict[str, List[float]], overall_avg: float) -> str:
@@ -102,7 +100,7 @@ def _generate_report(self, test_perfs: Dict[str, List[float]], overall_avg: floa
102100
"""
103101
report_lines = ["Test Performance Report:"]
104102
for test, perfs in test_perfs.items():
105-
report_lines.append(f"{test}: Min: {min(perfs[:-1])}, " f"Max: {max(perfs[:-1])}, " f"Avg: {perfs[-1]}")
103+
report_lines.append(f"{test}: Min: {min(perfs[:-1])}, Max: {max(perfs[:-1])}, Avg: {perfs[-1]}")
106104
report_lines.append(f"Overall Average Performance: {overall_avg}")
107105
return "\n".join(report_lines)
108106

@@ -113,8 +111,8 @@ def _save_report(self, report: str) -> None:
113111
Args:
114112
report (str): The report to save.
115113
"""
116-
report_path = os.path.join(self.output_path, "performance_report.csv")
117-
with open(report_path, "w", newline="") as file:
114+
report_path = self.output_path / "performance_report.csv"
115+
with report_path.open("w", newline="") as file:
118116
writer = csv.writer(file)
119117
for line in report.split("\n"):
120118
writer.writerow([line])

src/cloudai/_core/grading_strategy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# limitations under the License.
1616

1717
from abc import abstractmethod
18+
from pathlib import Path
1819

1920
from .test_template_strategy import TestTemplateStrategy
2021

@@ -23,12 +24,12 @@ class GradingStrategy(TestTemplateStrategy):
2324
"""Abstract class for grading test performance."""
2425

2526
@abstractmethod
26-
def grade(self, directory_path: str, ideal_perf: float) -> float:
27+
def grade(self, directory_path: Path, ideal_perf: float) -> float:
2728
"""
2829
Grades the performance of a test.
2930
3031
Args:
31-
directory_path (str): Path to the directory containing the test's output.
32+
directory_path (Path): Path to the directory containing the test's output.
3233
ideal_perf (float): The ideal performance value for comparison.
3334
3435
Returns:

src/cloudai/_core/job_status_retrieval_strategy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# limitations under the License.
1616

1717
from abc import abstractmethod
18+
from pathlib import Path
1819

1920
from .job_status_result import JobStatusResult
2021

@@ -23,12 +24,12 @@ class JobStatusRetrievalStrategy:
2324
"""Abstract class to define a strategy for retrieving job statuses from a given output directory."""
2425

2526
@abstractmethod
26-
def get_job_status(self, output_path: str) -> JobStatusResult:
27+
def get_job_status(self, output_path: Path) -> JobStatusResult:
2728
"""
2829
Retrieve the job status from a specified output directory.
2930
3031
Args:
31-
output_path (str): Path to the output directory.
32+
output_path (Path): Path to the output directory.
3233
3334
Returns:
3435
JobStatusResult: The result containing the job status and an optional error message.

src/cloudai/_core/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def parse(
6565
if not test_path.exists():
6666
raise FileNotFoundError(f"Test path '{test_path}' not found.")
6767

68-
system_parser = SystemParser(str(self.system_config_path))
68+
system_parser = SystemParser(self.system_config_path)
6969
system = system_parser.parse()
7070
logging.debug("Parsed system config")
7171

src/cloudai/_core/report_generation_strategy.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# limitations under the License.
1616

1717
from abc import abstractmethod
18+
from pathlib import Path
1819
from typing import Optional
1920

2021

@@ -27,26 +28,26 @@ class ReportGenerationStrategy:
2728
"""
2829

2930
@abstractmethod
30-
def can_handle_directory(self, directory_path: str) -> bool:
31+
def can_handle_directory(self, directory_path: Path) -> bool:
3132
"""
3233
Determine if the strategy can handle the directory.
3334
3435
Args:
35-
directory_path (str): Path to the directory.
36+
directory_path (Path): Path to the directory.
3637
3738
Returns:
3839
bool: True if can handle, False otherwise.
3940
"""
4041
pass
4142

4243
@abstractmethod
43-
def generate_report(self, test_name: str, directory_path: str, sol: Optional[float] = None) -> None:
44+
def generate_report(self, test_name: str, directory_path: Path, sol: Optional[float] = None) -> None:
4445
"""
4546
Generate a report from the directory.
4647
4748
Args:
4849
test_name (str): The name of the test.
49-
directory_path (str): Path to the directory.
50+
directory_path (Path): Path to the directory.
5051
sol (Optional[float]): Speed-of-light performance for reference.
5152
"""
5253
pass

src/cloudai/_core/system.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717

1818
from abc import ABC, abstractmethod
19+
from pathlib import Path
1920

2021

2122
class System(ABC):
@@ -25,24 +26,18 @@ class System(ABC):
2526
Attributes
2627
name (str): Unique name of the system.
2728
scheduler (str): Type of scheduler used by the system, determining the specific subclass of System to be used.
28-
output_path (str): Path to the output directory.
29+
output_path (Path): Path to the output directory.
2930
monitor_interval (int): Interval in seconds for monitoring jobs.
3031
"""
3132

32-
def __init__(
33-
self,
34-
name: str,
35-
scheduler: str,
36-
output_path: str,
37-
monitor_interval: int = 1,
38-
) -> None:
33+
def __init__(self, name: str, scheduler: str, output_path: Path, monitor_interval: int = 1) -> None:
3934
"""
4035
Initialize a System instance.
4136
4237
Args:
4338
name (str): Name of the system.
4439
scheduler (str): Type of scheduler used by the system.
45-
output_path (str): Path to the output directory.
40+
output_path (Path): Path to the output directory.
4641
monitor_interval (int): Interval in seconds for monitoring jobs.
4742
"""
4843
self.name = name

0 commit comments

Comments
 (0)