forked from lowRISC/dvsim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfs.py
More file actions
133 lines (102 loc) · 3.77 KB
/
fs.py
File metadata and controls
133 lines (102 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
"""file system utilities."""
import os
import shutil
import sys
from collections.abc import Sequence
from datetime import datetime
from pathlib import Path
from dvsim.logging import log
__all__ = (
"TS_FORMAT",
"TS_FORMAT_LONG",
"mk_path",
"mk_symlink",
"rm_path",
)
# Timestamp format when creating directory backups.
TS_FORMAT = "%Y%m%d_%H%M%S"
# Timestamp format when generating reports.
TS_FORMAT_LONG = "%A %B %d %Y %H:%M:%S UTC"
def rm_path(path: Path, *, ignore_error: bool = False) -> None:
"""Remove the specified path if it exists.
'path' is a Path-like object. If it does not exist, the function simply
returns. If 'ignore_error' is set, then exception caught by the remove
operation is raised, else it is ignored.
"""
# interface claims to be Path-like, but to be sure not to to introduce
# regressions convert to Path anyway.
path = Path(path)
# Nothing to do
if not path.exists():
return
if path.is_file() or path.is_symlink():
path.unlink()
return
try:
shutil.rmtree(path)
except OSError:
log.exception("Failed to remove %s:\n", path)
if not ignore_error:
raise
def mk_path(path: Path) -> None:
"""Create the specified path if it does not exist.
'path' is a Path-like object. If it does exist, the function simply
returns. If it does not exist, the function creates the path and its
parent dictories if necessary.
"""
try:
Path(path).mkdir(parents=True, exist_ok=True)
except PermissionError:
log.exception("Failed to create directory %s", path)
sys.exit(1)
def mk_symlink(*, path: Path, link: Path) -> None:
"""Create a symlink from the given path.
'link' is a Path-like object. If it does exist, remove the existing link
and create a new symlink with this given path.
If it does not exist, the function creates the symlink with the given path.
"""
if link.exists():
if not link.is_symlink():
log.error(
"Trying to create symlink %s, existing non symlink file found",
link,
)
raise TypeError
link.unlink()
link.symlink_to(path)
def clean_odirs(
odir: Path,
max_odirs: int,
ts_format: str = TS_FORMAT,
) -> Sequence[Path | str]:
"""Clean previous output directories.
When running jobs, we may want to maintain a limited history of
previous invocations. This method finds and deletes the output
directories at the base of input arg 'odir' with the oldest timestamps,
if that limit is reached. It returns a list of directories that
remain after deletion.
"""
odir = Path(odir)
if odir.exists():
# If output directory exists, back it up.
ts = datetime.fromtimestamp(os.stat(odir).st_ctime).strftime(ts_format)
# Prior to Python 3.9, shutil may run into an error when passing in
# Path objects (see https://bugs.python.org/issue32689). While this
# has been fixed in Python 3.9, string casts are added so that this
# also works with older versions.
shutil.move(str(odir), str(odir.with_name(ts)))
# Get list of past output directories sorted by creation time.
pdir = odir.resolve().parent
if not pdir.exists():
return []
dirs = sorted(
[old for old in pdir.iterdir() if (old.is_dir() and old != "summary")],
key=os.path.getctime,
reverse=True,
)
for old in dirs[max(0, max_odirs - 1) :]:
shutil.rmtree(old, ignore_errors=True)
return [] if max_odirs == 0 else dirs[: max_odirs - 1]