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

# Copyright (c) 2009, Tom Boothby <boothby@math.washington.edu> 

# 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 binary 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. 

# * Neither the name of Sage nor the 

# names of its contributors may be used to endorse or promote products 

# derived from this software without specific prior written permission. 

# 

# THIS SOFTWARE IS PROVIDED BY Tom Boothby ''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 Tom Boothby 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. 

  

""" 

  

Function Mangling 

  

This module provides utilities for extracting information about python 

functions. 

  

AUTHORS: 

  

- Tom Boothby (2009): Original version in Python 

- Simon King (2011): Use Cython. Speedup of ``fix_to_pos``, cleaning documentation. 

  

""" 

from __future__ import print_function 

  

from sage.misc.sageinspect import sage_getargspec 

  

cdef class ArgumentFixer: 

""" 

This class provides functionality to normalize the arguments 

passed into a function. While the various ways of calling a 

function are perfectly equivalent from the perspective of the 

callee, they don't always look the same for an object 

watching the caller. For example, 

:: 

  

sage: def f(x = 10): 

....: return min(1,x) 

  

the following calls are equivalent, 

:: 

  

sage: f() 

1 

sage: f(10) 

1 

sage: f(x=10) 

1 

  

but from the perspective of a wrapper, they are different:: 

  

sage: def wrap(g): 

....: def _g(*args,**kwargs): 

....: print("{} {}".format(args, kwargs)) 

....: return g(*args, **kwargs) 

....: return _g 

sage: h = wrap(f) 

sage: t = h() 

() {} 

sage: t = h(10) 

(10,) {} 

sage: t = h(x=10) 

() {'x': 10} 

  

For the purpose of cached functions, it is important not 

to distinguish between these uses. 

  

INPUT: 

  

- f -- a function 

- classmethod -- boolean (default False) -- True if the function 

is a classmethod and therefore the first 

argument is expected to be the class instance. 

In that case, we ignore the first argument. 

  

EXAMPLES:: 

  

sage: from sage.misc.function_mangling import ArgumentFixer 

sage: def wrap2(g): 

....: af = ArgumentFixer(g) 

....: def _g(*args, **kwargs): 

....: print(af.fix_to_pos()) 

....: return g(*args,**kwargs) 

....: return _g 

sage: h2 = wrap2(f) 

sage: t = h2() 

((10,), ()) 

sage: t = h2(10) 

((10,), ()) 

sage: t = h2(x=10) 

((10,), ()) 

  

:: 

  

sage: class one: 

....: def __init__(self, x = 1): 

....: self.x = x 

sage: af = ArgumentFixer(one.__init__.__func__, classmethod=True) 

sage: af.fix_to_pos(1,2,3,a=31,b=2,n=3) 

((1, 2, 3), (('a', 31), ('b', 2), ('n', 3))) 

  

""" 

def __init__(self, f, classmethod = False): 

try: 

arg_names, varargs, varkw, defaults = sage_getargspec(f) 

except AttributeError: 

# This error occurs if f is defined in a Cython file and the 

# source file has gone. 

if classmethod: 

arg_names = ['self'] 

varargs = 'args' 

varkws = 'kwds' 

defaults = None 

else: 

arg_names = [] 

varargs = 'args' 

varkws = 'kwds' 

defaults = None 

if defaults is None: 

self._default_tuple = defaults = () 

else: 

self._default_tuple = tuple(defaults) 

  

#code = f.__code__ 

  

self.f = f 

self._ndefault = len(defaults) 

if classmethod: 

self._nargs = len(arg_names)-1 #code.co_argcount-1 

self._arg_names = tuple(arg_names[1:]) #code.co_varnames[1:self._nargs+1] 

else: 

self._nargs = len(arg_names) #code.co_argcount 

self._arg_names = tuple(arg_names) #code.co_varnames[:self._nargs] 

self._classmethod = classmethod 

  

cdef dict default_map 

self._defaults = default_map = {} 

for k,v in zip(self._arg_names[-self._ndefault:], defaults): 

default_map[k] = v 

  

def __repr__(self): 

""" 

EXAMPLES:: 

  

sage: from sage.misc.function_mangling import ArgumentFixer 

sage: g = ArgumentFixer(number_of_partitions) 

sage: g 

Argument Fixer of <function number_of_partitions at 0x...> 

""" 

return "Argument Fixer of %s"%self.f 

  

def fix_to_named(self, *args,**kwargs): 

