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

# ######################### LICENSE ############################ # 

 

# Copyright (c) 2005-2018, Michele Simionato 

# All rights reserved. 

 

# Redistribution and use in source and binary forms, with or without 

# modification, are permitted provided that the following conditions are 

# met: 

 

# Redistributions of source code must retain the above copyright 

# notice, this list of conditions and the following disclaimer. 

# Redistributions in bytecode form must reproduce the above copyright 

# notice, this list of conditions and the following disclaimer in 

# the documentation and/or other materials provided with the 

# distribution. 

 

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 

# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 

# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 

# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 

# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 

# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 

# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 

# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 

# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 

# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 

# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 

# DAMAGE. 

 

""" 

Decorator module, see http://pypi.python.org/pypi/decorator 

for the documentation. 

""" 

from __future__ import print_function 

 

import re 

import sys 

import inspect 

import operator 

import itertools 

import collections 

 

__version__ = '4.3.0' 

 

if sys.version >= '3': 

from inspect import getfullargspec 

 

def get_init(cls): 

return cls.__init__ 

else: 

FullArgSpec = collections.namedtuple( 

'FullArgSpec', 'args varargs varkw defaults ' 

'kwonlyargs kwonlydefaults annotations') 

 

def getfullargspec(f): 

"A quick and dirty replacement for getfullargspec for Python 2.X" 

return FullArgSpec._make(inspect.getargspec(f) + ([], None, {})) 

 

def get_init(cls): 

return cls.__init__.__func__ 

 

try: 

iscoroutinefunction = inspect.iscoroutinefunction 

except AttributeError: 

# let's assume there are no coroutine functions in old Python 

def iscoroutinefunction(f): 

return False 

 

 

DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(') 

 

 

# basic functionality 

class FunctionMaker(object): 

""" 

An object with the ability to create functions with a given signature. 

It has attributes name, doc, module, signature, defaults, dict and 

methods update and make. 

""" 

 

# Atomic get-and-increment provided by the GIL 

_compile_count = itertools.count() 

 

# make pylint happy 

args = varargs = varkw = defaults = kwonlyargs = kwonlydefaults = () 

 

def __init__(self, func=None, name=None, signature=None, 

defaults=None, doc=None, module=None, funcdict=None): 

self.shortsignature = signature 

if func: 

# func can be a class or a callable, but not an instance method 

self.name = func.__name__ 

if self.name == '<lambda>': # small hack for lambda functions 

self.name = '_lambda_' 

self.doc = func.__doc__ 

self.module = func.__module__ 

if inspect.isfunction(func): 

argspec = getfullargspec(func) 

self.annotations = getattr(func, '__annotations__', {}) 

for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', 

'kwonlydefaults'): 

setattr(self, a, getattr(argspec, a)) 

for i, arg in enumerate(self.args): 

setattr(self, 'arg%d' % i, arg) 

allargs = list(self.args) 

allshortargs = list(self.args) 

if self.varargs: 

allargs.append('*' + self.varargs) 

allshortargs.append('*' + self.varargs) 

elif self.kwonlyargs: 

allargs.append('*') # single star syntax 

for a in self.kwonlyargs: 

allargs.append('%s=None' % a) 

allshortargs.append('%s=%s' % (a, a)) 

if self.varkw: 

allargs.append('**' + self.varkw) 

allshortargs.append('**' + self.varkw) 

self.signature = ', '.join(allargs) 

self.shortsignature = ', '.join(allshortargs) 

self.dict = func.__dict__.copy() 

# func=None happens when decorating a caller 

if name: 

self.name = name 

if signature is not None: 

self.signature = signature 

if defaults: 

self.defaults = defaults 

if doc: 

self.doc = doc 

if module: 

self.module = module 

if funcdict: 

self.dict = funcdict 

# check existence required attributes 

assert hasattr(self, 'name') 

if not hasattr(self, 'signature'): 

raise TypeError('You are decorating a non function: %s' % func) 

 

def update(self, func, **kw): 

"Update the signature of func with the data in self" 

func.__name__ = self.name 

func.__doc__ = getattr(self, 'doc', None) 

func.__dict__ = getattr(self, 'dict', {}) 

func.__defaults__ = self.defaults 

func.__kwdefaults__ = self.kwonlydefaults or None 

func.__annotations__ = getattr(self, 'annotations', None) 

try: 

frame = sys._getframe(3) 

except AttributeError: # for IronPython and similar implementations 

callermodule = '?' 

else: 

callermodule = frame.f_globals.get('__name__', '?') 

func.__module__ = getattr(self, 'module', callermodule) 

func.__dict__.update(kw) 

 

def make(self, src_templ, evaldict=None, addsource=False, **attrs): 

"Make a new function from a given template and update the signature" 

