code: def visit_Str(self, node):
          
          # Old format strings.
          self.used_names |= set(re.findall(r'\%\((\w+)\)', node.s))
  
          def is_identifier(s):
              return bool(re.match(r'[a-zA-Z_][a-zA-Z0-9_]*', s))
  
          # New format strings.
          parser = string.Formatter()
          try:
              names = [name for _, name, _, _ in parser.parse(node.s) if name]
          except ValueError:
              # Invalid format string.
              names = []
  
          for field_name in names:
              # Remove brackets and contents: "a[0][b].c[d].e" -> "a.c.e".
              # "a.b.c" -> name = "a", attributes = ["b", "c"]
              name_and_attrs = re.sub(r'\[\w*\]', '', field_name).split('.')
              name = name_and_attrs[0]
              if is_identifier(name):
                  self.used_names.add(name)
              for attr in name_and_attrs[1:]:
                  if is_identifier(attr):
                      self.used_attrs.add(attr)
docs: Parse variable names in format strings:
  
          '%(my_var)s' % locals()
          '{my_var}'.format(**locals())