8
8
import os
9
9
import platform
10
10
import re
11
- import shutil
12
11
import sys
12
+ from functools import wraps
13
13
from os import fspath , path
14
14
from pathlib import Path
15
- from subprocess import PIPE , CalledProcessError , check_output , run
15
+ from shutil import copyfileobj , rmtree
16
+ from subprocess import CalledProcessError , check_output , run
16
17
from textwrap import dedent
18
+ from urllib import request
19
+ from urllib .parse import urlparse
20
+ from zipfile import ZipFile
21
+
22
+ from tqdm .auto import tqdm
23
+ from tqdm .utils import CallbackIOWrapper
17
24
18
25
from . import cudasetup as cs
19
26
20
27
if os .getenv ("DISPLAY" , False ):
21
- from functools import wraps
22
28
from tkinter import Tk
23
29
from tkinter .filedialog import askdirectory as ask
24
30
@@ -37,21 +43,6 @@ def askdir(title, initialdir):
37
43
return initialdir if res == "" else res
38
44
39
45
40
- def askdirectory (title = "Folder" , initialdir = "~" , name = "" ):
41
- """
42
- Args:
43
- initialdir (str): default: "~"
44
- Returns (str):
45
- one of (decreasing Precedence):
46
- - `os.getenv(name)`
47
- - `input()` or `tkinter.filedialog.askdirectory()`
48
- - `initialdir`
49
- """
50
- initialdir = path .expanduser (initialdir )
51
- res = os .getenv (name , None )
52
- return askdir (title , initialdir ) if res is None else res
53
-
54
-
55
46
log = logging .getLogger (__name__ )
56
47
57
48
# NiftyReg
@@ -82,6 +73,7 @@ def askdirectory(title="Folder", initialdir="~", name=""):
82
73
# 'dcm2niix_27-Jun-2018_win.zip'
83
74
# sha1_dcm = '4b641113273d86ad73123816993092fc643ac62f'
84
75
# dcm_ver = '1.0.20180622'
76
+ http_dcm = {"Windows" : http_dcm_win , "Linux" : http_dcm_lin , "Darwin" : http_dcm_mac }
85
77
86
78
# source and build folder names
87
79
dirsrc = "_src"
@@ -91,6 +83,7 @@ def askdirectory(title="Folder", initialdir="~", name=""):
91
83
ncpu = multiprocessing .cpu_count ()
92
84
93
85
LOG_FORMAT = "%(levelname)s:%(asctime)s:%(name)s:%(funcName)s\n > %(message)s"
86
+ CHUNK_SIZE = 2 ** 15 # 32 kiB
94
87
95
88
96
89
class LogHandler (logging .StreamHandler ):
@@ -102,6 +95,73 @@ def __init__(self, *args, **kwargs):
102
95
self .setFormatter (fmt )
103
96
104
97
98
+ def askdirectory (title = "Folder" , initialdir = "~" , name = "" ):
99
+ """
100
+ Args:
101
+ initialdir (str): default: "~"
102
+ Returns (str):
103
+ one of (decreasing Precedence):
104
+ - `os.getenv(name)`
105
+ - `input()` or `tkinter.filedialog.askdirectory()`
106
+ - `initialdir`
107
+ """
108
+ initialdir = path .expanduser (initialdir )
109
+ res = os .getenv (name , None )
110
+ return askdir (title , initialdir ) if res is None else res
111
+
112
+
113
+ def urlopen_cached (url , outdir , fname = None , mode = "rb" ):
114
+ """
115
+ Download `url` to `outdir/fname`.
116
+ Cache based on `url` at `outdir/fname`.url
117
+
118
+ Args:
119
+ url (str): source
120
+ outdir (path-like): destination
121
+ fname (str): optional, auto-detected from `url` if not given
122
+ mode (str): for returned file object
123
+ Returns:
124
+ file
125
+ """
126
+ outdir = Path (outdir ).expanduser ()
127
+ outdir .mkdir (exist_ok = True )
128
+ if fname is None :
129
+ fname = Path (urlparse (url ).path ).name
130
+ fout = outdir / fname
131
+ cache = outdir / f"{ fname } .url"
132
+ if not fout .is_file () or not cache .is_file () or cache .read_text ().strip () != url :
133
+ req = request .Request (url = url )
134
+ with request .urlopen (req ) as raw :
135
+ with tqdm .wrapattr (raw , "read" , total = getattr (raw , "length" , None )) as fd :
136
+ with fout .open ("wb" ) as fo :
137
+ i = fd .read (CHUNK_SIZE )
138
+ while i :
139
+ fo .write (i )
140
+ i = fd .read (CHUNK_SIZE )
141
+ cache .write_text (url )
142
+ return fout .open (mode )
143
+
144
+
145
+ def extractall (fzip , dest , desc = "Extracting" ):
146
+ """zipfile.Zipfile(fzip).extractall(dest) with progress"""
147
+ dest = Path (dest ).expanduser ()
148
+ with ZipFile (fzip ) as zipf :
149
+ with tqdm (
150
+ desc = desc ,
151
+ unit = "B" ,
152
+ unit_scale = True ,
153
+ unit_divisor = 1024 ,
154
+ total = sum (getattr (i , "file_size" , 0 ) for i in zipf .infolist ()),
155
+ ) as pbar :
156
+ for i in zipf .infolist ():
157
+ if not getattr (i , "file_size" , 0 ): # directory
158
+ zipf .extract (i , fspath (dest ))
159
+ else :
160
+ with zipf .open (i ) as fi :
161
+ with open (fspath (dest / i .filename ), "wb" ) as fo :
162
+ copyfileobj (CallbackIOWrapper (pbar .update , fi ), fo )
163
+
164
+
105
165
def query_yesno (question ):
106
166
valid = {"yes" : True , "y" : True , "ye" : True , "no" : False , "n" : False }
107
167
prompt = " [Y/n]: "
@@ -243,32 +303,29 @@ def check_version(Cnt, chcklst=None):
243
303
# niftyreg reg_resample first
244
304
if "RESPATH" in chcklst and "RESPATH" in Cnt :
245
305
try :
246
- p = run ([Cnt ["RESPATH" ], "--version" ], stdout = PIPE )
247
- out = p .stdout .decode ("utf-8" )
306
+ out = check_output ([Cnt ["RESPATH" ], "--version" ]).decode ("U8" )
248
307
if reg_ver in out :
249
308
output ["RESPATH" ] = True
250
- except OSError :
309
+ except ( CalledProcessError , FileNotFoundError ) :
251
310
log .error ("NiftyReg (reg_resample) either is NOT installed or is corrupt." )
252
311
253
312
# niftyreg reg_aladin
254
313
if "REGPATH" in chcklst and "REGPATH" in Cnt :
255
314
try :
256
- p = run ([Cnt ["REGPATH" ], "--version" ], stdout = PIPE )
257
- out = p .stdout .decode ("utf-8" )
315
+ out = check_output ([Cnt ["REGPATH" ], "--version" ]).decode ("U8" )
258
316
if reg_ver in out :
259
317
output ["REGPATH" ] = True
260
- except OSError :
318
+ except ( CalledProcessError , FileNotFoundError ) :
261
319
log .error ("NiftyReg (reg_aladin) either is NOT installed or is corrupt." )
262
320
263
321
# dcm2niix
264
322
if "DCM2NIIX" in chcklst and "DCM2NIIX" in Cnt :
265
323
try :
266
- p = run ([Cnt ["DCM2NIIX" ], "-h" ], stdout = PIPE )
267
- out = p .stdout .decode ("utf-8" )
324
+ out = check_output ([Cnt ["DCM2NIIX" ], "-h" ]).decode ("U8" )
268
325
ver_str = re .search (r"(?<=dcm2niiX version v)\d{1,2}.\d{1,2}.\d*" , out )
269
326
if ver_str and dcm_ver in ver_str .group (0 ):
270
327
output ["DCM2NIIX" ] = True
271
- except OSError :
328
+ except ( CalledProcessError , FileNotFoundError ) :
272
329
log .error ("dcm2niix either is NOT installed or is corrupt." )
273
330
274
331
# hdw mu-map list
@@ -295,25 +352,13 @@ def download_dcm2niix(Cnt, dest):
295
352
)
296
353
297
354
# -create the installation folder
298
- if not dest .is_dir ():
299
- dest .mkdir ()
355
+ dest .mkdir (exist_ok = True )
300
356
binpath = dest / "bin"
301
- if not binpath .is_dir ():
302
- binpath .mkdir ()
357
+ binpath .mkdir (exist_ok = True )
303
358
304
- import urllib .error
305
- import urllib .parse
306
- import urllib .request
307
- import zipfile
308
-
309
- http_dcm = {"Windows" : http_dcm_win , "Linux" : http_dcm_lin , "Darwin" : http_dcm_mac }
310
- urllib .request .urlretrieve (
311
- http_dcm [platform .system ()], fspath (dest / "dcm2niix.zip" )
312
- )
359
+ with urlopen_cached (http_dcm [platform .system ()], dest ) as fd :
360
+ extractall (fd , binpath )
313
361
314
- zipf = zipfile .ZipFile (fspath (dest / "dcm2niix.zip" ), "r" )
315
- zipf .extractall (fspath (binpath ))
316
- zipf .close ()
317
362
Cnt ["DCM2NIIX" ] = fspath (next (binpath .glob ("dcm2niix*" )))
318
363
# ensure the permissions are given to the executable
319
364
os .chmod (Cnt ["DCM2NIIX" ], 755 )
@@ -333,14 +378,12 @@ def install_tool(app, Cnt):
333
378
# pick the target installation folder for tools
334
379
if Cnt .get ("PATHTOOLS" , None ):
335
380
path_tools = Path (Cnt ["PATHTOOLS" ])
336
- if not path_tools .is_dir ():
337
- path_tools .mkdir ()
381
+ path_tools .mkdir (exist_ok = True )
338
382
else :
339
383
path_tools = Path (
340
384
askdirectory (title = "Path to place NiftyPET tools" , name = "PATHTOOLS" )
341
385
)
342
- if not path_tools .is_dir ():
343
- path_tools .mkdir ()
386
+ path_tools .mkdir (exist_ok = True )
344
387
if path_tools .name != Cnt ["DIRTOOLS" ]:
345
388
path_tools /= Cnt ["DIRTOOLS" ]
346
389
Cnt ["PATHTOOLS" ] = fspath (path_tools )
@@ -357,8 +400,7 @@ def install_tool(app, Cnt):
357
400
)
358
401
359
402
# create the main tools folder
360
- if not path_tools .is_dir ():
361
- path_tools .mkdir ()
403
+ path_tools .mkdir (exist_ok = True )
362
404
# identify the specific path for the requested app
363
405
if app == "niftyreg" :
364
406
repo = repo_reg
@@ -378,7 +420,7 @@ def install_tool(app, Cnt):
378
420
379
421
# Check if the source folder exists and delete it, if it does
380
422
if dest .is_dir ():
381
- shutil . rmtree (fspath (dest ))
423
+ rmtree (fspath (dest ))
382
424
dest .mkdir ()
383
425
os .chdir (dest )
384
426
@@ -389,8 +431,7 @@ def install_tool(app, Cnt):
389
431
run (["git" , "checkout" , sha1 ])
390
432
os .chdir (cwd )
391
433
392
- if not dirbld .is_dir ():
393
- os .mkdir (dirbld )
434
+ dirbld .mkdir (exist_ok = True )
394
435
os .chdir (dirbld )
395
436
# run cmake with arguments
396
437
if platform .system () == "Windows" :
0 commit comments