Repository: dask/dask-jobqueue

Issue:
The function slurm_format_bytes_ceil raises Typerror if input is less than 1024

The function "[slurm_format_bytes_ceil](https://github.com/dask/dask-jobqueue/blob/b07308ea36da20ba4a109e53799e1dc26b613762/dask_jobqueue/slurm.py#L115)" parses number of bytes to a string compatible with Slurm.

If input number of bytes "n" is less than 1024, then it reaches the end of the conditional statements, which is missing a formatter string "%s". Thus, it throws an exception:
```
TypeError: not all arguments converted during string formatting
```
This also goes against its intended behavior, as shown in the examples in the docstring.

A simple fix is to replace `return "1K" % n` with `return "%sK" % n`

Hints from issue comments:

Solution patch:
diff --git a/dask_jobqueue/slurm.py b/dask_jobqueue/slurm.py
index dba37867..7af75b1a 100644
--- a/dask_jobqueue/slurm.py
+++ b/dask_jobqueue/slurm.py
@@ -116,13 +116,26 @@ def slurm_format_bytes_ceil(n):
     """Format bytes as text.
 
     SLURM expects KiB, MiB or Gib, but names it KB, MB, GB. SLURM does not handle Bytes, only starts at KB.
+    Thus minimum returned value is '1K'.
 
-    >>> slurm_format_bytes_ceil(1)
+    Parameters
+    ----------
+    n: int
+        Number of bytes
+
+    Returns
+    -------
+    str
+        Bytes formatted as string
+
+    Examples
+    --------
+    >>> slurm_format_bytes_ceil(12)
     '1K'
     >>> slurm_format_bytes_ceil(1234)
     '2K'
     >>> slurm_format_bytes_ceil(12345678)
-    '13M'
+    '12M'
     >>> slurm_format_bytes_ceil(1234567890)
     '2G'
     >>> slurm_format_bytes_ceil(15000000000)
@@ -134,7 +147,7 @@ def slurm_format_bytes_ceil(n):
         return "%dM" % math.ceil(n / (1024**2))
     if n >= 1024:
         return "%dK" % math.ceil(n / 1024)
-    return "1K" % n
+    return "1K"
 
 
 class SLURMCluster(JobQueueCluster):
diff --git a/docs/source/clusters-configuration-setup.rst b/docs/source/clusters-configuration-setup.rst
index b89c6e74..5ab24221 100644
--- a/docs/source/clusters-configuration-setup.rst
+++ b/docs/source/clusters-configuration-setup.rst
@@ -69,10 +69,13 @@ This can be avoided by always using 'GiB' in dask-jobqueue configuration.
 Processes
 ---------
 
-By default Dask will run one Python process per job.  However, you can
-optionally choose to cut up that job into multiple processes using the
-``processes`` configuration value.  This can be advantageous if your
-computations are bound by the GIL, but disadvantageous if you plan to
+By default Dask will try to cut up a job into multiple processes based on
+the value of ``cores``. If ``cores`` is less than or equal to 4, then the number
+of processes will be equal to the ``cores`` value. Else, the number of processes
+will be at least ``sqrt(cores)``, and rest will be threads, so that
+``cores = processes * threads`` is maintained. You can control the number of
+processes using the ``processes`` configuration value. Using processes can be advantageous
+if your computations are bound by the GIL, but disadvantageous if you plan to
 communicate a lot between processes.  Typically we find that for pure Numpy
 workloads a low number of processes (like one) is best, while for pure Python
 workloads a high number of processes (like one process per two cores) is best.


Test patch:
diff --git a/dask_jobqueue/tests/test_slurm.py b/dask_jobqueue/tests/test_slurm.py
index 5b4b166b..c06ece50 100644
--- a/dask_jobqueue/tests/test_slurm.py
+++ b/dask_jobqueue/tests/test_slurm.py
@@ -10,6 +10,7 @@
 from dask.utils import format_bytes, parse_bytes
 
 from dask_jobqueue import SLURMCluster
+from dask_jobqueue.slurm import slurm_format_bytes_ceil
 
 from . import QUEUE_WAIT
 
@@ -343,3 +344,18 @@ def test_slurm_runner():
     )
     output = output.decode()
     assert "Test passed" in output
+
+
+@pytest.mark.parametrize(
+    ["input", "expected_output"],
+    [
+        (12, "1K"),
+        (1234, "2K"),
+        (12345678, "12M"),
+        (15000000000, "14G"),
+    ],
+)
+def test_slurm_format_bytes_ceil(input, expected_output):
+    actual_output = slurm_format_bytes_ceil(input)
+
+    assert actual_output == expected_output