comparison mercurial/utils/stringutil.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children 9f70512ae2cf
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
64 ``level`` specifies the initial indent level. Used if ``indent > 0``. 64 ``level`` specifies the initial indent level. Used if ``indent > 0``.
65 """ 65 """
66 66
67 if isinstance(o, bytes): 67 if isinstance(o, bytes):
68 if bprefix: 68 if bprefix:
69 yield "b'%s'" % escapestr(o) 69 yield b"b'%s'" % escapestr(o)
70 else: 70 else:
71 yield "'%s'" % escapestr(o) 71 yield b"'%s'" % escapestr(o)
72 elif isinstance(o, bytearray): 72 elif isinstance(o, bytearray):
73 # codecs.escape_encode() can't handle bytearray, so escapestr fails 73 # codecs.escape_encode() can't handle bytearray, so escapestr fails
74 # without coercion. 74 # without coercion.
75 yield "bytearray['%s']" % escapestr(bytes(o)) 75 yield b"bytearray['%s']" % escapestr(bytes(o))
76 elif isinstance(o, list): 76 elif isinstance(o, list):
77 if not o: 77 if not o:
78 yield '[]' 78 yield b'[]'
79 return 79 return
80 80
81 yield '[' 81 yield b'['
82 82
83 if indent: 83 if indent:
84 level += 1 84 level += 1
85 yield '\n' 85 yield b'\n'
86 yield ' ' * (level * indent) 86 yield b' ' * (level * indent)
87 87
88 for i, a in enumerate(o): 88 for i, a in enumerate(o):
89 for chunk in pprintgen( 89 for chunk in pprintgen(
90 a, bprefix=bprefix, indent=indent, level=level 90 a, bprefix=bprefix, indent=indent, level=level
91 ): 91 ):
92 yield chunk 92 yield chunk
93 93
94 if i + 1 < len(o): 94 if i + 1 < len(o):
95 if indent: 95 if indent:
96 yield ',\n' 96 yield b',\n'
97 yield ' ' * (level * indent) 97 yield b' ' * (level * indent)
98 else: 98 else:
99 yield ', ' 99 yield b', '
100 100
101 if indent: 101 if indent:
102 level -= 1 102 level -= 1
103 yield '\n' 103 yield b'\n'
104 yield ' ' * (level * indent) 104 yield b' ' * (level * indent)
105 105
106 yield ']' 106 yield b']'
107 elif isinstance(o, dict): 107 elif isinstance(o, dict):
108 if not o: 108 if not o:
109 yield '{}' 109 yield b'{}'
110 return 110 return
111 111
112 yield '{' 112 yield b'{'
113 113
114 if indent: 114 if indent:
115 level += 1 115 level += 1
116 yield '\n' 116 yield b'\n'
117 yield ' ' * (level * indent) 117 yield b' ' * (level * indent)
118 118
119 for i, (k, v) in enumerate(sorted(o.items())): 119 for i, (k, v) in enumerate(sorted(o.items())):
120 for chunk in pprintgen( 120 for chunk in pprintgen(
121 k, bprefix=bprefix, indent=indent, level=level 121 k, bprefix=bprefix, indent=indent, level=level
122 ): 122 ):
123 yield chunk 123 yield chunk
124 124
125 yield ': ' 125 yield b': '
126 126
127 for chunk in pprintgen( 127 for chunk in pprintgen(
128 v, bprefix=bprefix, indent=indent, level=level 128 v, bprefix=bprefix, indent=indent, level=level
129 ): 129 ):
130 yield chunk 130 yield chunk
131 131
132 if i + 1 < len(o): 132 if i + 1 < len(o):
133 if indent: 133 if indent:
134 yield ',\n' 134 yield b',\n'
135 yield ' ' * (level * indent) 135 yield b' ' * (level * indent)
136 else: 136 else:
137 yield ', ' 137 yield b', '
138 138
139 if indent: 139 if indent:
140 level -= 1 140 level -= 1
141 yield '\n' 141 yield b'\n'
142 yield ' ' * (level * indent) 142 yield b' ' * (level * indent)
143 143
144 yield '}' 144 yield b'}'
145 elif isinstance(o, set): 145 elif isinstance(o, set):
146 if not o: 146 if not o:
147 yield 'set([])' 147 yield b'set([])'
148 return 148 return
149 149
150 yield 'set([' 150 yield b'set(['
151 151
152 if indent: 152 if indent:
153 level += 1 153 level += 1
154 yield '\n' 154 yield b'\n'
155 yield ' ' * (level * indent) 155 yield b' ' * (level * indent)
156 156
157 for i, k in enumerate(sorted(o)): 157 for i, k in enumerate(sorted(o)):
158 for chunk in pprintgen( 158 for chunk in pprintgen(
159 k, bprefix=bprefix, indent=indent, level=level 159 k, bprefix=bprefix, indent=indent, level=level
160 ): 160 ):
161 yield chunk 161 yield chunk
162 162
163 if i + 1 < len(o): 163 if i + 1 < len(o):
164 if indent: 164 if indent:
165 yield ',\n' 165 yield b',\n'
166 yield ' ' * (level * indent) 166 yield b' ' * (level * indent)
167 else: 167 else:
168 yield ', ' 168 yield b', '
169 169
170 if indent: 170 if indent:
171 level -= 1 171 level -= 1
172 yield '\n' 172 yield b'\n'
173 yield ' ' * (level * indent) 173 yield b' ' * (level * indent)
174 174
175 yield '])' 175 yield b'])'
176 elif isinstance(o, tuple): 176 elif isinstance(o, tuple):
177 if not o: 177 if not o:
178 yield '()' 178 yield b'()'
179 return 179 return
180 180
181 yield '(' 181 yield b'('
182 182
183 if indent: 183 if indent:
184 level += 1 184 level += 1
185 yield '\n' 185 yield b'\n'
186 yield ' ' * (level * indent) 186 yield b' ' * (level * indent)
187 187
188 for i, a in enumerate(o): 188 for i, a in enumerate(o):
189 for chunk in pprintgen( 189 for chunk in pprintgen(
190 a, bprefix=bprefix, indent=indent, level=level 190 a, bprefix=bprefix, indent=indent, level=level
191 ): 191 ):
192 yield chunk 192 yield chunk
193 193
194 if i + 1 < len(o): 194 if i + 1 < len(o):
195 if indent: 195 if indent:
196 yield ',\n' 196 yield b',\n'
197 yield ' ' * (level * indent) 197 yield b' ' * (level * indent)
198 else: 198 else:
199 yield ', ' 199 yield b', '
200 200
201 if indent: 201 if indent:
202 level -= 1 202 level -= 1
203 yield '\n' 203 yield b'\n'
204 yield ' ' * (level * indent) 204 yield b' ' * (level * indent)
205 205
206 yield ')' 206 yield b')'
207 elif isinstance(o, types.GeneratorType): 207 elif isinstance(o, types.GeneratorType):
208 # Special case of empty generator. 208 # Special case of empty generator.
209 try: 209 try:
210 nextitem = next(o) 210 nextitem = next(o)
211 except StopIteration: 211 except StopIteration:
212 yield 'gen[]' 212 yield b'gen[]'
213 return 213 return
214 214
215 yield 'gen[' 215 yield b'gen['
216 216
217 if indent: 217 if indent:
218 level += 1 218 level += 1
219 yield '\n' 219 yield b'\n'
220 yield ' ' * (level * indent) 220 yield b' ' * (level * indent)
221 221
222 last = False 222 last = False
223 223
224 while not last: 224 while not last:
225 current = nextitem 225 current = nextitem
234 ): 234 ):
235 yield chunk 235 yield chunk
236 236
237 if not last: 237 if not last:
238 if indent: 238 if indent:
239 yield ',\n' 239 yield b',\n'
240 yield ' ' * (level * indent) 240 yield b' ' * (level * indent)
241 else: 241 else:
242 yield ', ' 242 yield b', '
243 243
244 if indent: 244 if indent:
245 level -= 1 245 level -= 1
246 yield '\n' 246 yield b'\n'
247 yield ' ' * (level * indent) 247 yield b' ' * (level * indent)
248 248
249 yield ']' 249 yield b']'
250 else: 250 else:
251 yield pycompat.byterepr(o) 251 yield pycompat.byterepr(o)
252 252
253 253
254 def prettyrepr(o): 254 def prettyrepr(o):
259 while p0 < len(rs): 259 while p0 < len(rs):
260 # '... field=<type ... field=<type ...' 260 # '... field=<type ... field=<type ...'
261 # ~~~~~~~~~~~~~~~~ 261 # ~~~~~~~~~~~~~~~~
262 # p0 p1 q0 q1 262 # p0 p1 q0 q1
263 q0 = -1 263 q0 = -1
264 q1 = rs.find('<', p1 + 1) 264 q1 = rs.find(b'<', p1 + 1)
265 if q1 < 0: 265 if q1 < 0:
266 q1 = len(rs) 266 q1 = len(rs)
267 elif q1 > p1 + 1 and rs.startswith('=', q1 - 1): 267 elif q1 > p1 + 1 and rs.startswith(b'=', q1 - 1):
268 # backtrack for ' field=<' 268 # backtrack for ' field=<'
269 q0 = rs.rfind(' ', p1 + 1, q1 - 1) 269 q0 = rs.rfind(b' ', p1 + 1, q1 - 1)
270 if q0 < 0: 270 if q0 < 0:
271 q0 = q1 271 q0 = q1
272 else: 272 else:
273 q0 += 1 # skip ' ' 273 q0 += 1 # skip ' '
274 l = rs.count('<', 0, p0) - rs.count('>', 0, p0) 274 l = rs.count(b'<', 0, p0) - rs.count(b'>', 0, p0)
275 assert l >= 0 275 assert l >= 0
276 lines.append((l, rs[p0:q0].rstrip())) 276 lines.append((l, rs[p0:q0].rstrip()))
277 p0, p1 = q0, q1 277 p0, p1 = q0, q1
278 return '\n'.join(' ' * l + s for l, s in lines) 278 return b'\n'.join(b' ' * l + s for l, s in lines)
279 279
280 280
281 def buildrepr(r): 281 def buildrepr(r):
282 """Format an optional printable representation from unexpanded bits 282 """Format an optional printable representation from unexpanded bits
283 283
289 callable lambda: '<branch %r>' % sorted(b) 289 callable lambda: '<branch %r>' % sorted(b)
290 object other 290 object other
291 ======== ================================= 291 ======== =================================
292 """ 292 """
293 if r is None: 293 if r is None:
294 return '' 294 return b''
295 elif isinstance(r, tuple): 295 elif isinstance(r, tuple):
296 return r[0] % pycompat.rapply(pycompat.maybebytestr, r[1:]) 296 return r[0] % pycompat.rapply(pycompat.maybebytestr, r[1:])
297 elif isinstance(r, bytes): 297 elif isinstance(r, bytes):
298 return r 298 return r
299 elif callable(r): 299 elif callable(r):
302 return pprint(r) 302 return pprint(r)
303 303
304 304
305 def binary(s): 305 def binary(s):
306 """return true if a string is binary data""" 306 """return true if a string is binary data"""
307 return bool(s and '\0' in s) 307 return bool(s and b'\0' in s)
308 308
309 309
310 def stringmatcher(pattern, casesensitive=True): 310 def stringmatcher(pattern, casesensitive=True):
311 """ 311 """
312 accepts a string, possibly starting with 're:' or 'literal:' prefix. 312 accepts a string, possibly starting with 're:' or 'literal:' prefix.
343 343
344 case insensitive literal matches 344 case insensitive literal matches
345 >>> itest(b'ABCDEFG', b'abc', b'def', b'abcdefg') 345 >>> itest(b'ABCDEFG', b'abc', b'def', b'abcdefg')
346 ('literal', 'ABCDEFG', [False, False, True]) 346 ('literal', 'ABCDEFG', [False, False, True])
347 """ 347 """
348 if pattern.startswith('re:'): 348 if pattern.startswith(b're:'):
349 pattern = pattern[3:] 349 pattern = pattern[3:]
350 try: 350 try:
351 flags = 0 351 flags = 0
352 if not casesensitive: 352 if not casesensitive:
353 flags = remod.I 353 flags = remod.I
354 regex = remod.compile(pattern, flags) 354 regex = remod.compile(pattern, flags)
355 except remod.error as e: 355 except remod.error as e:
356 raise error.ParseError(_('invalid regular expression: %s') % e) 356 raise error.ParseError(_(b'invalid regular expression: %s') % e)
357 return 're', pattern, regex.search 357 return b're', pattern, regex.search
358 elif pattern.startswith('literal:'): 358 elif pattern.startswith(b'literal:'):
359 pattern = pattern[8:] 359 pattern = pattern[8:]
360 360
361 match = pattern.__eq__ 361 match = pattern.__eq__
362 362
363 if not casesensitive: 363 if not casesensitive:
364 ipat = encoding.lower(pattern) 364 ipat = encoding.lower(pattern)
365 match = lambda s: ipat == encoding.lower(s) 365 match = lambda s: ipat == encoding.lower(s)
366 return 'literal', pattern, match 366 return b'literal', pattern, match
367 367
368 368
369 def shortuser(user): 369 def shortuser(user):
370 """Return a short representation of a user name or email address.""" 370 """Return a short representation of a user name or email address."""
371 f = user.find('@') 371 f = user.find(b'@')
372 if f >= 0: 372 if f >= 0:
373 user = user[:f] 373 user = user[:f]
374 f = user.find('<') 374 f = user.find(b'<')
375 if f >= 0: 375 if f >= 0:
376 user = user[f + 1 :] 376 user = user[f + 1 :]
377 f = user.find(' ') 377 f = user.find(b' ')
378 if f >= 0: 378 if f >= 0:
379 user = user[:f] 379 user = user[:f]
380 f = user.find('.') 380 f = user.find(b'.')
381 if f >= 0: 381 if f >= 0:
382 user = user[:f] 382 user = user[:f]
383 return user 383 return user
384 384
385 385
386 def emailuser(user): 386 def emailuser(user):
387 """Return the user portion of an email address.""" 387 """Return the user portion of an email address."""
388 f = user.find('@') 388 f = user.find(b'@')
389 if f >= 0: 389 if f >= 0:
390 user = user[:f] 390 user = user[:f]
391 f = user.find('<') 391 f = user.find(b'<')
392 if f >= 0: 392 if f >= 0:
393 user = user[f + 1 :] 393 user = user[f + 1 :]
394 return user 394 return user
395 395
396 396
397 def email(author): 397 def email(author):
398 '''get email of author.''' 398 '''get email of author.'''
399 r = author.find('>') 399 r = author.find(b'>')
400 if r == -1: 400 if r == -1:
401 r = None 401 r = None
402 return author[author.find('<') + 1 : r] 402 return author[author.find(b'<') + 1 : r]
403 403
404 404
405 def person(author): 405 def person(author):
406 """Returns the name before an email address, 406 """Returns the name before an email address,
407 interpreting it as per RFC 5322 407 interpreting it as per RFC 5322
419 >>> person(b'Foo "buz" Bar <foo@bar>') 419 >>> person(b'Foo "buz" Bar <foo@bar>')
420 'Foo "buz" Bar' 420 'Foo "buz" Bar'
421 >>> person(b'"Foo Bar <foo@bar>') 421 >>> person(b'"Foo Bar <foo@bar>')
422 'Foo Bar' 422 'Foo Bar'
423 """ 423 """
424 if '@' not in author: 424 if b'@' not in author:
425 return author 425 return author
426 f = author.find('<') 426 f = author.find(b'<')
427 if f != -1: 427 if f != -1:
428 return author[:f].strip(' "').replace('\\"', '"') 428 return author[:f].strip(b' "').replace(b'\\"', b'"')
429 f = author.find('@') 429 f = author.find(b'@')
430 return author[:f].replace('.', ' ') 430 return author[:f].replace(b'.', b' ')
431 431
432 432
433 @attr.s(hash=True) 433 @attr.s(hash=True)
434 class mailmapping(object): 434 class mailmapping(object):
435 '''Represents a username/email key or value in 435 '''Represents a username/email key or value in
495 495
496 for line in mailmapcontent.splitlines(): 496 for line in mailmapcontent.splitlines():
497 497
498 # Don't bother checking the line if it is a comment or 498 # Don't bother checking the line if it is a comment or
499 # is an improperly formed author field 499 # is an improperly formed author field
500 if line.lstrip().startswith('#'): 500 if line.lstrip().startswith(b'#'):
501 continue 501 continue
502 502
503 # names, emails hold the parsed emails and names for each line 503 # names, emails hold the parsed emails and names for each line
504 # name_builder holds the words in a persons name 504 # name_builder holds the words in a persons name
505 names, emails = [], [] 505 names, emails = [], []
506 namebuilder = [] 506 namebuilder = []
507 507
508 for element in line.split(): 508 for element in line.split():
509 if element.startswith('#'): 509 if element.startswith(b'#'):
510 # If we reach a comment in the mailmap file, move on 510 # If we reach a comment in the mailmap file, move on
511 break 511 break
512 512
513 elif element.startswith('<') and element.endswith('>'): 513 elif element.startswith(b'<') and element.endswith(b'>'):
514 # We have found an email. 514 # We have found an email.
515 # Parse it, and finalize any names from earlier 515 # Parse it, and finalize any names from earlier
516 emails.append(element[1:-1]) # Slice off the "<>" 516 emails.append(element[1:-1]) # Slice off the "<>"
517 517
518 if namebuilder: 518 if namebuilder:
519 names.append(' '.join(namebuilder)) 519 names.append(b' '.join(namebuilder))
520 namebuilder = [] 520 namebuilder = []
521 521
522 # Break if we have found a second email, any other 522 # Break if we have found a second email, any other
523 # data does not fit the spec for .mailmap 523 # data does not fit the spec for .mailmap
524 if len(emails) > 1: 524 if len(emails) > 1:
585 # We call this commit2 as not to erase original commit fields 585 # We call this commit2 as not to erase original commit fields
586 commit2 = mailmapping(email=commit.email) 586 commit2 = mailmapping(email=commit.email)
587 proper = mailmap.get(commit2, mailmapping(None, None)) 587 proper = mailmap.get(commit2, mailmapping(None, None))
588 588
589 # Return the author field with proper values filled in 589 # Return the author field with proper values filled in
590 return '%s <%s>' % ( 590 return b'%s <%s>' % (
591 proper.name if proper.name else commit.name, 591 proper.name if proper.name else commit.name,
592 proper.email if proper.email else commit.email, 592 proper.email if proper.email else commit.email,
593 ) 593 )
594 594
595 595
618 return _correctauthorformat.match(author) is not None 618 return _correctauthorformat.match(author) is not None
619 619
620 620
621 def ellipsis(text, maxlength=400): 621 def ellipsis(text, maxlength=400):
622 """Trim string to at most maxlength (default: 400) columns in display.""" 622 """Trim string to at most maxlength (default: 400) columns in display."""
623 return encoding.trim(text, maxlength, ellipsis='...') 623 return encoding.trim(text, maxlength, ellipsis=b'...')
624 624
625 625
626 def escapestr(s): 626 def escapestr(s):
627 if isinstance(s, memoryview): 627 if isinstance(s, memoryview):
628 s = bytes(s) 628 s = bytes(s)
673 colwidth = encoding.ucolwidth 673 colwidth = encoding.ucolwidth
674 for i in pycompat.xrange(len(ucstr)): 674 for i in pycompat.xrange(len(ucstr)):
675 l += colwidth(ucstr[i]) 675 l += colwidth(ucstr[i])
676 if space_left < l: 676 if space_left < l:
677 return (ucstr[:i], ucstr[i:]) 677 return (ucstr[:i], ucstr[i:])
678 return ucstr, '' 678 return ucstr, b''
679 679
680 # overriding of base class 680 # overriding of base class
681 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): 681 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
682 space_left = max(width - cur_len, 1) 682 space_left = max(width - cur_len, 1)
683 683
693 def _wrap_chunks(self, chunks): 693 def _wrap_chunks(self, chunks):
694 colwidth = encoding.ucolwidth 694 colwidth = encoding.ucolwidth
695 695
696 lines = [] 696 lines = []
697 if self.width <= 0: 697 if self.width <= 0:
698 raise ValueError("invalid width %r (must be > 0)" % self.width) 698 raise ValueError(b"invalid width %r (must be > 0)" % self.width)
699 699
700 # Arrange in reverse order so items can be efficiently popped 700 # Arrange in reverse order so items can be efficiently popped
701 # from a stack of chucks. 701 # from a stack of chucks.
702 chunks.reverse() 702 chunks.reverse()
703 703
757 global _MBTextWrapper 757 global _MBTextWrapper
758 _MBTextWrapper = tw 758 _MBTextWrapper = tw
759 return tw(**kwargs) 759 return tw(**kwargs)
760 760
761 761
762 def wrap(line, width, initindent='', hangindent=''): 762 def wrap(line, width, initindent=b'', hangindent=b''):
763 maxindent = max(len(hangindent), len(initindent)) 763 maxindent = max(len(hangindent), len(initindent))
764 if width <= maxindent: 764 if width <= maxindent:
765 # adjust for weird terminal size 765 # adjust for weird terminal size
766 width = max(78, maxindent + 1) 766 width = max(78, maxindent + 1)
767 line = line.decode( 767 line = line.decode(
781 ) 781 )
782 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding)) 782 return wrapper.fill(line).encode(pycompat.sysstr(encoding.encoding))
783 783
784 784
785 _booleans = { 785 _booleans = {
786 '1': True, 786 b'1': True,
787 'yes': True, 787 b'yes': True,
788 'true': True, 788 b'true': True,
789 'on': True, 789 b'on': True,
790 'always': True, 790 b'always': True,
791 '0': False, 791 b'0': False,
792 'no': False, 792 b'no': False,
793 'false': False, 793 b'false': False,
794 'off': False, 794 b'off': False,
795 'never': False, 795 b'never': False,
796 } 796 }
797 797
798 798
799 def parsebool(s): 799 def parsebool(s):
800 """Parse s into a boolean. 800 """Parse s into a boolean.