11import grp
22import os
33import 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
810def 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
0 commit comments