docs: def qsub(command, queue=None, cwd=True, name=None, deps=[], stdout='',
      stderr='', env=[], array=None, context='grid', hostname=None,
      memfree=None, hvmem=None, gpumem=None, pe_opt=None, io_big=False):
    """Submits a shell job to a given grid queue
  
    Keyword parameters:
  
    command
      The command to be submitted to the grid
  
    queue
      A valid queue name or None, to use the default queue
  
    cwd
      If the job should change to the current working directory before starting
  
    name
      An optional name to set for the job. If not given, defaults to the script
      name being launched.
  
    deps
      Job ids to which this job will be dependent on
  
    stdout
      The standard output directory. If not given, defaults to what qsub has as a
      default.
  
    stderr
      The standard error directory (if not given, defaults to the stdout
      directory).
  
    env
      This is a list of extra variables that will be set on the environment
      running the command of your choice.
  
    array
      If set should be either:
  
      1. a string in the form m[-n[:s]] which indicates the starting range 'm',
         the closing range 'n' and the step 's'.
      2. an integer value indicating the total number of jobs to be submitted.
         This is equivalent ot set the parameter to a string "1-k:1" where "k" is
         the passed integer value
      3. a tuple that contains either 1, 2 or 3 elements indicating the start,
         end and step arguments ("m", "n", "s").
  
      The minimum value for "m" is 1. Giving "0" is an error.
  
      If submitted with this option, the job to be created will be an SGE
      parametric job. In this mode SGE does not allow individual control of each
      job. The environment variable SGE_TASK_ID will be set on the executing
      process automatically by SGE and indicates the unique identifier in the
      range for which the current job instance is for.
  
    context
      The setshell context in which we should try a 'qsub'. Normally you don't
      need to change the default. This variable can also be set to a context
      dictionary in which case we just setup using that context instead of
      probing for a new one, what can be fast.
  
    memfree
      If set, it asks the queue for a node with a minimum amount of memory
      Used only if mem is not set
      (cf. qsub -l mem_free=<...>)
  
    hvmem
      If set, it asks the queue for a node with a minimum amount of memory
      Used only if mem is not set
      (cf. qsub -l h_vmem=<...>)
  
    gpumem
      Applicable only for GPU-based queues. If set, it asks for the GPU queue
      with a minimum amount of memory. The amount should not be more than 24.
      (cf. qsub -l gpumem=<...>)
  
    hostname
      If set, it asks the queue to use only a subset of the available nodes
      Symbols: | for OR, & for AND, ! for NOT, etc.
      (cf. qsub -l hostname=<...>)
  
    pe_opt
      If set, add a -pe option when launching a job (for instance pe_exclusive* 1-)
  
    io_big
      If set to true, the io_big flag will be set.
      Use this flag if your process will need a lot of Input/Output operations.
  
    Returns the job id assigned to this job (integer)
    """
  
    scmd = ['qsub']
  
    import six
    if isinstance(queue, six.string_types) and queue not in ('all.q', 'default'):
      scmd += ['-l', queue]
  
    if memfree: scmd += ['-l', 'mem_free=%s' % memfree]
    if hvmem: scmd += ['-l', 'h_vmem=%s' % hvmem]
  
    if gpumem: scmd += ['-l', 'gpumem=%s' % gpumem]
  
    if io_big: scmd += ['-l', 'io_big']
  
    if hostname: scmd += ['-l', 'hostname=%s' % hostname]
  
    if pe_opt: scmd += ['-pe'] + pe_opt.split()
  
    if cwd: scmd += ['-cwd']
  
    if name: scmd += ['-N', name]
  
    if deps: scmd += ['-hold_jid', ','.join(['%d' % k for k in deps])]
  
    if stdout:
  
      if not cwd:
        # pivot, temporarily, to home directory
        curdir = os.path.realpath(os.curdir)
        os.chdir(os.environ['HOME'])
  
      if not os.path.exists(stdout): makedirs_safe(stdout)
  
      if not cwd:
        # go back
        os.chdir(os.path.realpath(curdir))
  
      scmd += ['-o', stdout]
  
    if stderr:
      if not os.path.exists(stderr): makedirs_safe(stderr)
      scmd += ['-e', stderr]
    elif stdout: #just re-use the stdout settings
      scmd += ['-e', stdout]
  
    scmd += ['-terse'] # simplified job identifiers returned by the command line
  
    for k in env: scmd += ['-v', k]
  
    if array is not None:
      scmd.append('-t')
      if isinstance(array, six.string_types):
        try:
          i = int(array)
          scmd.append('1-%d:1' % i)
        except ValueError:
          #must be complete...
          scmd.append('%s' % array)
      if isinstance(array, six.integer_types):
        scmd.append('1-%d:1' % array)
      if isinstance(array, (tuple, list)):
        if len(array) < 1 or len(array) > 3:
          raise RuntimeError("Array tuple should have length between 1 and 3")
        elif len(array) == 1:
          scmd.append('%s' % array[0])
        elif len(array) == 2:
          scmd.append('%s-%s' % (array[0], array[1]))
        elif len(array) == 3:
          scmd.append('%s-%s:%s' % (array[0], array[1], array[2]))
  
    if not isinstance(command, (list, tuple)): command = [command]
    scmd += command
  
    logger.debug("Qsub command '%s'", ' '.join(scmd))
  
    from .setshell import sexec
    jobid = str_(sexec(context, scmd))
    return int(jobid.split('.',1)[0])