prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Improve platform command line length limits
  I have a question related to #510 
  
  When I run `pre-commit run` all hooks work as expected (processing just changed files), but `pre-commit run -a` divides a list of files into 5 bulks and run the same hook in parallel executions. This creates unnecessary checks of files which belong to the same directory.
  
  Is it possible to control that certain hooks should not be executed in parallel but rather pass all filenames to it?
  
  Alternatively, is there an environment variable or something like that which says that pre-commit is running a hook for all files?
  
  </issue>
  <code>
  [start of pre_commit/xargs.py]
  1 from __future__ import absolute_import
  2 from __future__ import division
  3 from __future__ import unicode_literals
  4 
  5 import concurrent.futures
  6 import contextlib
  7 import math
  8 import sys
  9 
  10 import six
  11 
  12 from pre_commit import parse_shebang
  13 from pre_commit.util import cmd_output
  14 
  15 
  16 # TODO: properly compute max_length value
  17 def _get_platform_max_length():
  18     # posix minimum
  19     return 4 * 1024
  20 
  21 
  22 def _command_length(*cmd):
  23     full_cmd = ' '.join(cmd)
  24 
  25     # win32 uses the amount of characters, more details at:
  26     # https://github.com/pre-commit/pre-commit/pull/839
  27     if sys.platform == 'win32':
  28         # the python2.x apis require bytes, we encode as UTF-8
  29         if six.PY2:
  30             return len(full_cmd.encode('utf-8'))
  31         else:
  32             return len(full_cmd.encode('utf-16le')) // 2
  33     else:
  34         return len(full_cmd.encode(sys.getfilesystemencoding()))
  35 
  36 
  37 class ArgumentTooLongError(RuntimeError):
  38     pass
  39 
  40 
  41 def partition(cmd, varargs, target_concurrency, _max_length=None):
  42     _max_length = _max_length or _get_platform_max_length()
  43 
  44     # Generally, we try to partition evenly into at least `target_concurrency`
  45     # partitions, but we don't want a bunch of tiny partitions.
  46     max_args = max(4, math.ceil(len(varargs) / target_concurrency))
  47 
  48     cmd = tuple(cmd)
  49     ret = []
  50 
  51     ret_cmd = []
  52     # Reversed so arguments are in order
  53     varargs = list(reversed(varargs))
  54 
  55     total_length = _command_length(*cmd)
  56     while varargs:
  57         arg = varargs.pop()
  58 
  59         arg_length = _command_length(arg) + 1
  60         if (
  61                 total_length + arg_length <= _max_length and
  62                 len(ret_cmd) < max_args
  63         ):
  64             ret_cmd.append(arg)
  65             total_length += arg_length
  66         elif not ret_cmd:
  67             raise ArgumentTooLongError(arg)
  68         else:
  69             # We've exceeded the length, yield a command
  70             ret.append(cmd + tuple(ret_cmd))
  71             ret_cmd = []
  72             total_length = _command_length(*cmd)
  73             varargs.append(arg)
  74 
  75     ret.append(cmd + tuple(ret_cmd))
  76 
  77     return tuple(ret)
  78 
  79 
  80 @contextlib.contextmanager
  81 def _thread_mapper(maxsize):
  82     if maxsize == 1:
  83         yield map
  84     else:
  85         with concurrent.futures.ThreadPoolExecutor(maxsize) as ex:
  86             yield ex.map
  87 
  88 
  89 def xargs(cmd, varargs, **kwargs):
  90     """A simplified implementation of xargs.
  91 
  92     negate: Make nonzero successful and zero a failure
  93     target_concurrency: Target number of partitions to run concurrently
  94     """
  95     negate = kwargs.pop('negate', False)
  96     target_concurrency = kwargs.pop('target_concurrency', 1)
  97     retcode = 0
  98     stdout = b''
  99     stderr = b''
  100 
  101     try:
  102         parse_shebang.normexe(cmd[0])
  103     except parse_shebang.ExecutableNotFoundError as e:
  104         return e.to_output()
  105 
  106     partitions = partition(cmd, varargs, target_concurrency, **kwargs)
  107 
  108     def run_cmd_partition(run_cmd):
  109         return cmd_output(*run_cmd, encoding=None, retcode=None)
  110 
  111     threads = min(len(partitions), target_concurrency)
  112     with _thread_mapper(threads) as thread_map:
  113         results = thread_map(run_cmd_partition, partitions)
  114 
  115         for proc_retcode, proc_out, proc_err in results:
  116             # This is *slightly* too clever so I'll explain it.
  117             # First the xor boolean table:
  118             #     T | F |
  119             #   +-------+
  120             # T | F | T |
  121             # --+-------+
  122             # F | T | F |
  123             # --+-------+
  124             # When negate is True, it has the effect of flipping the return
  125             # code. Otherwise, the returncode is unchanged.
  126             retcode |= bool(proc_retcode) ^ negate
  127             stdout += proc_out
  128             stderr += proc_err
  129 
  130     return retcode, stdout, stderr
  131
  [end of pre_commit/xargs.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py
  --- a/pre_commit/xargs.py
  +++ b/pre_commit/xargs.py
  @@ -5,6 +5,7 @@
   import concurrent.futures
   import contextlib
   import math
  +import os
   import sys
   
   import six
  @@ -13,10 +14,24 @@
   from pre_commit.util import cmd_output
   
   
  -# TODO: properly compute max_length value
  -def _get_platform_max_length():
  -    # posix minimum
  -    return 4 * 1024
  +def _environ_size(_env=None):
  +    environ = _env if _env is not None else getattr(os, 'environb', os.environ)
  +    size = 8 * len(environ)  # number of pointers in `envp`
  +    for k, v in environ.items():
  +        size += len(k) + len(v) + 2  # c strings in `envp`
  +    return size
  +
  +
  +def _get_platform_max_length():  # pragma: no cover (platform specific)
  +    if os.name == 'posix':
  +        maximum = os.sysconf(str('SC_ARG_MAX')) - 2048 - _environ_size()
  +        maximum = min(maximum, 2 ** 17)
  +        return maximum
  +    elif os.name == 'nt':
  +        return 2 ** 15 - 2048  # UNICODE_STRING max - headroom
  +    else:
  +        # posix minimum
  +        return 2 ** 12
   
   
   def _command_length(*cmd):
  @@ -52,7 +67,7 @@
       # Reversed so arguments are in order
       varargs = list(reversed(varargs))
   
  -    total_length = _command_length(*cmd)
  +    total_length = _command_length(*cmd) + 1
       while varargs:
           arg = varargs.pop()
   
  @@ -69,7 +84,7 @@
               # We've exceeded the length, yield a command
               ret.append(cmd + tuple(ret_cmd))
               ret_cmd = []
  -            total_length = _command_length(*cmd)
  +            total_length = _command_length(*cmd) + 1
               varargs.append(arg)
   
       ret.append(cmd + tuple(ret_cmd))
  @@ -99,7 +114,7 @@
       stderr = b''
   
       try:
  -        parse_shebang.normexe(cmd[0])
  +        cmd = parse_shebang.normalize_cmd(cmd)
       except parse_shebang.ExecutableNotFoundError as e:
           return e.to_output()
  
verification_info: {"golden_diff": "diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py\n--- a/pre_commit/xargs.py\n+++ b/pre_commit/xargs.py\n@@ -5,6 +5,7 @@\n import concurrent.futures\n import contextlib\n import math\n+import os\n import sys\n \n import six\n@@ -13,10 +14,24 @@\n from pre_commit.util import cmd_output\n \n \n-# TODO: properly compute max_length value\n-def _get_platform_max_length():\n-    # posix minimum\n-    return 4 * 1024\n+def _environ_size(_env=None):\n+    environ = _env if _env is not None else getattr(os, 'environb', os.environ)\n+    size = 8 * len(environ)  # number of pointers in `envp`\n+    for k, v in environ.items():\n+        size += len(k) + len(v) + 2  # c strings in `envp`\n+    return size\n+\n+\n+def _get_platform_max_length():  # pragma: no cover (platform specific)\n+    if os.name == 'posix':\n+        maximum = os.sysconf(str('SC_ARG_MAX')) - 2048 - _environ_size()\n+        maximum = min(maximum, 2 ** 17)\n+        return maximum\n+    elif os.name == 'nt':\n+        return 2 ** 15 - 2048  # UNICODE_STRING max - headroom\n+    else:\n+        # posix minimum\n+        return 2 ** 12\n \n \n def _command_length(*cmd):\n@@ -52,7 +67,7 @@\n     # Reversed so arguments are in order\n     varargs = list(reversed(varargs))\n \n-    total_length = _command_length(*cmd)\n+    total_length = _command_length(*cmd) + 1\n     while varargs:\n         arg = varargs.pop()\n \n@@ -69,7 +84,7 @@\n             # We've exceeded the length, yield a command\n             ret.append(cmd + tuple(ret_cmd))\n             ret_cmd = []\n-            total_length = _command_length(*cmd)\n+            total_length = _command_length(*cmd) + 1\n             varargs.append(arg)\n \n     ret.append(cmd + tuple(ret_cmd))\n@@ -99,7 +114,7 @@\n     stderr = b''\n \n     try:\n-        parse_shebang.normexe(cmd[0])\n+        cmd = parse_shebang.normalize_cmd(cmd)\n     except parse_shebang.ExecutableNotFoundError as e:\n         return e.to_output()\n", "issue": "Improve platform command line length limits\nI have a question related to #510 \r\n\r\nWhen I run `pre-commit run` all hooks work as expected (processing just changed files), but `pre-commit run -a` divides a list of files into 5 bulks and run the same hook in parallel executions. This creates unnecessary checks of files which belong to the same directory.\r\n\r\nIs it possible to control that certain hooks should not be executed in parallel but rather pass all filenames to it?\r\n\r\nAlternatively, is there an environment variable or something like that which says that pre-commit is running a hook for all files?\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import unicode_literals\n\nimport concurrent.futures\nimport contextlib\nimport math\nimport sys\n\nimport six\n\nfrom pre_commit import parse_shebang\nfrom pre_commit.util import cmd_output\n\n\n# TODO: properly compute max_length value\ndef _get_platform_max_length():\n    # posix minimum\n    return 4 * 1024\n\n\ndef _command_length(*cmd):\n    full_cmd = ' '.join(cmd)\n\n    # win32 uses the amount of characters, more details at:\n    # https://github.com/pre-commit/pre-commit/pull/839\n    if sys.platform == 'win32':\n        # the python2.x apis require bytes, we encode as UTF-8\n        if six.PY2:\n            return len(full_cmd.encode('utf-8'))\n        else:\n            return len(full_cmd.encode('utf-16le')) // 2\n    else:\n        return len(full_cmd.encode(sys.getfilesystemencoding()))\n\n\nclass ArgumentTooLongError(RuntimeError):\n    pass\n\n\ndef partition(cmd, varargs, target_concurrency, _max_length=None):\n    _max_length = _max_length or _get_platform_max_length()\n\n    # Generally, we try to partition evenly into at least `target_concurrency`\n    # partitions, but we don't want a bunch of tiny partitions.\n    max_args = max(4, math.ceil(len(varargs) / target_concurrency))\n\n    cmd = tuple(cmd)\n    ret = []\n\n    ret_cmd = []\n    # Reversed so arguments are in order\n    varargs = list(reversed(varargs))\n\n    total_length = _command_length(*cmd)\n    while varargs:\n        arg = varargs.pop()\n\n        arg_length = _command_length(arg) + 1\n        if (\n                total_length + arg_length <= _max_length and\n                len(ret_cmd) < max_args\n        ):\n            ret_cmd.append(arg)\n            total_length += arg_length\n        elif not ret_cmd:\n            raise ArgumentTooLongError(arg)\n        else:\n            # We've exceeded the length, yield a command\n            ret.append(cmd + tuple(ret_cmd))\n            ret_cmd = []\n            total_length = _command_length(*cmd)\n            varargs.append(arg)\n\n    ret.append(cmd + tuple(ret_cmd))\n\n    return tuple(ret)\n\n\n@contextlib.contextmanager\ndef _thread_mapper(maxsize):\n    if maxsize == 1:\n        yield map\n    else:\n        with concurrent.futures.ThreadPoolExecutor(maxsize) as ex:\n            yield ex.map\n\n\ndef xargs(cmd, varargs, **kwargs):\n    \"\"\"A simplified implementation of xargs.\n\n    negate: Make nonzero successful and zero a failure\n    target_concurrency: Target number of partitions to run concurrently\n    \"\"\"\n    negate = kwargs.pop('negate', False)\n    target_concurrency = kwargs.pop('target_concurrency', 1)\n    retcode = 0\n    stdout = b''\n    stderr = b''\n\n    try:\n        parse_shebang.normexe(cmd[0])\n    except parse_shebang.ExecutableNotFoundError as e:\n        return e.to_output()\n\n    partitions = partition(cmd, varargs, target_concurrency, **kwargs)\n\n    def run_cmd_partition(run_cmd):\n        return cmd_output(*run_cmd, encoding=None, retcode=None)\n\n    threads = min(len(partitions), target_concurrency)\n    with _thread_mapper(threads) as thread_map:\n        results = thread_map(run_cmd_partition, partitions)\n\n        for proc_retcode, proc_out, proc_err in results:\n            # This is *slightly* too clever so I'll explain it.\n            # First the xor boolean table:\n            #     T | F |\n            #   +-------+\n            # T | F | T |\n            # --+-------+\n            # F | T | F |\n            # --+-------+\n            # When negate is True, it has the effect of flipping the return\n            # code. Otherwise, the returncode is unchanged.\n            retcode |= bool(proc_retcode) ^ negate\n            stdout += proc_out\n            stderr += proc_err\n\n    return retcode, stdout, stderr\n", "path": "pre_commit/xargs.py"}]}