Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

 

""" 

 

Collection of custom magic functions. 

 

""" 

 

__all__ = () 

 

import re 

import math 

import ColorPrimer 

from glob import glob 

from . import GPI, logger, register_line_magic 

from .Jobtree.OnlineJTWriter import OnlineJTWriter as JT 

from PythonCK.itertools import chunks 

 

# http://heim.ifi.uio.no/~inf3330/scripting/doc/python/ipython/node6.html 

 

#=============================================================================== 

 

def parser_list_int(list_string): 

""" 

Try to parse for a list of int. Ignore the unmatches. 

 

>>> tuple(parser_list_int('123')) 

(123,) 

>>> tuple(parser_list_int('123 456')) 

(123, 456) 

 

## Don't accept string 

>>> tuple(parser_list_int('bad')) 

() 

 

## Reject float 

>>> tuple(parser_list_int('12.3')) 

() 

 

## Treat strict int (distinguish from subjobs) 

>>> tuple(parser_list_int('12.0')) 

() 

 

## Exclude bad ones 

>>> tuple(parser_list_int('12 13 14 15.0 16')) 

(12, 13, 14, 16) 

 

""" 

for s in list_string.split(): 

try: 

yield int(s) 

except ValueError as e: 

logger.exception(e) 

logger.debug('Ignore: %r'%s) 

 

#------------------------------------------------------------------------------- 

# PEEK 

#------------------------------------------------------------------------------- 

 

@register_line_magic 

def peek(line): 

""" 

run `peek 1279.0` as shortcut for jobs("1279.0").peek('stdout')  

""" 

## note: GPI.jobs cannot directly convert unicode string yet. Do workaround 

j = GPI.jobs(str(line)) 

if j.status in ('completed', 'failed'): 

try: 

j.peek('stdout') 

j.peek('std.out') 

except: 

logger.debug('magic_peek stdout failed.') 

# Finally, show ls peek at the bottom, if exists 

try: 

j.peek() 

except: 

logger.debug('magic_peek stdout failed.') 

 

#------------------------------------------------------------------------------- 

# QUICK - JOBVIEW 

#------------------------------------------------------------------------------- 

 

def _print_failed(jid, sjid): 

"""Try to print useful info for failed subjobs debugging.""" 

sj = GPI.jobs('%s.%s'%(str(jid),str(sjid))) 

bname = sj.backend._impl._name 

if bname == 'Dirac': 

print '| {0:>4}.{1.id:<4} | {1.backend.actualCE:14} | {1.backend.statusInfo}: {1.backend.extraInfo}'.format(jid, sj) 

 

 

def _qview_single(jobid): 

job = GPI.jobs(jobid) 

cols = 120 

# l = [ColorPrimer.status(sj.status, grey_completed=True).format(sj.id) for sj in job.subjobs] 

inputs = re.findall( r'\d+\.(\d+)\s+\|\s*(\S+)\s', str(job.subjobs)) 

lenmax = len(inputs[-1][0]) # Max length of subjobsid, ~3 or 4 digits 

l = [ ColorPrimer.status(s, grey_completed=True).format(jid) for (jid,s) in inputs ] 

N = int(cols) / (lenmax+1) # Adjust column width to len of idsubjobs 

N = int(5*math.floor(float(N)/5)) # Round-down to nearest 5X 

padding = '{0:>%i}'%(lenmax+13) # Offset from len(red) 

header = "\nJob::#" + ColorPrimer.status(job.status).format(jobid) 

print header, job.name, '::', job.comment 

for sublist in chunks(l, N): 

print " ".join([padding.format(x) for x in sublist]) 

 

def _qview_all(*list_jid): 

## If not given, call all non-final 

if not list_jid: 

pattern = r'(\d+)\s+\|\s*(submitting|submitted|running|completing)' 

list_jid = zip(*re.findall( pattern, str(GPI.jobs))) 

if list_jid: 

list_jid = list_jid[0] 

 

## Start printing 

print "\x1b[2J" # clear line 

for jid in list_jid: 

_qview_single(jid) 

 

## Color legends 

print '' # blank line 

ColorPrimer.print_status_color_legend() 

 

## Attempt to see detail of failed subjobs, if any 

# # note: can be slow 

queue = [] 

for jid in list_jid: 

s = str(GPI.jobs(jid).subjobs) 

for sjid in re.findall( r'\d+\.(\d+)\s+\|\s+failed\s+', s): 

queue.append([ jid, sjid ]) 

if queue: 

print 'List of error subjobs::' 

print '| JID | actualCE | statusInfo ' 

print '-'*80 

for jid,sjid in queue: 

_print_failed(jid, sjid) 

 

@register_line_magic 

def jv(line=''): 

