code: def arg_spec(cls, mtd_name):
      
      mtd = getattr(cls, mtd_name)
  
      required_params = []
      optional_params = []
  
      if hasattr(inspect, 'signature'):  # Python 3
          params = inspect.signature(mtd).parameters  # pylint: disable=no-member
  
          for k in params.keys():
              if params[k].default == inspect.Parameter.empty:  # pylint: disable=no-member
                  # Python 3 does not make a difference between unbound methods and functions, so the
                  # only way to distinguish if the first argument is of a regular method, or a class
                  # method, is to look for the conventional argument name. Yikes.
                  if not (params[k].name == 'self' or params[k].name == 'cls'):
                      required_params.append(k)
              else:
                  optional_params.append(k)
      else:  # Python 2
          params = inspect.getargspec(mtd)  # pylint: disable=deprecated-method
          num = len(params[0]) if params[0] else 0
          n_opt = len(params[3]) if params[3] else 0
          n_req = (num - n_opt) if n_opt <= num else 0
          for i in range(0, n_req):
              required_params.append(params[0][i])
          for i in range(n_req, num):
              optional_params.append(params[0][i])
  
          if inspect.isroutine(getattr(cls, mtd_name)):
              bound_mtd = cls.__dict__[mtd_name]
              if not isinstance(bound_mtd, staticmethod):
                  del required_params[0]
  
      return required_params, optional_params
docs: Cross-version argument signature inspection
  
      Parameters
      ----------
      cls : class
      mtd_name : str
          Name of the method to be inspected
  
      Returns
      -------
      required_params : list of str
          List of required, positional parameters
      optional_params : list of str
          List of optional parameters, i.e. parameters with a default value