code: def _make_multidim_func(one_d_func, n, *args):
      
      _args = list(args)
      n = np.atleast_1d(n)
      args = list(map(np.atleast_1d, _args))
  
      if all([x.size == 1 for x in [n] + args]):
          return one_d_func(n[0], *_args)
  
      d = n.size
  
      for i in range(len(args)):
          if args[i].size == 1:
              args[i] = np.repeat(args[i], d)
  
      nodes = []
      weights = []
  
      for i in range(d):
          ai = [x[i] for x in args]
          _1d = one_d_func(n[i], *ai)
          nodes.append(_1d[0])
          weights.append(_1d[1])
  
      weights = ckron(*weights[::-1])  # reverse ordered tensor product
  
      nodes = gridmake(*nodes)
      return nodes, weights
docs: A helper function to cut down on code repetition. Almost all of the
      code in qnwcheb, qnwlege, qnwsimp, qnwtrap is just dealing
      various forms of input arguments and then shelling out to the
      corresponding 1d version of the function.
  
      This routine does all the argument checking and passes things
      through the appropriate 1d function before using a tensor product
      to combine weights and nodes.
  
      Parameters
      ----------
      one_d_func : function
          The 1d function to be called along each dimension
  
      n : int or array_like(float)
          A length-d iterable of the number of nodes in each dimension
  
      args :
          These are the arguments to various qnw____ functions.  For the
          majority of the functions this is just a and b, but some differ.
  
      Returns
      -------
      func : function
          The multi-dimensional version of the parameter ``one_d_func``