""" 

Print the list of subjobs id with its respective colors 

 

Usage: 

jv 975 # Print single job 

jv 1090 1091 1092 # Print mutiple jobs 

jv # If no jids, print non-final jobs (e.g., running, submitting, etc.) 

""" 

_qview_all(*list(parser_list_int(line))) 

 

#-------------------------------------------------------------- 

 

@register_line_magic 

def jrm(line): 

""" 

rm-equivalent to delete the job from given id. 

 

Slightly safer with delete-guard, which prevent accidental delete of Job. 

Any Job already put inside jobtree will be guarded by this mechanism, and 

can be deleted either forcefully with command 

 

Due to compatibility reason, check for jid both in string and int. 

""" 

force_delete = False 

if '-f' in line: 

force_delete = True 

line = line.replace('-f', '') 

for intjid in parser_list_int(line): 

loc1 = GPI.jobtree.find(intjid) 

loc2 = GPI.jobtree.find(str(intjid)) 

if (loc1 or loc2) and not force_delete: # CAREFUL, use strjid, not intjid 

print 'Safety-delete-guard: please remove job:%r from jobtree first.'%intjid 

print 'Alternatively, provide flag `-f` to force-delete.' 

print 'Location1: %r' % loc1 

print 'Location2: %r' % loc2 

else: 

GPI.jobs(intjid).remove() 

 

#-------------------------------------------------------------- 

 

def _parse_index(rawindex): 

""" 

Return tuple of indices to be cd by jobtree. 

 

>>> _parse_index('4') 

(4,) 

>>> _parse_index('4,0,0') 

(4, 0, 0) 

>>> _parse_index('400') 

(4, 0, 0) 

 

""" 

if rawindex=='': # root 

return None 

try: 

x = eval(rawindex) 

if isinstance(x, tuple): 

return x 

else: 

# If it's int, try split by single char 

return tuple(int(i) for i in str(rawindex)) 

except Exception, e: 

logger.exception(e) 

raise ValueError('Unknown index: ' + rawindex) 

 

def _parse_cmd(rawcmd): 

""" 

 

>>> _parse_cmd('BANANA') 

Traceback (most recent call last): 

... 

ValueError: Unknown cmd given: BANANA 

 

""" 

if rawcmd=='': 

return None 

if rawcmd in JT.MAGIC_CMDS: 

return rawcmd 

raise ValueError('Unknown cmd given: '+rawcmd) 

 

def parser_magic_jt(s): 

""" 

1. Consecutive numbers (with optional comma) 

2. Optional command text 

3. Optional command's argument(s) 

 

## Good args, whitespace doesn't matter 

 

>>> parser_magic_jt('112 mkdir') 

((1, 1, 2), 'mkdir', []) 

>>> parser_magic_jt('112 mkdir ') 

((1, 1, 2), 'mkdir', []) 

>>> parser_magic_jt('112 mkdir Hello') 

((1, 1, 2), 'mkdir', ['Hello']) 

>>> parser_magic_jt('112 mkdir s1 s2') 

((1, 1, 2), 'mkdir', ['s1', 's2']) 

 

## Root cmd needs no index 

 

>>> parser_magic_jt('mkdir') 

(None, 'mkdir', []) 

>>> parser_magic_jt('mkdir hello') 

(None, 'mkdir', ['hello']) 

 

## Bad indices 

 

>>> parser_magic_jt('4 0 0') 

Traceback (most recent call last): 

... 

ValueError: ... 

 

>>> parser_magic_jt('40 0') 

Traceback (most recent call last): 

... 

ValueError: ... 

 

>>> parser_magic_jt('4,0 0') 

Traceback (most recent call last): 

... 

ValueError: ... 

 

>>> parser_magic_jt('40-44') 

Traceback (most recent call last): 

... 

ValueError: ... 

 

 

""" 

rawindex, rawcmd, rawargs = re.findall(r'([\d,]*)[ ]*([A-z]*)(.*)', s)[0] 

rawargs = rawargs.strip() 

index = _parse_index(rawindex) 

cmd = _parse_cmd(rawcmd) 

args = rawargs.split(' ') if rawargs else [] 

if (not cmd) and (args): 

raise ValueError('Bad input: Arguments with no command: '+s) 

return index, cmd, args 

 

@register_line_magic 

def jt(line): 

""" 

 

`JT` is an enhanced approach to Ganga's built-in `jobtree` 

 

Forget the `jobtree.cd`. Let's do it from the indices. 

 

.. image:: ../fig_myganga_GPI.jobtree.png 

 

Usages:: 

jt # Calling it to see your current tree. 

jt mkdir mydir # Create new folder at root. Sorted alphabetically. 

jt 0 # Refering to the `mydir1` directory. 

jt 0 mkdir subdir # Sub directory can be created. 

jt 00 add 208,209 # Add jobs 208 209 to the `mydir1/subdir` directory. 

jt 0 rename newname # Rename the `mydir` directory to `newname` 

 

List of commands: 

 

- add: 

- clean: 

- close: 

- mkdir: 

- open 

- rename: 

- rm: 

 

""" 

 