src = src_templ % vars(self) # expand name and signature 

evaldict = evaldict or {} 

mo = DEF.search(src) 

if mo is None: 

raise SyntaxError('not a valid function template\n%s' % src) 

name = mo.group(1) # extract the function name 

names = set([name] + [arg.strip(' *') for arg in 

self.shortsignature.split(',')]) 

for n in names: 

if n in ('_func_', '_call_'): 

raise NameError('%s is overridden in\n%s' % (n, src)) 

 

if not src.endswith('\n'): # add a newline for old Pythons 

src += '\n' 

 

# Ensure each generated function has a unique filename for profilers 

# (such as cProfile) that depend on the tuple of (<filename>, 

# <definition line>, <function name>) being unique. 

filename = '<decorator-gen-%d>' % (next(self._compile_count),) 

try: 

code = compile(src, filename, 'single') 

exec(code, evaldict) 

except Exception: 

print('Error in generated code:', file=sys.stderr) 

print(src, file=sys.stderr) 

raise 

func = evaldict[name] 

if addsource: 

attrs['__source__'] = src 

self.update(func, **attrs) 

return func 

 

@classmethod 

def create(cls, obj, body, evaldict, defaults=None, 

doc=None, module=None, addsource=True, **attrs): 

""" 

Create a function from the strings name, signature and body. 

evaldict is the evaluation dictionary. If addsource is true an 

attribute __source__ is added to the result. The attributes attrs 

are added, if any. 

""" 

if isinstance(obj, str): # "name(signature)" 

name, rest = obj.strip().split('(', 1) 

signature = rest[:-1] # strip a right parens 

func = None 

else: # a function 

name = None 

signature = None 

func = obj 

self = cls(func, name, signature, defaults, doc, module) 

ibody = '\n'.join(' ' + line for line in body.splitlines()) 

caller = evaldict.get('_call_') # when called from `decorate` 

if caller and iscoroutinefunction(caller): 

body = ('async def %(name)s(%(signature)s):\n' + ibody).replace( 

'return', 'return await') 

else: 

body = 'def %(name)s(%(signature)s):\n' + ibody 

return self.make(body, evaldict, addsource, **attrs) 

 

 

def decorate(func, caller, extras=()): 

""" 

decorate(func, caller) decorates a function using a caller. 

""" 

evaldict = dict(_call_=caller, _func_=func) 

es = '' 

for i, extra in enumerate(extras): 

ex = '_e%d_' % i 

evaldict[ex] = extra 

es += ex + ', ' 

fun = FunctionMaker.create( 

func, "return _call_(_func_, %s%%(shortsignature)s)" % es, 

evaldict, __wrapped__=func) 

if hasattr(func, '__qualname__'): 

fun.__qualname__ = func.__qualname__ 

return fun 

 

 

def decorator(caller, _func=None): 

"""decorator(caller) converts a caller function into a decorator""" 

if _func is not None: # return a decorated function 

# this is obsolete behavior; you should use decorate instead 

return decorate(_func, caller) 

# else return a decorator function 

defaultargs, defaults = '', () 

if inspect.isclass(caller): 

name = caller.__name__.lower() 

doc = 'decorator(%s) converts functions/generators into ' \ 

'factories of %s objects' % (caller.__name__, caller.__name__) 

elif inspect.isfunction(caller): 

if caller.__name__ == '<lambda>': 

name = '_lambda_' 

else: 

name = caller.__name__ 

doc = caller.__doc__ 

nargs = caller.__code__.co_argcount 

ndefs = len(caller.__defaults__ or ()) 

defaultargs = ', '.join(caller.__code__.co_varnames[nargs-ndefs:nargs]) 

if defaultargs: 

defaultargs += ',' 

defaults = caller.__defaults__ 

else: # assume caller is an object with a __call__ method 

name = caller.__class__.__name__.lower() 

doc = caller.__call__.__doc__ 

evaldict = dict(_call=caller, _decorate_=decorate) 

dec = FunctionMaker.create( 

'%s(%s func)' % (name, defaultargs), 

'if func is None: return lambda func: _decorate_(func, _call, (%s))\n' 

'return _decorate_(func, _call, (%s))' % (defaultargs, defaultargs), 

evaldict, doc=doc, module=caller.__module__, __wrapped__=caller) 

if defaults: 

dec.__defaults__ = defaults + (None,) 

return dec 

 

 

# ####################### contextmanager ####################### # 

 

try: # Python >= 3.2 

from contextlib import _GeneratorContextManager 

except ImportError: # Python >= 2.5 

from contextlib import GeneratorContextManager as _GeneratorContextManager 

 

 

class ContextManager(_GeneratorContextManager): 

def __call__(self, func): 

"""Context manager decorator""" 

