prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Initial calculation of unicode table is not atomic
  If you're using e.g. pytest-xdist this can result in a corrupt unicode table, which is bad. Instead the file should be created under a temporary name then moved atomically.
  
  
  </issue>
  <code>
  [start of src/hypothesis/internal/charmap.py]
  1 # coding=utf-8
  2 #
  3 # This file is part of Hypothesis (https://github.com/DRMacIver/hypothesis)
  4 #
  5 # Most of this work is copyright (C) 2013-2015 David R. MacIver
  6 # (david@drmaciver.com), but it contains contributions by others. See
  7 # https://github.com/DRMacIver/hypothesis/blob/master/CONTRIBUTING.rst for a
  8 # full list of people who may hold copyright, and consult the git log if you
  9 # need to determine who owns an individual contribution.
  10 #
  11 # This Source Code Form is subject to the terms of the Mozilla Public License,
  12 # v. 2.0. If a copy of the MPL was not distributed with this file, You can
  13 # obtain one at http://mozilla.org/MPL/2.0/.
  14 #
  15 # END HEADER
  16 
  17 from __future__ import division, print_function, absolute_import
  18 
  19 import os
  20 import sys
  21 import gzip
  22 import pickle
  23 import unicodedata
  24 
  25 from hypothesis.configuration import storage_directory
  26 from hypothesis.internal.compat import hunichr
  27 
  28 
  29 def charmap_file():
  30     return os.path.join(
  31         storage_directory('unicodedata', unicodedata.unidata_version),
  32         'charmap.pickle.gz'
  33     )
  34 
  35 _charmap = None
  36 
  37 
  38 def charmap():
  39     global _charmap
  40     if _charmap is None:
  41         f = charmap_file()
  42         if not os.path.exists(f):
  43             _charmap = {}
  44             for i in range(0, sys.maxunicode + 1):
  45                 cat = unicodedata.category(hunichr(i))
  46                 rs = _charmap.setdefault(cat, [])
  47                 if rs and rs[-1][-1] == i - 1:
  48                     rs[-1][-1] += 1
  49                 else:
  50                     rs.append([i, i])
  51             # We explicitly set the mtime to an arbitary value so as to get
  52             # a stable format for our charmap.
  53             data = sorted(
  54                 (k, tuple((map(tuple, v))))
  55                 for k, v in _charmap.items())
  56             with gzip.GzipFile(f, 'wb', mtime=1) as o:
  57                 o.write(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))
  58         with gzip.open(f, 'rb') as i:
  59             _charmap = dict(pickle.loads(i.read()))
  60     assert _charmap is not None
  61     return _charmap
  62 
  63 
  64 _categories = None
  65 
  66 
  67 def categories():
  68     global _categories
  69     if _categories is None:
  70         cm = charmap()
  71         _categories = sorted(
  72             cm.keys(), key=lambda c: len(cm[c])
  73         )
  74         _categories.remove('Cc')
  75         _categories.remove('Cs')
  76         _categories.append('Cc')
  77         _categories.append('Cs')
  78     return _categories
  79 
  80 
  81 def _union_interval_lists(x, y):
  82     if not x:
  83         return y
  84     if not y:
  85         return x
  86     intervals = sorted(x + y, reverse=True)
  87     result = [intervals.pop()]
  88     while intervals:
  89         u, v = intervals.pop()
  90         a, b = result[-1]
  91         if u <= b + 1:
  92             result[-1] = (a, v)
  93         else:
  94             result.append((u, v))
  95     return tuple(result)
  96 
  97 
  98 category_index_cache = {
  99     (): (),
  100 }
  101 
  102 
  103 def _category_key(exclude, include):
  104     cs = categories()
  105     if include is None:
  106         include = set(cs)
  107     else:
  108         include = set(include)
  109     exclude = set(exclude or ())
  110     include -= exclude
  111     result = tuple(c for c in cs if c in include)
  112     return result
  113 
  114 
  115 def _query_for_key(key):
  116     try:
  117         return category_index_cache[key]
  118     except KeyError:
  119         pass
  120     assert key
  121     cs = categories()
  122     if len(key) == len(cs):
  123         result = ((0, sys.maxunicode),)
  124     else:
  125         result = _union_interval_lists(
  126             _query_for_key(key[:-1]), charmap()[key[-1]]
  127         )
  128     category_index_cache[key] = result
  129     return result
  130 
  131 
  132 limited_category_index_cache = {}
  133 
  134 
  135 def query(
  136     exclude_categories=(), include_categories=None,
  137     min_codepoint=None, max_codepoint=None
  138 ):
  139     if min_codepoint is None:
  140         min_codepoint = 0
  141     if max_codepoint is None:
  142         max_codepoint = sys.maxunicode
  143     catkey = _category_key(exclude_categories, include_categories)
  144     qkey = (catkey, min_codepoint, max_codepoint)
  145     try:
  146         return limited_category_index_cache[qkey]
  147     except KeyError:
  148         pass
  149     base = _query_for_key(catkey)
  150     result = []
  151     for u, v in base:
  152         if v >= min_codepoint and u <= max_codepoint:
  153             result.append((
  154                 max(u, min_codepoint), min(v, max_codepoint)
  155             ))
  156     result = tuple(result)
  157     limited_category_index_cache[qkey] = result
  158     return result
  159
  [end of src/hypothesis/internal/charmap.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/src/hypothesis/internal/charmap.py b/src/hypothesis/internal/charmap.py
  --- a/src/hypothesis/internal/charmap.py
  +++ b/src/hypothesis/internal/charmap.py
  @@ -20,6 +20,8 @@
   import sys
   import gzip
   import pickle
  +import shutil
  +import tempfile
   import unicodedata
   
   from hypothesis.configuration import storage_directory
  @@ -53,8 +55,13 @@
               data = sorted(
                   (k, tuple((map(tuple, v))))
                   for k, v in _charmap.items())
  -            with gzip.GzipFile(f, 'wb', mtime=1) as o:
  +
  +            # Write the Unicode table atomically
  +            _, tmpfile = tempfile.mkstemp()
  +            with gzip.GzipFile(tmpfile, 'wb', mtime=1) as o:
                   o.write(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))
  +            shutil.move(tmpfile, f)
  +
           with gzip.open(f, 'rb') as i:
               _charmap = dict(pickle.loads(i.read()))
       assert _charmap is not None
  