""" 

Normalize the arguments with a preference for named arguments. 

  

INPUT: 

  

- any positional and named arguments. 

  

OUTPUT: 

  

We return a tuple 

  

`(e_1, e_2, ..., e_k), ((n_1, v_1), ... , (n_m, v_m))` 

  

where `n_1, ... , n_m` are the names of the arguments and 

`v_1, ..., v_m` are the values passed in; and `e_1, ..., e_k` are 

the unnamed arguments. We minimize `k`. 

  

The defaults are extracted from the function and filled 

into the list ``K`` of named arguments. The names `n_1, ..., n_t` 

are in order of the function definition, where `t` is the number 

of named arguments. The remaining names, `n_{t+1}, ..., n_m` are 

given in alphabetical order. This is useful to extract 

the names of arguments, but **does not** maintain 

equivalence of 

:: 

  

A,K = self.fix_to_pos(...) 

self.f(*A,**dict(K))` 

  

and 

:: 

  

self.f(...) 

  

in all cases. 

  

EXAMPLES:: 

  

sage: from sage.misc.function_mangling import ArgumentFixer 

sage: def sum3(a,b,c=3,*args,**kwargs): 

....: return a+b+c 

sage: AF = ArgumentFixer(sum3) 

sage: AF.fix_to_named(1,2,3,4,5,6,f=14,e=16) 

((4, 5, 6), (('a', 1), ('b', 2), ('c', 3), ('e', 16), ('f', 14))) 

sage: AF.fix_to_named(1,2,f=14) 

((), (('a', 1), ('b', 2), ('c', 3), ('f', 14))) 

  

""" 

cdef list ARGS = [] 

cdef tuple arg_names = self._arg_names 

cdef int lenargs = len(args) 

cdef dict defaults = self._defaults 

cdef int i 

cdef dict kwargs_ = dict(kwargs) 

for i from 0<=i<self._nargs: 

name = arg_names[i] 

if i >= lenargs: 

if name in kwargs_: 

val = kwargs_[name] 

del kwargs_[name] 

else: 

val = defaults[name] 

else: 

val = args[i] 

ARGS.append((name,val)) 

extra_args = args[self._nargs:] 

for k in sorted(kwargs_.keys()): 

ARGS.append((k,kwargs_[k])) 

return tuple(extra_args), tuple(ARGS) 

  

def fix_to_pos(self, *args, **kwds): 

""" 

Normalize the arguments with a preference for positional arguments. 

  

INPUT: 

  

Any positional or named arguments 

  

OUTPUT: 

  

We return a tuple 

  

`(e_1, e_2, ..., e_k), ((n_1, v_1), ... , (n_m, v_m))` 

  

where `n_1, ... , n_m` are the names of the arguments and 

`v_1, ..., v_m` are the values passed in; and `e_1, ..., e_k` 

are the unnamed arguments. We minimize `m`. 

  

The commands 

:: 

  

A,K = self.fix_to_pos(...) 

self.f(*A,**dict(K)) 

  

are equivalent to 

:: 

  

self.f(...) 

  

though defaults are extracted from the function and 

appended to the tuple ``A`` of positional arguments. 

The names `n_1, ..., n_m` are given in alphabetical 

order. 

  

EXAMPLES:: 

  

sage: from sage.misc.function_mangling import ArgumentFixer 

sage: def do_something(a, b, c=3, *args, **kwargs): 

....: print("{} {} {} {} {}".format(a, b, c, args, 

....: sorted(kwargs.items()))) 

sage: AF = ArgumentFixer(do_something) 

sage: A, K = AF.fix_to_pos(1, 2, 3, 4, 5, 6, f=14, e=16) 

sage: print("{} {}".format(A, K)) 

(1, 2, 3, 4, 5, 6) (('e', 16), ('f', 14)) 

sage: do_something(*A, **dict(K)) 

1 2 3 (4, 5, 6) [('e', 16), ('f', 14)] 

sage: do_something(1, 2, 3, 4, 5, 6, f=14, e=16) 

1 2 3 (4, 5, 6) [('e', 16), ('f', 14)] 

""" 

return self.fix_to_pos_args_kwds(args, kwds) 

  

cdef fix_to_pos_args_kwds(self, tuple args, dict kwds): 

""" 

Fast Cython implementation of :meth:`fix_to_pos`. 

""" 

cdef Py_ssize_t lenargs = len(args) 

cdef Py_ssize_t nargs = self._nargs 

cdef tuple arg_names = self._arg_names 

cdef dict defaults = self._defaults 

# a shortpath for the case of no named arguments: 

if not kwds: 

if lenargs >= nargs: 

return args, () 

# we take the given arguments, plus the default arguments 

return args + self._default_tuple[-nargs+lenargs:],() 

cdef list Largs = list(args) 

cdef dict kwargs = dict(kwds) 

cdef Py_ssize_t i 

for i in range(lenargs, nargs): 

# in addition to the positional arguments, we take the 

# ones with default values, unless they are overridded by 

# the named arguments. 

name = arg_names[i] 

if name in kwargs: 

val = kwargs[name] 

del kwargs[name] 

else: 

val = defaults[name] 

Largs.append(val) 

cdef list Items = sorted(kwargs.items()) 

return tuple(Largs), tuple(Items)