logger.debug(line) 

# Simple no args 

if not line: 

print JT() 

return 

try: 

key, cmd, args = parser_magic_jt(line) 

except Exception, e: 

logger.exception(e) 

logger.warning('Failed to parse magic_jt: '+line) 

return 

# 1. key, !cmd, !args (viewing tree) 

if key and not cmd and not args: 

print JT()[key] 

# 2. key, cmd, ... (full command, regardless args) 

elif key and cmd: 

getattr(JT()[key], cmd)(*args) 

# 3. !key, cmd, ... (command, operating at root) 

elif not key and cmd: 

getattr(JT(), cmd)(*args) 

else: 

raise ValueError('Unknown strategy: %r'%locals()) 

 

 

#=============================================================================== 

 

@register_line_magic 

def grun(_): 

""" 

Run the existing `*ganga*.py` file in that directory, only if there's no conflict. 

""" 

l = glob('*ganga*.py') 

if len(l) == 1: 

fname = l[0] 

logger.info("Will run '%s'"%fname) 

 

# ## 6.3.1: Fetch function directly without magic 

# from Ganga.Runtime.IPythonMagic import magic_ganga 

# magic_ganga(fname) 

 

# ## 6.1.20  

# import __main__ 

# ip = __main__.get_ipython() 

# ip.run_line_magic( 'ganga', fname) 

 

# magic_ganga(l[0]) 

# from IPython.iplib import InteractiveShell as IS 

# if hasattr( IS, 'magic_ganga' ): # Pre 6.1 Ganga 

# IS.magic_ganga(l[0]) 

# return 

# else: # Execute file manually 

# IS.magic_run('-i '+l[0]) 

# return 

 

## Report in case otherwise 

if len(l)==0: 

logger.info('No *ganga.py matched. Ignore') 

else: 

logger.info('Multiple *ganga.py matched. Ignore') 

for x in l: 

print logger.info(x) 

# IS.magic_ganga(self) # Print more help text. 

 

#=============================================================================== 

 

# ? Application Finished With Errors 

# ? Exception During Execution 

 

@register_line_magic 

def resubmit(line): 

""" 

More user-friendly command to resubmit on job with subjobs. 

 

Note: It may be more natural to extend this functionality to Job instance. 

""" 

 

def run(cmd): 

# print 'DRY-RUN: ', cmd 

GPI.queues.add(cmd) 

 

def treat_failed_dirac(j): 

aname = j.application._impl._name 

info = j.backend.statusInfo 

if info == 'Execution Complete': 

cmd = j.backend.reset 

elif info == 'Application Finished Successfully': 

cmd = j.backend.reset 

elif info == 'Pending Requests': 

cmd = j.backend.reset 

elif info == 'Requests done': 

cmd = j.backend.reset 

elif info == 'Exception During Execution': 

## Try on other CE if it's Gauss, free to roam around 

if aname == 'Gauss': 

j.backend.settings['BannedSites'] = [j.backend.actualCE] 

cmd = j.resubmit 

else: 

## Simple retry 

cmd = j.backend.reset 

elif info == 'Job stalled: pilot not running': 

j.backend.settings['BannedSites'] = [j.backend.actualCE] 

cmd = j.resubmit 

elif info == 'Job has reached the CPU limit of the queue': 

# ban site used, ignore previous ban 

j.backend.settings['BannedSites'] = [j.backend.actualCE] 

cmd = j.resubmit 

elif 'Stalling for more than' in info: 

j.backend.settings['BannedSites'] = [j.backend.actualCE] 

cmd = j.resubmit 

else: # unknown, resubmit to other CE 

j.backend.settings['BannedSites'] = [j.backend.actualCE] 

cmd = j.resubmit 

run(cmd) 

 

for x in line.split(): 

x = str(x.strip()) # clean, unicode->str, as native GPI is bossy. 

j = GPI.jobs(x) 

bname = j.backend._impl._name 

if j.status == 'failed' and not j.subjobs: 

raise NotImplementedError 

## If it has any failed subjobs, treat only those. 

queue = j.subjobs.select(status='failed') 

if queue: 

if bname == 'Dirac': 

for sj in queue: 

treat_failed_dirac( sj ) 

else: 

run(sj.resubmit) 

continue # move to next job in magic queue 

## Then, when there's no more 'failed' jobs, treat 'completing' Dirac's 

queue = j.subjobs.select(status='completing') 

if queue: 

if bname == 'Dirac': 

for sj in queue: 

run(sj.backend.reset) 

continue # move to next job in magic queue 

## EXIT