return FunctionMaker.create( 

func, "with _self_: return _func_(%(shortsignature)s)", 

dict(_self_=self, _func_=func), __wrapped__=func) 

 

 

init = getfullargspec(_GeneratorContextManager.__init__) 

n_args = len(init.args) 

if n_args == 2 and not init.varargs: # (self, genobj) Python 2.7 

def __init__(self, g, *a, **k): 

return _GeneratorContextManager.__init__(self, g(*a, **k)) 

ContextManager.__init__ = __init__ 

elif n_args == 2 and init.varargs: # (self, gen, *a, **k) Python 3.4 

pass 

elif n_args == 4: # (self, gen, args, kwds) Python 3.5 

def __init__(self, g, *a, **k): 

return _GeneratorContextManager.__init__(self, g, a, k) 

ContextManager.__init__ = __init__ 

 

_contextmanager = decorator(ContextManager) 

 

 

def contextmanager(func): 

# Enable Pylint config: contextmanager-decorators=decorator.contextmanager 

return _contextmanager(func) 

 

 

# ############################ dispatch_on ############################ # 

 

def append(a, vancestors): 

""" 

Append ``a`` to the list of the virtual ancestors, unless it is already 

included. 

""" 

add = True 

for j, va in enumerate(vancestors): 

if issubclass(va, a): 

add = False 

break 

if issubclass(a, va): 

vancestors[j] = a 

add = False 

if add: 

vancestors.append(a) 

 

 

# inspired from simplegeneric by P.J. Eby and functools.singledispatch 

def dispatch_on(*dispatch_args): 

""" 

Factory of decorators turning a function into a generic function 

dispatching on the given arguments. 

""" 

assert dispatch_args, 'No dispatch args passed' 

dispatch_str = '(%s,)' % ', '.join(dispatch_args) 

 

def check(arguments, wrong=operator.ne, msg=''): 

"""Make sure one passes the expected number of arguments""" 

if wrong(len(arguments), len(dispatch_args)): 

raise TypeError('Expected %d arguments, got %d%s' % 

(len(dispatch_args), len(arguments), msg)) 

 

def gen_func_dec(func): 

"""Decorator turning a function into a generic function""" 

 

# first check the dispatch arguments 

argset = set(getfullargspec(func).args) 

if not set(dispatch_args) <= argset: 

raise NameError('Unknown dispatch arguments %s' % dispatch_str) 

 

typemap = {} 

 

def vancestors(*types): 

""" 

Get a list of sets of virtual ancestors for the given types 

""" 

check(types) 

ras = [[] for _ in range(len(dispatch_args))] 

for types_ in typemap: 

for t, type_, ra in zip(types, types_, ras): 

if issubclass(t, type_) and type_ not in t.mro(): 

append(type_, ra) 

return [set(ra) for ra in ras] 

 

def ancestors(*types): 

""" 

Get a list of virtual MROs, one for each type 

""" 

check(types) 

lists = [] 

for t, vas in zip(types, vancestors(*types)): 

n_vas = len(vas) 

if n_vas > 1: 

raise RuntimeError( 

'Ambiguous dispatch for %s: %s' % (t, vas)) 

elif n_vas == 1: 

va, = vas 

mro = type('t', (t, va), {}).mro()[1:] 

else: 

mro = t.mro() 

lists.append(mro[:-1]) # discard t and object 

return lists 

 

def register(*types): 

""" 

Decorator to register an implementation for the given types 

""" 

check(types) 

 

def dec(f): 

check(getfullargspec(f).args, operator.lt, ' in ' + f.__name__) 

typemap[types] = f 

return f 

return dec 

 

def dispatch_info(*types): 

""" 

An utility to introspect the dispatch algorithm 

""" 

check(types) 

lst = [] 

for anc in itertools.product(*ancestors(*types)): 

lst.append(tuple(a.__name__ for a in anc)) 

return lst 

 

def _dispatch(dispatch_args, *args, **kw): 

types = tuple(type(arg) for arg in dispatch_args) 

try: # fast path 

f = typemap[types] 

except KeyError: 

pass 

else: 

return f(*args, **kw) 

combinations = itertools.product(*ancestors(*types)) 

next(combinations) # the first one has been already tried 

for types_ in combinations: 

f = typemap.get(types_) 

if f is not None: 

return f(*args, **kw) 

 

# else call the default implementation 

return func(*args, **kw) 

 

return FunctionMaker.create( 

func, 'return _f_(%s, %%(shortsignature)s)' % dispatch_str, 

dict(_f_=_dispatch), register=register, default=func, 

typemap=typemap, vancestors=vancestors, ancestors=ancestors, 

dispatch_info=dispatch_info, __wrapped__=func) 

 

gen_func_dec.__name__ = 'dispatch_on' + dispatch_str 

return gen_func_dec