docs: def make_annotations(f, globals_d=None):
      # type: (Callable, Dict) -> Dict[str, Any]
      """Create an annotations dictionary from Python2 type comments
  
      http://mypy.readthedocs.io/en/latest/python2.html
  
      Args:
          f: The function to examine for type comments
          globals_d: The globals dictionary to get type idents from. If not
              specified then make the annotations dict contain strings rather
              than the looked up objects
      """
      locals_d = {}  # type: Dict[str, Any]
      if globals_d is None:
          # If not given a globals_d then we should just populate annotations with
          # the strings in the type comment.
          globals_d = {}
          # The current approach is to use eval, which means manufacturing a
          # dict like object that will just echo the string back to you. This
          # has a number of complexities for somthing like numpy.number or
          # Callable[..., int], which are handled in EchoStr above, so it might be
          # better off as an ast.parse in the future...
          locals_d = EchoDict()
      lines, _ = inspect.getsourcelines(f)
      arg_spec = getargspec(f)
      args = list(arg_spec.args)
      if arg_spec.varargs is not None:
          args.append(arg_spec.varargs)
      if arg_spec.keywords is not None:
          args.append(arg_spec.keywords)
      it = iter(lines)
      types = []  # type: List
      found = None
      for token in tokenize.generate_tokens(lambda: next(it)):
          typ, string, start, end, line = token
          if typ == tokenize.COMMENT:
              found = type_re.match(string)
              if found:
                  parts = found.groups()
                  # (...) is used to represent all the args so far
                  if parts[0] != "(...)":
                      expr = parts[0].replace("*", "")
                      try:
                          ob = eval(expr, globals_d, locals_d)
                      except Exception as e:
                          raise ValueError(
                              "Error evaluating %r: %s" % (expr, e))
                      if isinstance(ob, tuple):
                          # We got more than one argument
                          types += list(ob)
                      else:
                          # We got a single argument
                          types.append(ob)
                  if parts[1]:
                      # Got a return, done
                      try:
                          ob = eval(parts[2], globals_d, locals_d)
                      except Exception as e:
                          raise ValueError(
                              "Error evaluating %r: %s" % (parts[2], e))
                      if args and args[0] in ["self", "cls"]:
                          # Allow the first argument to be inferred
                          if len(args) == len(types) + 1:
                              args = args[1:]
                      assert len(args) == len(types), \
                          "Args %r Types %r length mismatch" % (args, types)
                      ret = dict(zip(args, types))
                      ret["return"] = ob
                      return ret
      if found:
          # If we have ever found a type comment, but not the return value, error
          raise ValueError("Got to the end of the function without seeing ->")
      return {}