verification_info: {"golden_diff": "diff --git a/src/hypothesis/internal/charmap.py b/src/hypothesis/internal/charmap.py\n--- a/src/hypothesis/internal/charmap.py\n+++ b/src/hypothesis/internal/charmap.py\n@@ -20,6 +20,8 @@\n import sys\n import gzip\n import pickle\n+import shutil\n+import tempfile\n import unicodedata\n \n from hypothesis.configuration import storage_directory\n@@ -53,8 +55,13 @@\n             data = sorted(\n                 (k, tuple((map(tuple, v))))\n                 for k, v in _charmap.items())\n-            with gzip.GzipFile(f, 'wb', mtime=1) as o:\n+\n+            # Write the Unicode table atomically\n+            _, tmpfile = tempfile.mkstemp()\n+            with gzip.GzipFile(tmpfile, 'wb', mtime=1) as o:\n                 o.write(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))\n+            shutil.move(tmpfile, f)\n+\n         with gzip.open(f, 'rb') as i:\n             _charmap = dict(pickle.loads(i.read()))\n     assert _charmap is not None\n", "issue": "Initial calculation of unicode table is not atomic\nIf you're using e.g. pytest-xdist this can result in a corrupt unicode table, which is bad. Instead the file should be created under a temporary name then moved atomically.\n\n", "before_files": [{"content": "# coding=utf-8\n#\n# This file is part of Hypothesis (https://github.com/DRMacIver/hypothesis)\n#\n# Most of this work is copyright (C) 2013-2015 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# https://github.com/DRMacIver/hypothesis/blob/master/CONTRIBUTING.rst for a\n# full list of people who may hold copyright, and consult the git log if you\n# need to determine who owns an individual contribution.\n#\n# This Source Code Form is subject to the terms of the Mozilla Public License,\n# v. 2.0. If a copy of the MPL was not distributed with this file, You can\n# obtain one at http://mozilla.org/MPL/2.0/.\n#\n# END HEADER\n\nfrom __future__ import division, print_function, absolute_import\n\nimport os\nimport sys\nimport gzip\nimport pickle\nimport unicodedata\n\nfrom hypothesis.configuration import storage_directory\nfrom hypothesis.internal.compat import hunichr\n\n\ndef charmap_file():\n    return os.path.join(\n        storage_directory('unicodedata', unicodedata.unidata_version),\n        'charmap.pickle.gz'\n    )\n\n_charmap = None\n\n\ndef charmap():\n    global _charmap\n    if _charmap is None:\n        f = charmap_file()\n        if not os.path.exists(f):\n            _charmap = {}\n            for i in range(0, sys.maxunicode + 1):\n                cat = unicodedata.category(hunichr(i))\n                rs = _charmap.setdefault(cat, [])\n                if rs and rs[-1][-1] == i - 1:\n                    rs[-1][-1] += 1\n                else:\n                    rs.append([i, i])\n            # We explicitly set the mtime to an arbitary value so as to get\n            # a stable format for our charmap.\n            data = sorted(\n                (k, tuple((map(tuple, v))))\n                for k, v in _charmap.items())\n            with gzip.GzipFile(f, 'wb', mtime=1) as o:\n                o.write(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))\n        with gzip.open(f, 'rb') as i:\n            _charmap = dict(pickle.loads(i.read()))\n    assert _charmap is not None\n    return _charmap\n\n\n_categories = None\n\n\ndef categories():\n    global _categories\n    if _categories is None:\n        cm = charmap()\n        _categories = sorted(\n            cm.keys(), key=lambda c: len(cm[c])\n        )\n        _categories.remove('Cc')\n        _categories.remove('Cs')\n        _categories.append('Cc')\n        _categories.append('Cs')\n    return _categories\n\n\ndef _union_interval_lists(x, y):\n    if not x:\n        return y\n    if not y:\n        return x\n    intervals = sorted(x + y, reverse=True)\n    result = [intervals.pop()]\n    while intervals:\n        u, v = intervals.pop()\n        a, b = result[-1]\n        if u <= b + 1:\n            result[-1] = (a, v)\n        else:\n            result.append((u, v))\n    return tuple(result)\n\n\ncategory_index_cache = {\n    (): (),\n}\n\n\ndef _category_key(exclude, include):\n    cs = categories()\n    if include is None:\n        include = set(cs)\n    else:\n        include = set(include)\n    exclude = set(exclude or ())\n    include -= exclude\n    result = tuple(c for c in cs if c in include)\n    return result\n\n\ndef _query_for_key(key):\n    try:\n        return category_index_cache[key]\n    except KeyError:\n        pass\n    assert key\n    cs = categories()\n    if len(key) == len(cs):\n        result = ((0, sys.maxunicode),)\n    else:\n        result = _union_interval_lists(\n            _query_for_key(key[:-1]), charmap()[key[-1]]\n        )\n    category_index_cache[key] = result\n    return result\n\n\nlimited_category_index_cache = {}\n\n\ndef query(\n    exclude_categories=(), include_categories=None,\n    min_codepoint=None, max_codepoint=None\n):\n    if min_codepoint is None:\n        min_codepoint = 0\n    if max_codepoint is None:\n        max_codepoint = sys.maxunicode\n    catkey = _category_key(exclude_categories, include_categories)\n    qkey = (catkey, min_codepoint, max_codepoint)\n    try:\n        return limited_category_index_cache[qkey]\n    except KeyError:\n        pass\n    base = _query_for_key(catkey)\n    result = []\n    for u, v in base:\n        if v >= min_codepoint and u <= max_codepoint:\n            result.append((\n                max(u, min_codepoint), min(v, max_codepoint)\n            ))\n    result = tuple(result)\n    limited_category_index_cache[qkey] = result\n    return result\n", "path": "src/hypothesis/internal/charmap.py"}]}