Beispiel #1
0
    def visit_JoinedStr(self, node):
        self.prefix(node)
        content = fmt.get(node, 'content')

        if content is None:
            parts = []
            for val in node.values:
                if isinstance(val, ast.Str):
                    parts.append(val.s)
                else:
                    parts.append(fstring_utils.placeholder(len(parts)))
            content = repr(''.join(parts))

        values = [to_str(v) for v in fstring_utils.get_formatted_values(node)]
        self.code += fstring_utils.perform_replacements(content, values)
        self.suffix(node)
Beispiel #2
0
    def fstr_parser():
      # Reads the whole fstring as a string, then parses it char by char
      if self.peek_non_whitespace().type == TOKENS.STRING:
        # Normal fstrings are one ore more STRING tokens, maybe mixed with
        # spaces, e.g.: f"Hello, {name}"
        str_content = self.str()
      else:
        # Format specifiers in fstrings are also JoinedStr nodes, but these are
        # arbitrary expressions, e.g. in: f"{value:{width}.{precision}}", the
        # format specifier is an fstring: "{width}.{precision}" but these are
        # not STRING tokens.
        def fstr_eater(tok):
          if tok.type == TOKENS.OP and tok.src == '}':
            if fstr_eater.level <= 0:
              return False
            fstr_eater.level -= 1
          if tok.type == TOKENS.OP and tok.src == '{':
            fstr_eater.level += 1
          return True
        fstr_eater.level = 0
        str_content = self.eat_tokens(fstr_eater)

      indexed_chars = enumerate(str_content)
      val_idx = 0
      i = -1
      result = ''
      in_fstring = False
      string_quote = None
      while i < len(str_content) - 1:
        i, c = next(indexed_chars)
        result += c

        # If we haven't actually parsing string content yet, check if a string
        # (with or without fstring prefix) has started
        if string_quote is None:
          if str_content[i:i+4] in ('f"""', "f'''"):
            string_quote = str_content[i+1:i+4]
            in_fstring = True
          elif str_content[i:i+3] in ('"""', "'''"):
            string_quote = str_content[i:i+3]
            in_fstring = False
          elif str_content[i:+2] in ('f"', "f'"):
            string_quote = str_content[i+1]
            in_fstring = True
          elif c in ('"', "'"):
            string_quote = c
            in_fstring = False
          if string_quote:
            # Skip uneaten quote characters
            for _ in range(len(string_quote) + (1 if in_fstring else 0) - 1):
              i, c = next(indexed_chars)
              result += c
            continue

        # If we are still not parsing characters in a string, no extra
        # processing is needed
        if string_quote is None:
          continue

        # If we ARE in a string, check if the next characters are the
        # close-quote for that string
        if (str_content[i:i+len(string_quote)] == string_quote and
            str_content[i-1] != '\\'):
          # Skip uneaten quote characters
          for _ in range(len(string_quote) - 1):
            i, c = next(indexed_chars)
            result += c
          string_quote = None
          in_fstring = False
          continue

        # If we are NOT in an fstring, skip all FormattedValue processing.
        if not in_fstring:
          continue

        # When an open bracket is encountered, start parsing a subexpression
        if c == '{':
          # First check if this is part of an escape sequence
          # (f"{{" is used to escape a bracket literal)
          nexti, nextc = next(indexed_chars)
          if nextc == '{':
            result += c
            continue
          indexed_chars = itertools.chain([(nexti, nextc)], indexed_chars)

          # Add a placeholder onto the result
          result += fstring_utils.placeholder(val_idx) + '}'
          val_idx += 1

          # Yield a new token generator to parse the subexpression only
          tg = TokenGenerator(str_content[i+1:], ignore_error_token=True)
          yield (result, tg)
          result = ''

          # Skip the number of characters consumed by the subexpression
          for tg_i in range(tg.chars_consumed()):
            i, c = next(indexed_chars)

          # Eat up to and including the close bracket
          i, c = next(indexed_chars)
          while c != '}':
            i, c = next(indexed_chars)
      # Yield the rest of the fstring, when done
      yield (result, None)
        def fstr_parser():
            # Reads the whole fstring as a string, then parses it char by char
            if self.peek_non_whitespace().type == TOKENS.STRING:
                # Normal fstrings are one ore more STRING tokens, maybe mixed with
                # spaces, e.g.: f"Hello, {name}"
                str_content = self.str()
            else:
                # Format specifiers in fstrings are also JoinedStr nodes, but these are
                # arbitrary expressions, e.g. in: f"{value:{width}.{precision}}", the
                # format specifier is an fstring: "{width}.{precision}" but these are
                # not STRING tokens.
                def fstr_eater(tok):
                    if tok.type == TOKENS.OP and tok.src == '}':
                        if fstr_eater.level <= 0:
                            return False
                        fstr_eater.level -= 1
                    if tok.type == TOKENS.OP and tok.src == '{':
                        fstr_eater.level += 1
                    return True

                fstr_eater.level = 0
                str_content = self.eat_tokens(fstr_eater)

            indexed_chars = enumerate(str_content)
            val_idx = 0
            i = -1
            result = ''
            while i < len(str_content) - 1:
                i, c = next(indexed_chars)
                result += c

                # When an open bracket is encountered, start parsing a subexpression
                if c == '{':
                    # First check if this is part of an escape sequence
                    # (f"{{" is used to escape a bracket literal)
                    nexti, nextc = next(indexed_chars)
                    if nextc == '{':
                        result += c
                        continue
                    indexed_chars = itertools.chain([(nexti, nextc)],
                                                    indexed_chars)

                    # Add a placeholder onto the result
                    result += fstring_utils.placeholder(val_idx) + '}'
                    val_idx += 1

                    # Yield a new token generator to parse the subexpression only
                    tg = TokenGenerator(str_content[i + 1:],
                                        ignore_error_token=True)
                    yield (result, tg)
                    result = ''

                    # Skip the number of characters consumed by the subexpression
                    for tg_i in range(tg.chars_consumed()):
                        i, c = next(indexed_chars)

                    # Eat up to and including the close bracket
                    i, c = next(indexed_chars)
                    while c != '}':
                        i, c = next(indexed_chars)
            # Yield the rest of the fstring, when done
            yield (result, None)