4
4
from collections import OrderedDict
5
5
6
6
from jmespath import exceptions
7
- from jmespath .compat import string_type as STRING_TYPE
8
7
from jmespath .compat import get_methods
8
+ from jmespath .compat import iteritems
9
+ from jmespath .compat import map
10
+ from jmespath .compat import string_type as STRING_TYPE
9
11
10
12
11
13
# python types -> jmespath types
@@ -83,22 +85,28 @@ def call_function(self, function_name, resolved_args):
83
85
return function (self , * resolved_args )
84
86
85
87
def _validate_arguments (self , args , signature , function_name ):
86
- if signature and signature [- 1 ].get ('variadic' ):
88
+ required_arguments_count = len ([param for param in signature if not param .get ('optional' ) or not param ['optional' ]])
89
+ optional_arguments_count = len ([param for param in signature if param .get ('optional' ) and param ['optional' ]])
90
+ has_variadic = signature [- 1 ].get ('variadic' ) if signature != None else False
91
+ if has_variadic :
87
92
if len (args ) < len (signature ):
88
93
raise exceptions .VariadictArityError (
89
94
len (signature ), len (args ), function_name )
90
- elif len (args ) != len (signature ):
95
+ elif optional_arguments_count > 0 :
96
+ if len (args ) < required_arguments_count or len (args ) > (required_arguments_count + optional_arguments_count ):
97
+ raise exceptions .ArityError (
98
+ len (signature ), len (args ), function_name )
99
+ elif len (args ) != required_arguments_count :
91
100
raise exceptions .ArityError (
92
101
len (signature ), len (args ), function_name )
93
102
return self ._type_check (args , signature , function_name )
94
103
95
104
def _type_check (self , actual , signature , function_name ):
96
- for i in range (len (signature )):
97
- allowed_types = signature [i ][ 'types' ]
105
+ for i in range (min ( len (signature ), len ( actual ) )):
106
+ allowed_types = self . _get_allowed_types_from_signature ( signature [i ])
98
107
if allowed_types :
99
108
self ._type_check_single (actual [i ], allowed_types ,
100
109
function_name )
101
-
102
110
def _type_check_single (self , current , types , function_name ):
103
111
# Type checking involves checking the top level type,
104
112
# and in the case of arrays, potentially checking the types
@@ -122,6 +130,13 @@ def _type_check_single(self, current, types, function_name):
122
130
self ._subtype_check (current , allowed_subtypes ,
123
131
types , function_name )
124
132
133
+ ## signature supports monotype {'type': 'type-name'}
134
+ ## or multiple types {'types': ['type1-name', 'type2-name']}
135
+ def _get_allowed_types_from_signature (self , spec ):
136
+ if spec .get ('type' ):
137
+ spec .update ({'types' : [spec .get ('type' )]})
138
+ return spec .get ('types' )
139
+
125
140
def _get_allowed_pytypes (self , types ):
126
141
allowed_types = []
127
142
allowed_subtypes = []
@@ -166,6 +181,14 @@ def _subtype_check(self, current, allowed_subtypes, types, function_name):
166
181
@signature ({'types' : ['number' ]})
167
182
def _func_abs (self , arg ):
168
183
return abs (arg )
184
+
185
+ @signature ({'types' : ['string' ]})
186
+ def _func_lower (self , arg ):
187
+ return arg .lower ()
188
+
189
+ @signature ({'types' : ['string' ]})
190
+ def _func_upper (self , arg ):
191
+ return arg .upper ()
169
192
170
193
@signature ({'types' : ['array-number' ]})
171
194
def _func_avg (self , arg ):
@@ -283,12 +306,172 @@ def _func_sort(self, arg):
283
306
def _func_sum (self , arg ):
284
307
return sum (arg )
285
308
309
+ @signature ({'types' : ['object' ]})
310
+ def _func_items (self , arg ):
311
+ return list (map (list , iteritems (arg )))
312
+
313
+ @signature ({'types' : ['array' ]})
314
+ def _func_from_items (self , items ):
315
+ return dict (items )
316
+
286
317
@signature ({"types" : ['object' ]})
287
318
def _func_keys (self , arg ):
288
319
# To be consistent with .values()
289
320
# should we also return the indices of a list?
290
321
return list (arg .keys ())
291
322
323
+ @signature (
324
+ {'type' : 'string' },
325
+ {'type' : 'string' },
326
+ {'type' : 'number' , 'optional' : True },
327
+ {'type' : 'number' , 'optional' : True })
328
+ def _func_find_first (self , text , search , start = 0 , end = None ):
329
+ self ._ensure_integer ('find_first' , 'start' , start )
330
+ self ._ensure_integer ('find_first' , 'end' , end )
331
+ return self ._find_impl (
332
+ text ,
333
+ search ,
334
+ lambda t , s : t .find (s ),
335
+ start ,
336
+ end
337
+ )
338
+
339
+ @signature (
340
+ {'type' : 'string' },
341
+ {'type' : 'string' },
342
+ {'type' : 'number' , 'optional' : True },
343
+ {'type' : 'number' , 'optional' : True })
344
+ def _func_find_last (self , text , search , start = 0 , end = None ):
345
+ self ._ensure_integer ('find_last' , 'start' , start )
346
+ self ._ensure_integer ('find_last' , 'end' , end )
347
+ return self ._find_impl (
348
+ text ,
349
+ search ,
350
+ lambda t , s : t .rfind (s ),
351
+ start ,
352
+ end
353
+ )
354
+
355
+ def _find_impl (self , text , search , func , start , end ):
356
+ if len (search ) == 0 :
357
+ return None
358
+ if end == None :
359
+ end = len (text )
360
+
361
+ pos = func (text [start :end ], search )
362
+ if start < 0 :
363
+ start = start + len (text )
364
+
365
+ # restrict resulting range to valid indices
366
+ start = min (max (start , 0 ), len (text ))
367
+ return start + pos if pos != - 1 else None
368
+
369
+ @signature (
370
+ {'type' : 'string' },
371
+ {'type' : 'number' },
372
+ {'type' : 'string' , 'optional' : True })
373
+ def _func_pad_left (self , text , width , padding = ' ' ):
374
+ self ._ensure_non_negative_integer ('pad_left' , 'width' , width )
375
+ return self ._pad_impl (lambda : text .rjust (width , padding ), padding )
376
+
377
+ @signature (
378
+ {'type' : 'string' },
379
+ {'type' : 'number' },
380
+ {'type' : 'string' , 'optional' : True })
381
+ def _func_pad_right (self , text , width , padding = ' ' ):
382
+ self ._ensure_non_negative_integer ('pad_right' , 'width' , width )
383
+ return self ._pad_impl (lambda : text .ljust (width , padding ), padding )
384
+
385
+ def _pad_impl (self , func , padding ):
386
+ if len (padding ) != 1 :
387
+ raise exceptions .JMESPathError (
388
+ 'syntax-error: pad_right() expects $padding to have a '
389
+ 'single character, but received `{}` instead.'
390
+ .format (padding ))
391
+ return func ()
392
+
393
+ @signature (
394
+ {'type' : 'string' },
395
+ {'type' : 'string' },
396
+ {'type' : 'string' },
397
+ {'type' : 'number' , 'optional' : True })
398
+ def _func_replace (self , text , search , replacement , count = None ):
399
+ self ._ensure_non_negative_integer (
400
+ 'replace' ,
401
+ 'count' ,
402
+ count )
403
+
404
+ if count != None :
405
+ return text .replace (search , replacement , int (count ))
406
+ return text .replace (search , replacement )
407
+
408
+ @signature (
409
+ {'type' : 'string' },
410
+ {'type' : 'string' },
411
+ {'type' : 'number' , 'optional' : True })
412
+ def _func_split (self , text , search , count = None ):
413
+ self ._ensure_non_negative_integer (
414
+ 'split' ,
415
+ 'count' ,
416
+ count )
417
+
418
+ if len (search ) == 0 :
419
+ chars = list (text )
420
+ if count == None :
421
+ return chars
422
+
423
+ head = [c for c in chars [:count ]]
424
+ tail = ['' .join (chars [count :])]
425
+ return head + tail
426
+
427
+ if count != None :
428
+ return text .split (search , count )
429
+ return text .split (search )
430
+
431
+ def _ensure_integer (
432
+ self ,
433
+ func_name ,
434
+ param_name ,
435
+ param_value ):
436
+
437
+ if param_value != None :
438
+ if int (param_value ) != param_value :
439
+ raise exceptions .JMESPathValueError (
440
+ func_name ,
441
+ param_value ,
442
+ "integer" )
443
+
444
+ def _ensure_non_negative_integer (
445
+ self ,
446
+ func_name ,
447
+ param_name ,
448
+ param_value ):
449
+
450
+ if param_value != None :
451
+ if int (param_value ) != param_value or int (param_value ) < 0 :
452
+ raise exceptions .JMESPathValueError (
453
+ func_name ,
454
+ param_name ,
455
+ "non-negative integer" )
456
+
457
+ @signature ({'type' : 'string' }, {'type' : 'string' , 'optional' : True })
458
+ def _func_trim (self , text , chars = None ):
459
+ if chars == None or len (chars ) == 0 :
460
+ return text .strip ()
461
+ return text .strip (chars )
462
+
463
+ @signature ({'type' : 'string' }, {'type' : 'string' , 'optional' : True })
464
+ def _func_trim_left (self , text , chars = None ):
465
+ if chars == None or len (chars ) == 0 :
466
+ return text .lstrip ()
467
+ return text .lstrip (chars )
468
+
469
+ @signature ({'type' : 'string' }, {'type' : 'string' , 'optional' : True })
470
+ def _func_trim_right (self , text , chars = None ):
471
+ if chars == None or len (chars ) == 0 :
472
+ return text .rstrip ()
473
+ return text .rstrip (chars )
474
+
292
475
@signature ({"types" : ['object' ]})
293
476
def _func_values (self , arg ):
294
477
return list (arg .values ())
@@ -348,6 +531,10 @@ def _func_max_by(self, array, expref):
348
531
else :
349
532
return None
350
533
534
+ @signature ({'types' : ['array' ], 'variadic' : True })
535
+ def _func_zip (self , * arguments ):
536
+ return list (map (list , zip (* arguments )))
537
+
351
538
@signature ({'types' : ['array' ]}, {'types' : ['expref' ]})
352
539
def _func_group_by (self , array , expref ):
353
540
keyfunc = self ._create_key_func (expref , ['null' , 'string' ], 'group_by' )
0 commit comments