Skip to content

Commit f7f4fba

Browse files
authored
Merge pull request #307 from andrewdnolan/speed_up_permission_updates
Speed up permission updates
2 parents 6d33dc1 + 1c948a4 commit f7f4fba

File tree

4 files changed

+76
-89
lines changed

4 files changed

+76
-89
lines changed

conda/meta.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ requirements:
2323
- jinja2
2424
- lxml
2525
- pyyaml
26-
- progressbar2
2726
- rsync
2827
- termcolor
28+
- tqdm
2929

3030
test:
3131
source_files:

mache/permissions.py

Lines changed: 73 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import grp
22
import os
33
import stat
4+
from concurrent.futures import ThreadPoolExecutor, as_completed
5+
from pathlib import Path
46

5-
import progressbar
7+
from tqdm import tqdm
68

79

810
def update_permissions( # noqa: C901
@@ -11,6 +13,7 @@ def update_permissions( # noqa: C901
1113
show_progress=True,
1214
group_writable=False,
1315
other_readable=True,
16+
workers=4,
1417
):
1518
"""
1619
Update the group that a directory belongs to along with the "group" and
@@ -33,6 +36,9 @@ def update_permissions( # noqa: C901
3336
other_readable : bool, optional
3437
Whether to allow world read (and, where appropriate, execute)
3538
permissions
39+
40+
workers : int, optional
41+
Number of threads to parallelize across
3642
"""
3743

3844
if isinstance(base_paths, str):
@@ -76,92 +82,73 @@ def update_permissions( # noqa: C901
7682
and dir_stat.st_uid == new_uid
7783
and dir_stat.st_gid == new_gid
7884
):
79-
continue
85+
pass
8086

8187
try:
8288
os.chown(directory, new_uid, new_gid)
8389
os.chmod(directory, exec_perm)
84-
except OSError:
85-
continue
86-
87-
files_and_dirs = []
88-
for base in directories:
89-
for _, dirs, files in os.walk(base):
90-
files_and_dirs.extend(dirs)
91-
files_and_dirs.extend(files)
92-
93-
if show_progress:
94-
widgets = [
95-
progressbar.Percentage(),
96-
' ',
97-
progressbar.Bar(),
98-
' ',
99-
progressbar.ETA(),
100-
]
101-
bar = progressbar.ProgressBar(
102-
widgets=widgets, maxval=len(files_and_dirs), maxerror=False
103-
).start()
104-
else:
105-
bar = None
106-
progress = 0
107-
for base in directories:
108-
for root, dirs, files in os.walk(base):
109-
for directory in dirs:
110-
progress += 1
111-
if show_progress:
112-
bar.update(progress)
113-
114-
directory = os.path.join(root, directory)
115-
116-
try:
117-
dir_stat = os.stat(directory)
118-
except OSError:
119-
continue
120-
121-
perm = dir_stat.st_mode & mask
122-
123-
if (
124-
perm == exec_perm
125-
and dir_stat.st_uid == new_uid
126-
and dir_stat.st_gid == new_gid
127-
):
128-
continue
129-
130-
try:
131-
os.chown(directory, new_uid, new_gid)
132-
os.chmod(directory, exec_perm)
133-
except OSError:
134-
continue
135-
136-
for file_name in files:
137-
progress += 1
138-
bar.update(progress)
139-
file_name = os.path.join(root, file_name)
140-
try:
141-
file_stat = os.stat(file_name)
142-
except OSError:
143-
continue
144-
145-
perm = file_stat.st_mode & mask
146-
147-
if perm & stat.S_IXUSR:
148-
# executable, so make sure others can execute it
149-
new_perm = exec_perm
150-
else:
151-
new_perm = read_write_perm
152-
153-
if (
154-
perm == new_perm
155-
and file_stat.st_uid == new_uid
156-
and file_stat.st_gid == new_gid
157-
):
158-
continue
159-
160-
try:
161-
os.chown(file_name, new_uid, new_gid)
162-
os.chmod(file_name, new_perm)
163-
except OSError:
164-
continue
165-
166-
if show_progress:
167-
bar.finish()
90+
except OSError as e:
91+
print(f'{e} – skipping {directory}')
92+
93+
paths_iter = (p for p in Path(directory).rglob('*'))
94+
n_files = sum(1 for _ in Path(directory).rglob('*'))
95+
96+
print(f'Updating file permissions for: {directory}')
97+
98+
with (
99+
ThreadPoolExecutor(max_workers=workers) as pool,
100+
tqdm(
101+
total=n_files,
102+
unit='item',
103+
dynamic_ncols=True,
104+
disable=(not show_progress),
105+
) as bar,
106+
):
107+
futures = [
108+
pool.submit(
109+
_update,
110+
p,
111+
uid=new_uid,
112+
gid=new_gid,
113+
read_write_perm=read_write_perm,
114+
exec_perm=exec_perm,
115+
mask=mask,
116+
)
117+
for p in paths_iter
118+
]
119+
120+
for fut in as_completed(futures):
121+
bar.update(1)
122+
fut.result()
123+
124+
125+
def _update(
126+
path: Path,
127+
uid: int,
128+
gid: int,
129+
read_write_perm: int,
130+
exec_perm: int,
131+
mask: int,
132+
) -> None:
133+
"""
134+
Update file permissions for a single path
135+
"""
136+
try:
137+
_stat = os.stat(path)
138+
_perm = _stat.st_mode & mask
139+
140+
if path.is_file():
141+
if _perm & stat.S_IXUSR:
142+
new_perm = exec_perm
143+
else:
144+
new_perm = read_write_perm
145+
elif path.is_dir():
146+
new_perm = exec_perm
147+
148+
if _perm == new_perm and _stat.st_uid == uid and _stat.st_gid == gid:
149+
return
150+
151+
os.chown(path, uid, gid)
152+
os.chmod(path, new_perm)
153+
except OSError:
154+
pass

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ dependencies = [
2525
"jinja2",
2626
"lxml",
2727
"pyyaml",
28-
"progressbar2",
2928
"termcolor",
29+
"tqdm"
3030
]
3131

3232
[project.optional-dependencies]

spec-file.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ importlib_resources
33
jinja2
44
lxml
55
pyyaml
6-
progressbar2
76
requests
87
rsync
98
termcolor
9+
tqdm
1010

1111
# Building
1212
setuptools >=60

0 commit comments

Comments
 (0)