13
13
import re
14
14
15
15
from typing import (
16
- cast , List , Tuple , Dict , Callable , Union , Optional , Pattern , Match , Set , Any
16
+ cast , List , Tuple , Dict , Callable , Union , Optional , Pattern , Match , Set
17
17
)
18
18
from typing_extensions import Final , TYPE_CHECKING
19
19
@@ -50,14 +50,14 @@ def compile_format_re() -> Pattern[str]:
50
50
See https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
51
51
The regexp is intentionally a bit wider to report better errors.
52
52
"""
53
- key_re = r'(\(([^( )]*)\))?' # (optional) parenthesised sequence of characters.
54
- flags_re = r'([#0\-+ ]*)' # (optional) sequence of flags.
55
- width_re = r'(\*| [1-9][0-9]*)?' # (optional) minimum field width (* or numbers).
56
- precision_re = r'(?:\.(\*|[0-9]+)?)?' # (optional) . followed by * of numbers.
53
+ key_re = r'(\((?P<key>[^ )]*)\))?' # (optional) parenthesised sequence of characters.
54
+ flags_re = r'(?P<flag> [#0\-+ ]*)' # (optional) sequence of flags.
55
+ width_re = r'(?P<width> [1-9][0-9]*|\ *)?' # (optional) minimum field width (* or numbers).
56
+ precision_re = r'(?:\.(?P<precision> \*|[0-9]+)?)?' # (optional) . followed by * of numbers.
57
57
length_mod_re = r'[hlL]?' # (optional) length modifier (unused).
58
- type_re = r'(.)?' # conversion type.
58
+ type_re = r'(?P<type> .)?' # conversion type.
59
59
format_re = '%' + key_re + flags_re + width_re + precision_re + length_mod_re + type_re
60
- return re .compile (format_re )
60
+ return re .compile ('({})' . format ( format_re ) )
61
61
62
62
63
63
def compile_new_format_re (custom_spec : bool ) -> Pattern [str ]:
@@ -114,16 +114,20 @@ def compile_new_format_re(custom_spec: bool) -> Pattern[str]:
114
114
115
115
116
116
class ConversionSpecifier :
117
- def __init__ (self , key : Optional [str ],
118
- flags : str , width : str , precision : str , type : str ,
117
+ def __init__ (self , type : str ,
118
+ key : Optional [str ],
119
+ flags : Optional [str ],
120
+ width : Optional [str ],
121
+ precision : Optional [str ],
119
122
format_spec : Optional [str ] = None ,
120
123
conversion : Optional [str ] = None ,
121
- field : Optional [str ] = None ) -> None :
124
+ field : Optional [str ] = None ,
125
+ whole_seq : Optional [str ] = None ) -> None :
126
+ self .type = type
122
127
self .key = key
123
128
self .flags = flags
124
129
self .width = width
125
130
self .precision = precision
126
- self .type = type
127
131
# Used only for str.format() calls (it may be custom for types with __format__()).
128
132
self .format_spec = format_spec
129
133
self .non_standard_format_spec = False
@@ -132,24 +136,27 @@ def __init__(self, key: Optional[str],
132
136
# Full formatted expression (i.e. key plus following attributes and/or indexes).
133
137
# Used only for str.format() calls.
134
138
self .field = field
139
+ self .whole_seq = whole_seq
135
140
136
141
@classmethod
137
- def from_match (cls , match_obj : Match [str ],
142
+ def from_match (cls , match : Match [str ],
138
143
non_standard_spec : bool = False ) -> 'ConversionSpecifier' :
139
144
"""Construct specifier from match object resulted from parsing str.format() call."""
140
- match = cast (Any , match_obj ) # TODO: remove this once typeshed is fixed.
141
145
if non_standard_spec :
142
- spec = cls (match .group ('key' ),
143
- flags = '' , width = '' , precision = '' , type = '' ,
146
+ spec = cls (type = '' ,
147
+ key = match .group ('key' ),
148
+ flags = '' , width = '' , precision = '' ,
144
149
format_spec = match .group ('format_spec' ),
145
150
conversion = match .group ('conversion' ),
146
151
field = match .group ('field' ))
147
152
spec .non_standard_format_spec = True
148
153
return spec
149
154
# Replace unmatched optional groups with empty matches (for convenience).
150
- return cls (match .group ('key' ),
151
- flags = match .group ('flags' ) or '' , width = match .group ('width' ) or '' ,
152
- precision = match .group ('precision' ) or '' , type = match .group ('type' ) or '' ,
155
+ return cls (type = match .group ('type' ) or '' ,
156
+ key = match .group ('key' ),
157
+ flags = match .group ('flags' ) or '' ,
158
+ width = match .group ('width' ) or '' ,
159
+ precision = match .group ('precision' ) or '' ,
153
160
format_spec = match .group ('format_spec' ),
154
161
conversion = match .group ('conversion' ),
155
162
field = match .group ('field' ))
@@ -622,10 +629,12 @@ def check_str_interpolation(self,
622
629
623
630
def parse_conversion_specifiers (self , format : str ) -> List [ConversionSpecifier ]:
624
631
specifiers : List [ConversionSpecifier ] = []
625
- for parens_key , key , flags , width , precision , type in FORMAT_RE .findall (format ):
632
+ for whole_seq , parens_key , key , flags , width , precision , type \
633
+ in FORMAT_RE .findall (format ):
626
634
if parens_key == '' :
627
635
key = None
628
- specifiers .append (ConversionSpecifier (key , flags , width , precision , type ))
636
+ specifiers .append (ConversionSpecifier (type , key , flags , width , precision ,
637
+ whole_seq = whole_seq ))
629
638
return specifiers
630
639
631
640
def analyze_conversion_specifiers (self , specifiers : List [ConversionSpecifier ],
0 commit comments