@@ -521,6 +521,68 @@ def findall(patt, filename, encoding='utf-8'):
521521 return list (evaluate (x ) for x in finditer (patt , filename , encoding ))
522522
523523
524+ def _callable_name (fn ):
525+ fn_name = '<unknown>'
526+ try :
527+ # Assume fn is standard function
528+ fn_name = fn .__name__
529+ except AttributeError :
530+ try :
531+ # Assume fn is callable object
532+ fn_name = fn .__class__ .__name__
533+ except AttributeError :
534+ pass
535+
536+ return fn_name
537+
538+
539+ def _extractiter_tag (patt , filename , tag , conv , encoding ):
540+ if isinstance (conv , collections .Iterable ):
541+ conv = conv [0 ]
542+
543+ for m in finditer (patt , filename , encoding ):
544+ try :
545+ val = m .group (tag )
546+ except (IndexError , KeyError ):
547+ raise SanityError (f'no such group in pattern { patt !r} : { tag } ' )
548+
549+ try :
550+ yield conv (val ) if callable (conv ) else val
551+ except ValueError :
552+ fn_name = _callable_name (conv )
553+ raise SanityError (
554+ f'could not convert value { val !r} using { fn_name } ()' )
555+
556+
557+ def _extractiter_multitag (patt , filename , tags , conv , encoding ):
558+ for m in finditer (patt , filename , encoding ):
559+ val = []
560+ for t in tags :
561+ try :
562+ val .append (m .group (t ))
563+ except (IndexError , KeyError ):
564+ raise SanityError (f'no such group in pattern { patt !r} : { t } ' )
565+
566+ converted_vals = []
567+ if not isinstance (conv , collections .Iterable ):
568+ conv = [conv ] * builtins .len (val )
569+ elif builtins .len (conv ) > builtins .len (val ):
570+ conv = conv [:builtins .len (val )]
571+
572+ # Here we use the last conversion function for the remaining
573+ # tags which don't have a corresponding one, if length of the
574+ # conversion function iterable is less that the one of tags
575+ for v , c in itertools .zip_longest (val , conv , fillvalue = conv [- 1 ]):
576+ try :
577+ converted_vals .append (c (v ) if callable (c ) else v )
578+ except ValueError :
579+ fn_name = _callable_name (conv )
580+ raise SanityError (
581+ f'could not convert value { v !r} using { fn_name } ()' )
582+
583+ yield tuple (converted_vals )
584+
585+
524586@deferrable
525587def extractiter (patt , filename , tag = 0 , conv = None , encoding = 'utf-8' ):
526588 '''Get an iterator over the values extracted from the capturing group
@@ -530,66 +592,10 @@ def extractiter(patt, filename, tag=0, conv=None, encoding='utf-8'):
530592 a generator object, instead of a list, which you can use to iterate over
531593 the extracted values.
532594 '''
533- for m in finditer (patt , filename , encoding ):
534- if isinstance (tag , collections .Iterable ) and not isinstance (tag , str ):
535- val = []
536- for t in tag :
537- try :
538- val .append (m .group (t ))
539- except (IndexError , KeyError ):
540- raise SanityError (
541- "no such group in pattern `%s': %s" % (patt , t ))
542- else :
543- try :
544- val = m .group (tag )
545- except (IndexError , KeyError ):
546- raise SanityError (
547- "no such group in pattern `%s': %s" % (patt , tag ))
548-
549- if isinstance (val , list ):
550- converted_vals = []
551- if not isinstance (conv , collections .Iterable ):
552- conv = [conv ] * len (val )
553-
554- # Here we use the last conversion function for the remaining
555- # tags which don't have a corresponding one
556- for v , c in itertools .zip_longest (val , conv , fillvalue = conv [- 1 ]):
557- try :
558- converted_vals .append (c (v ) if callable (c ) else v )
559- except ValueError :
560- fn_name = '<unknown>'
561- try :
562- # Assume conv is standard function
563- fn_name = c .__name__
564- except AttributeError :
565- try :
566- # Assume conv is callable object
567- fn_name = c .__class__ .__name__
568- except AttributeError :
569- pass
570-
571- raise SanityError (
572- "could not convert value `%s' using `%s()'" %
573- (v , fn_name ))
574-
575- yield (tuple (converted_vals ))
576- else :
577- try :
578- yield conv (val ) if callable (conv ) else val
579- except ValueError :
580- fn_name = '<unknown>'
581- try :
582- # Assume conv is standard function
583- fn_name = conv .__name__
584- except AttributeError :
585- try :
586- # Assume conv is callable object
587- fn_name = conv .__class__ .__name__
588- except AttributeError :
589- pass
590-
591- raise SanityError ("could not convert value `%s' using `%s()'" %
592- (val , fn_name ))
595+ if isinstance (tag , collections .Iterable ) and not isinstance (tag , str ):
596+ yield from _extractiter_multitag (patt , filename , tag , conv , encoding )
597+ else :
598+ yield from _extractiter_tag (patt , filename , tag , conv , encoding )
593599
594600
595601@deferrable
@@ -610,11 +616,26 @@ def extractall(patt, filename, tag=0, conv=None, encoding='utf-8'):
610616 Group ``0`` refers always to the whole match.
611617 Since the file is processed line by line, this means that group ``0``
612618 returns the whole line that was matched.
613- :arg conv: A callable that takes a single argument and returns a new value.
614- If provided, it will be used to convert the extracted values before
615- returning them.
616- :returns: A list of the extracted values from the matched regex.
619+ :arg conv: A callable or iterable of callables taking a single argument
620+ and returning a new value.
621+ If provided, and is not an iterable it will be used to convert
622+ the extracted values for all the capturing groups of ``tag``
623+ returning the converted values.
624+ If an iterable of callables is provided, each one will be used to
625+ convert the corresponding extracted capturing group of `tag`.
626+ If more callables functions than the corresponding capturing groups of
627+ ``tag`` are provided, the last conversion function is used for the
628+ remaining capturing groups.
629+ :returns: A list of the extracted values from the matched regex if ``tag``
630+ converted using the ``conv`` callable if ``tag`` is a single value.
631+ In case of multiple capturing groups, a list of tuples where each one
632+ contains the extracted capturing converted using the corresponding
633+ callable of ``conv``.
617634 :raises reframe.core.exceptions.SanityError: In case of errors.
635+
636+ .. versionchanged:: 3.1
637+ Multiple regex capturing groups are now supporetd via ``tag`` and
638+ multiple callables can be used in ``conv``.
618639 '''
619640 return list (evaluate (x )
620641 for x in extractiter (patt , filename , tag , conv , encoding ))
0 commit comments