Instantly share code, notes, and snippets.
CreatedOctober 3, 2009 22:25
Save vsajip/200936 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| # | |
| # Copyright (C) 2009 Vinay Sajip. | |
| # Licensed under the PSF licence. | |
| # Exploratory code to convert %-format strings to {}-format. | |
| # | |
| importre | |
| PATTERN=re.compile(r''' | |
| % # specifier start | |
| (\((?P<key>[^)]+)\))? # optional mapping key | |
| (?P<flags>[#+0 -]*) # conversion flags | |
| (?P<min_width>(\*|([1-9][0-9]*)))? # minimum width | |
| (\.(?P<precision>(\*|([1-9][0-9]*))))? # precision | |
| (?P<len_modifier>[hlL])? # length modifier | |
| (?P<conversion>[diouxXeEfFgGcrs%]) # conversion | |
| ''',re.VERBOSE) | |
| classPercentConversion: | |
| '''Represents a single conversion group.''' | |
| def__init__(self,converter,match): | |
| ''' | |
| Initialize using the regex match information. | |
| The converter is passed because we need to keep track of the | |
| positional argument index, so that we can support minimum width | |
| and precision values of '*'. | |
| ''' | |
| self.source=match.group(0) | |
| d=match.groupdict() | |
| self.span=match.span() | |
| s=d['key'] | |
| ifsisNone: | |
| self.key=converter.next_index() | |
| else: | |
| self.key=s | |
| s=d['min_width'] | |
| ifs=='*': | |
| #TODO {} representation is hard-wired, could generalise this | |
| #if needed. | |
| self.min_width='{%s}'%converter.next_index() | |
| else: | |
| self.min_width=s | |
| s=d['precision'] | |
| ifs=='*': | |
| self.precision='{%s}'%converter.next_index() | |
| else: | |
| self.precision=s | |
| #len_modifier not used | |
| #self.len_modifier = d['len_modifier'] | |
| self.flags=d['flags'] | |
| self.conversion=d['conversion'] | |
| defas_brace(self): | |
| '''Return representation of this conversion group for {}-formatting.''' | |
| #TODO: '#', ' ' flags not handled | |
| conversion=self.conversion | |
| ifconversion=='%': | |
| result='%' | |
| else: | |
| key=self.key | |
| flags=self.flags | |
| ifself.min_widthisNone: | |
| align='' | |
| elif'-'inflags: | |
| align=''# could use '<', but as it's the default... | |
| elif'0'inflagsandconversionin'diouxX': | |
| align='=' | |
| else: | |
| align='>' | |
| sign='' | |
| if'+'inflags: | |
| sign='+' | |
| elif' 'inflags: | |
| sign=' ' | |
| alt='' | |
| fill='' | |
| if'0'inflagsand'-'notinflagsandconversionnotin'crs': | |
| fill='0' | |
| if'#'inflagsandconversionin'diuxX':# exclude 'o' | |
| alt='#' | |
| transform='' | |
| ifconversionin'iu': | |
| conversion='d' | |
| #%s is interpreted as calling str() on the operand, so | |
| #we specify !s in the {}-format. If we don't do this, then | |
| #we can't convert the case where %s is used to print e.g. integers | |
| #or floats. | |
| elifconversionin'rs': | |
| transform='!'+conversion | |
| conversion='' | |
| prefix='%s%s'% (key,transform) | |
| suffix='%s%s%s%s'% (fill,align,sign,alt) | |
| ifself.min_width: | |
| suffix+=self.min_width | |
| ifself.precision: | |
| suffix+='.'+self.precision | |
| suffix+=conversion | |
| result=prefix | |
| ifsuffix: | |
| result+=':'+suffix | |
| result='{%s}'%result | |
| returnresult | |
| classPercentToBraceConverter: | |
| '''Utility class to convert a %-format string to a {}-format string.''' | |
| def__init__(self,percent_format): | |
| self.initial=percent_format | |
| self.argindex=0 | |
| defnext_index(self): | |
| '''Get the next argument index.''' | |
| result='%d'%self.argindex | |
| self.argindex+=1 | |
| returnresult | |
| defconvert(self): | |
| '''Perform the conversion, and return its result.''' | |
| s=self.initial.replace('{','{{').replace('}','}}') | |
| result= [cforcins]# convert to mutable form | |
| matches= [mforminPATTERN.finditer(s)] | |
| conversions= [] | |
| ifmatches: | |
| forminmatches: | |
| conversions.append(PercentConversion(self,m)) | |
| #Go backwards so that span values remain valid as we go | |
| forconvinreversed(conversions): | |
| s,e=conv.span | |
| result[s:e]=conv.as_brace() | |
| return''.join(result),conversions | |
| defmain(): | |
| '''Quick-n-dirty test harness for PercentToBraceConverter.''' | |
| importsys | |
| fromrandomimportchoice | |
| ifsys.version_info[0]==3: | |
| get_str=input | |
| else: | |
| get_str=raw_input | |
| classDummy: | |
| def__str__(self): | |
| return"I'm a dummy at %#x"%id(self) | |
| INSTANCE_VALUE=Dummy() | |
| INTEGER_VALUE=0x1234 | |
| NEGATIVE_VALUE=-0x5678 | |
| FLOAT_VALUE=3.14159265358979323846 | |
| LARGE_FLOAT_VALUE=31415926535323846e108 | |
| NEGATIVE_FLOAT_VALUE=-2.71828182845904523536 | |
| LARGE_NEGATIVE_FLOAT_VALUE=-271828182845904523536e108 | |
| INT_VALUES= (INTEGER_VALUE,NEGATIVE_VALUE) | |
| FLOAT_VALUES= (FLOAT_VALUE,NEGATIVE_FLOAT_VALUE,LARGE_FLOAT_VALUE,LARGE_NEGATIVE_FLOAT_VALUE) | |
| STRING_VALUE='abcd' | |
| STRING_VALUES= (INTEGER_VALUE,NEGATIVE_VALUE,FLOAT_VALUE,LARGE_FLOAT_VALUE,NEGATIVE_FLOAT_VALUE,LARGE_NEGATIVE_FLOAT_VALUE,STRING_VALUE,INSTANCE_VALUE) | |
| VALUE_MAP= { | |
| 'd' :INT_VALUES, | |
| 'i' :INT_VALUES, | |
| 'o' :INT_VALUES, | |
| 'u' :INT_VALUES, | |
| 'x' :INT_VALUES, | |
| 'X' :INT_VALUES, | |
| 'e' :FLOAT_VALUES, | |
| 'E' :FLOAT_VALUES, | |
| 'f' :FLOAT_VALUES, | |
| 'F' :FLOAT_VALUES, | |
| 'g' :FLOAT_VALUES, | |
| 'G' :FLOAT_VALUES, | |
| 'c' : (INTEGER_VALUE,), | |
| 'r' :STRING_VALUES, | |
| 's' :STRING_VALUES, | |
| } | |
| whileTrue: | |
| s=get_str("Enter a %-format string:") | |
| ifnots: | |
| break | |
| f,conversions=PercentToBraceConverter(s).convert() | |
| print('%s -> %s'% (s,f)) | |
| ifconversions: | |
| values= [] | |
| forcinconversions: | |
| vals=VALUE_MAP.get(c.conversion,None) | |
| ifvals: | |
| values.append(choice(vals)) | |
| values=tuple(values) | |
| print("%-20s: %s"% ('Example raw values',', '.join([repr(v)forvinvalues]))) | |
| print("%-20s: %s"% ('Output using %',s%values)) | |
| print("%-20s: %s"% ('Output using {}',f.format(*values))) | |
| if__name__=="__main__": | |
| main() |
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment