mercurial/utils/stringutil.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43506 9f70512ae2cf
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.