We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
RELEASE.rst is not checked for trailing whitespace
Which means that when such a file is merged (e.g. from #1515), subsequent builds break.  This is bad and should be fixed ASAP.  See #1525 for temporary workaround.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tooling/src/hypothesistooling/releasemanagement.py`
Content:
```
1 # coding=utf-8
2 #
3 # This file is part of Hypothesis, which may be found at
4 # https://github.com/HypothesisWorks/hypothesis-python
5 #
6 # Most of this work is copyright (C) 2013-2018 David R. MacIver
7 # (david@drmaciver.com), but it contains contributions by others. See
8 # CONTRIBUTING.rst for a full list of people who may hold copyright, and
9 # consult the git log if you need to determine who owns an individual
10 # contribution.
11 #
12 # This Source Code Form is subject to the terms of the Mozilla Public License,
13 # v. 2.0. If a copy of the MPL was not distributed with this file, You can
14 # obtain one at http://mozilla.org/MPL/2.0/.
15 #
16 # END HEADER
17 
18 """Helpful common code for release management tasks that is shared across
19 multiple projects.
20 
21 Note that most code in here is brittle and specific to our build and
22 probably makes all sorts of undocumented assumptions, even as it looks
23 like a nice tidy reusable set of functionality.
24 """
25 
26 
27 from __future__ import division, print_function, absolute_import
28 
29 import re
30 from datetime import datetime, timedelta
31 
32 import hypothesistooling as tools
33 
34 
35 def release_date_string():
36     """Returns a date string that represents what should be considered "today"
37     for the purposes of releasing. It is always measured in UTC, but if it's in
38     the last hour of the day it will actually be considered tomorrow.
39 
40     The reason for counting it as the later day is that it ensures that
41     (unless our release process takes more than 23 hours) this value
42     remains consistent throughout the entire release.
43     """
44     now = datetime.utcnow()
45 
46     return max([
47         d.strftime('%Y-%m-%d') for d in (now, now + timedelta(hours=1))
48     ])
49 
50 
51 def assignment_matcher(name):
52     """
53     Matches a single line of the form (some space)name = (some value). e.g.
54     "  foo = 1".
55     The whole line up to the assigned value is the first matching group,
56     the rest of the line is the second matching group.
57     i.e. group 1 is the assignment, group 2 is the value. In the above
58     example group 1 would be "  foo = " and group 2 would be "1"
59     """
60     return re.compile(r'\A(\s*%s\s*=\s*)(.+)\Z' % (re.escape(name),))
61 
62 
63 def extract_assignment_from_string(contents, name):
64     lines = contents.split('\n')
65 
66     matcher = assignment_matcher(name)
67 
68     for i, l in enumerate(lines):
69         match = matcher.match(l)
70         if match is not None:
71             return match[2].strip()
72 
73     raise ValueError('Key %s not found in %s' % (
74         name, contents
75     ))
76 
77 
78 def extract_assignment(filename, name):
79     with open(filename) as i:
80         return extract_assignment_from_string(i.read(), name)
81 
82 
83 def replace_assignment_in_string(contents, name, value):
84     lines = contents.split('\n')
85 
86     matcher = assignment_matcher(name)
87 
88     count = 0
89 
90     for i, l in enumerate(lines):
91         match = matcher.match(l)
92         if match is not None:
93             count += 1
94             lines[i] = match[1] + value
95 
96     if count == 0:
97         raise ValueError('Key %s not found in %s' % (
98             name, contents
99         ))
100     if count > 1:
101         raise ValueError('Key %s found %d times in %s' % (
102             name, count, contents
103         ))
104 
105     return '\n'.join(lines)
106 
107 
108 def replace_assignment(filename, name, value):
109     """Replaces a single assignment of the form key = value in a file with a
110     new value, attempting to preserve the existing format.
111 
112     This is fairly fragile - in particular it knows nothing about
113     the file format. The existing value is simply the rest of the line after
114     the last space after the equals.
115     """
116     with open(filename) as i:
117         contents = i.read()
118     result = replace_assignment_in_string(contents, name, value)
119     with open(filename, 'w') as o:
120         o.write(result)
121 
122 
123 RELEASE_TYPE = re.compile(r"^RELEASE_TYPE: +(major|minor|patch)")
124 
125 
126 MAJOR = 'major'
127 MINOR = 'minor'
128 PATCH = 'patch'
129 
130 
131 VALID_RELEASE_TYPES = (MAJOR, MINOR, PATCH)
132 
133 
134 def parse_release_file(filename):
135     with open(filename) as i:
136         return parse_release_file_contents(i.read(), filename)
137 
138 
139 def parse_release_file_contents(release_contents, filename):
140     release_lines = release_contents.split('\n')
141 
142     m = RELEASE_TYPE.match(release_lines[0])
143     if m is not None:
144         release_type = m.group(1)
145         if release_type not in VALID_RELEASE_TYPES:
146             raise ValueError('Unrecognised release type %r' % (release_type,))
147         del release_lines[0]
148         release_contents = '\n'.join(release_lines).strip()
149     else:
150         raise ValueError(
151             '%s does not start by specifying release type. The first '
152             'line of the file should be RELEASE_TYPE: followed by one of '
153             'major, minor, or patch, to specify the type of release that '
154             'this is (i.e. which version number to increment). Instead the '
155             'first line was %r' % (filename, release_lines[0],)
156         )
157 
158     return release_type, release_contents
159 
160 
161 def bump_version_info(version_info, release_type):
162     new_version = list(version_info)
163     bump = VALID_RELEASE_TYPES.index(release_type)
164     new_version[bump] += 1
165     for i in range(bump + 1, len(new_version)):
166         new_version[i] = 0
167     new_version = tuple(new_version)
168     new_version_string = '.'.join(map(str, new_version))
169     return new_version_string, new_version
170 
171 
172 def update_markdown_changelog(changelog, name, version, entry):
173     with open(changelog) as i:
174         prev_contents = i.read()
175 
176     title = '# %(name)s %(version)s (%(date)s)\n\n' % {
177         'name': name, 'version': version, 'date': release_date_string(),
178     }
179 
180     with open(changelog, 'w') as o:
181         o.write(title)
182         o.write(entry.strip())
183         o.write('\n\n')
184         o.write(prev_contents)
185 
186 
187 def parse_version(version):
188     return tuple(map(int, version.split('.')))
189 
190 
191 def commit_pending_release(project):
192     """Create a commit with the new release."""
193     tools.git('rm', project.RELEASE_FILE)
194     tools.git('add', '-u', project.BASE_DIR)
195 
196     tools.git(
197         'commit', '-m',
198         'Bump %s version to %s and update changelog'
199         '\n\n[skip ci]' % (project.PACKAGE_NAME, project.current_version(),)
200     )
201
```

Path: `tooling/src/hypothesistooling/projects/hypothesispython.py`
Content:
```
1 # coding=utf-8
2 #
3 # This file is part of Hypothesis, which may be found at
4 # https://github.com/HypothesisWorks/hypothesis-python
5 #
6 # Most of this work is copyright (C) 2013-2018 David R. MacIver
7 # (david@drmaciver.com), but it contains contributions by others. See
8 # CONTRIBUTING.rst for a full list of people who may hold copyright, and
9 # consult the git log if you need to determine who owns an individual
10 # contribution.
11 #
12 # This Source Code Form is subject to the terms of the Mozilla Public License,
13 # v. 2.0. If a copy of the MPL was not distributed with this file, You can
14 # obtain one at http://mozilla.org/MPL/2.0/.
15 #
16 # END HEADER
17 
18 from __future__ import division, print_function, absolute_import
19 
20 import os
21 import re
22 import sys
23 import shutil
24 import subprocess
25 
26 import hypothesistooling as tools
27 import hypothesistooling.releasemanagement as rm
28 from hypothesistooling.releasemanagement import bump_version_info, \
29     replace_assignment, release_date_string
30 
31 PACKAGE_NAME = 'hypothesis-python'
32 
33 HYPOTHESIS_PYTHON = os.path.join(tools.ROOT, PACKAGE_NAME)
34 PYTHON_TAG_PREFIX = 'hypothesis-python-'
35 
36 
37 BASE_DIR = HYPOTHESIS_PYTHON
38 
39 PYTHON_SRC = os.path.join(HYPOTHESIS_PYTHON, 'src')
40 PYTHON_TESTS = os.path.join(HYPOTHESIS_PYTHON, 'tests')
41 
42 RELEASE_FILE = os.path.join(HYPOTHESIS_PYTHON, 'RELEASE.rst')
43 
44 assert os.path.exists(PYTHON_SRC)
45 
46 
47 __version__ = None
48 __version_info__ = None
49 
50 VERSION_FILE = os.path.join(PYTHON_SRC, 'hypothesis/version.py')
51 
52 with open(VERSION_FILE) as o:
53     exec(o.read())
54 
55 assert __version__ is not None
56 assert __version_info__ is not None
57 
58 
59 def has_release():
60     return os.path.exists(RELEASE_FILE)
61 
62 
63 def parse_release_file():
64     return rm.parse_release_file(RELEASE_FILE)
65 
66 
67 def has_source_changes():
68     return tools.has_changes([PYTHON_SRC])
69 
70 
71 CHANGELOG_ANCHOR = re.compile(r"^\.\. _v\d+\.\d+\.\d+:$")
72 CHANGELOG_BORDER = re.compile(r"^-+$")
73 CHANGELOG_HEADER = re.compile(r"^\d+\.\d+\.\d+ - \d\d\d\d-\d\d-\d\d$")
74 
75 
76 def update_changelog_and_version():
77     global __version_info__
78     global __version__
79 
80     contents = changelog()
81     assert '\r' not in contents
82     lines = contents.split('\n')
83     for i, l in enumerate(lines):
84         if CHANGELOG_ANCHOR.match(l):
85             assert CHANGELOG_BORDER.match(lines[i + 2]), repr(lines[i + 2])
86             assert CHANGELOG_HEADER.match(lines[i + 3]), repr(lines[i + 3])
87             assert CHANGELOG_BORDER.match(lines[i + 4]), repr(lines[i + 4])
88             beginning = '\n'.join(lines[:i])
89             rest = '\n'.join(lines[i:])
90             assert '\n'.join((beginning, rest)) == contents
91             break
92 
93     release_type, release_contents = parse_release_file()
94 
95     new_version_string, new_version_info = bump_version_info(
96         __version_info__, release_type)
97 
98     __version_info__ = new_version_info
99     __version__ = new_version_string
100 
101     replace_assignment(
102         VERSION_FILE, '__version_info__', repr(new_version_info))
103 
104     heading_for_new_version = ' - '.join((
105         new_version_string, release_date_string()))
106     border_for_new_version = '-' * len(heading_for_new_version)
107 
108     new_changelog_parts = [
109         beginning.strip(),
110         '',
111         '.. _v%s:' % (new_version_string),
112         '',
113         border_for_new_version,
114         heading_for_new_version,
115         border_for_new_version,
116         '',
117         release_contents,
118         '',
119         rest
120     ]
121 
122     with open(CHANGELOG_FILE, 'w') as o:
123         o.write('\n'.join(new_changelog_parts))
124 
125 
126 CHANGELOG_FILE = os.path.join(HYPOTHESIS_PYTHON, 'docs', 'changes.rst')
127 DIST = os.path.join(HYPOTHESIS_PYTHON, 'dist')
128 
129 
130 def changelog():
131     with open(CHANGELOG_FILE) as i:
132         return i.read()
133 
134 
135 def build_distribution():
136     if os.path.exists(DIST):
137         shutil.rmtree(DIST)
138 
139     subprocess.check_output([
140         sys.executable, 'setup.py', 'sdist', '--dist-dir', DIST,
141     ])
142 
143 
144 def upload_distribution():
145     tools.assert_can_release()
146     subprocess.check_call([
147         sys.executable, '-m', 'twine', 'upload',
148         '--config-file', tools.PYPIRC,
149         os.path.join(DIST, '*'),
150     ])
151 
152 
153 def current_version():
154     return __version__
155 
156 
157 def latest_version():
158     versions = []
159 
160     for t in tools.tags():
161         if t.startswith(PYTHON_TAG_PREFIX):
162             t = t[len(PYTHON_TAG_PREFIX):]
163         else:
164             continue
165         assert t == t.strip()
166         parts = t.split('.')
167         assert len(parts) == 3
168         v = tuple(map(int, parts))
169         versions.append((v, t))
170 
171     _, latest = max(versions)
172 
173     return latest
174 
175 
176 def tag_name():
177     return PYTHON_TAG_PREFIX + __version__
178
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tooling/src/hypothesistooling/projects/hypothesispython.py b/tooling/src/hypothesistooling/projects/hypothesispython.py
--- a/tooling/src/hypothesistooling/projects/hypothesispython.py
+++ b/tooling/src/hypothesistooling/projects/hypothesispython.py
@@ -25,8 +25,6 @@
 
 import hypothesistooling as tools
 import hypothesistooling.releasemanagement as rm
-from hypothesistooling.releasemanagement import bump_version_info, \
-    replace_assignment, release_date_string
 
 PACKAGE_NAME = 'hypothesis-python'
 
@@ -92,17 +90,17 @@
 
     release_type, release_contents = parse_release_file()
 
-    new_version_string, new_version_info = bump_version_info(
+    new_version_string, new_version_info = rm.bump_version_info(
         __version_info__, release_type)
 
     __version_info__ = new_version_info
     __version__ = new_version_string
 
-    replace_assignment(
+    rm.replace_assignment(
         VERSION_FILE, '__version_info__', repr(new_version_info))
 
     heading_for_new_version = ' - '.join((
-        new_version_string, release_date_string()))
+        new_version_string, rm.release_date_string()))
     border_for_new_version = '-' * len(heading_for_new_version)
 
     new_changelog_parts = [
diff --git a/tooling/src/hypothesistooling/releasemanagement.py b/tooling/src/hypothesistooling/releasemanagement.py
--- a/tooling/src/hypothesistooling/releasemanagement.py
+++ b/tooling/src/hypothesistooling/releasemanagement.py
@@ -137,7 +137,7 @@
 
 
 def parse_release_file_contents(release_contents, filename):
-    release_lines = release_contents.split('\n')
+    release_lines = [l.rstrip() for l in release_contents.split('\n')]
 
     m = RELEASE_TYPE.match(release_lines[0])
     if m is not None:

{"golden_diff": "diff --git a/tooling/src/hypothesistooling/projects/hypothesispython.py b/tooling/src/hypothesistooling/projects/hypothesispython.py\n--- a/tooling/src/hypothesistooling/projects/hypothesispython.py\n+++ b/tooling/src/hypothesistooling/projects/hypothesispython.py\n@@ -25,8 +25,6 @@\n \n import hypothesistooling as tools\n import hypothesistooling.releasemanagement as rm\n-from hypothesistooling.releasemanagement import bump_version_info, \\\n-    replace_assignment, release_date_string\n \n PACKAGE_NAME = 'hypothesis-python'\n \n@@ -92,17 +90,17 @@\n \n     release_type, release_contents = parse_release_file()\n \n-    new_version_string, new_version_info = bump_version_info(\n+    new_version_string, new_version_info = rm.bump_version_info(\n         __version_info__, release_type)\n \n     __version_info__ = new_version_info\n     __version__ = new_version_string\n \n-    replace_assignment(\n+    rm.replace_assignment(\n         VERSION_FILE, '__version_info__', repr(new_version_info))\n \n     heading_for_new_version = ' - '.join((\n-        new_version_string, release_date_string()))\n+        new_version_string, rm.release_date_string()))\n     border_for_new_version = '-' * len(heading_for_new_version)\n \n     new_changelog_parts = [\ndiff --git a/tooling/src/hypothesistooling/releasemanagement.py b/tooling/src/hypothesistooling/releasemanagement.py\n--- a/tooling/src/hypothesistooling/releasemanagement.py\n+++ b/tooling/src/hypothesistooling/releasemanagement.py\n@@ -137,7 +137,7 @@\n \n \n def parse_release_file_contents(release_contents, filename):\n-    release_lines = release_contents.split('\\n')\n+    release_lines = [l.rstrip() for l in release_contents.split('\\n')]\n \n     m = RELEASE_TYPE.match(release_lines[0])\n     if m is not None:\n", "issue": "RELEASE.rst is not checked for trailing whitespace\nWhich means that when such a file is merged (e.g. from #1515), subsequent builds break.  This is bad and should be fixed ASAP.  See #1525 for temporary workaround.\n", "before_files": [{"content": "# coding=utf-8\n#\n# This file is part of Hypothesis, which may be found at\n# https://github.com/HypothesisWorks/hypothesis-python\n#\n# Most of this work is copyright (C) 2013-2018 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# CONTRIBUTING.rst for a full list of people who may hold copyright, and\n# consult the git log if you need to determine who owns an individual\n# 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\n\"\"\"Helpful common code for release management tasks that is shared across\nmultiple projects.\n\nNote that most code in here is brittle and specific to our build and\nprobably makes all sorts of undocumented assumptions, even as it looks\nlike a nice tidy reusable set of functionality.\n\"\"\"\n\n\nfrom __future__ import division, print_function, absolute_import\n\nimport re\nfrom datetime import datetime, timedelta\n\nimport hypothesistooling as tools\n\n\ndef release_date_string():\n    \"\"\"Returns a date string that represents what should be considered \"today\"\n    for the purposes of releasing. It is always measured in UTC, but if it's in\n    the last hour of the day it will actually be considered tomorrow.\n\n    The reason for counting it as the later day is that it ensures that\n    (unless our release process takes more than 23 hours) this value\n    remains consistent throughout the entire release.\n    \"\"\"\n    now = datetime.utcnow()\n\n    return max([\n        d.strftime('%Y-%m-%d') for d in (now, now + timedelta(hours=1))\n    ])\n\n\ndef assignment_matcher(name):\n    \"\"\"\n    Matches a single line of the form (some space)name = (some value). e.g.\n    \"  foo = 1\".\n    The whole line up to the assigned value is the first matching group,\n    the rest of the line is the second matching group.\n    i.e. group 1 is the assignment, group 2 is the value. In the above\n    example group 1 would be \"  foo = \" and group 2 would be \"1\"\n    \"\"\"\n    return re.compile(r'\\A(\\s*%s\\s*=\\s*)(.+)\\Z' % (re.escape(name),))\n\n\ndef extract_assignment_from_string(contents, name):\n    lines = contents.split('\\n')\n\n    matcher = assignment_matcher(name)\n\n    for i, l in enumerate(lines):\n        match = matcher.match(l)\n        if match is not None:\n            return match[2].strip()\n\n    raise ValueError('Key %s not found in %s' % (\n        name, contents\n    ))\n\n\ndef extract_assignment(filename, name):\n    with open(filename) as i:\n        return extract_assignment_from_string(i.read(), name)\n\n\ndef replace_assignment_in_string(contents, name, value):\n    lines = contents.split('\\n')\n\n    matcher = assignment_matcher(name)\n\n    count = 0\n\n    for i, l in enumerate(lines):\n        match = matcher.match(l)\n        if match is not None:\n            count += 1\n            lines[i] = match[1] + value\n\n    if count == 0:\n        raise ValueError('Key %s not found in %s' % (\n            name, contents\n        ))\n    if count > 1:\n        raise ValueError('Key %s found %d times in %s' % (\n            name, count, contents\n        ))\n\n    return '\\n'.join(lines)\n\n\ndef replace_assignment(filename, name, value):\n    \"\"\"Replaces a single assignment of the form key = value in a file with a\n    new value, attempting to preserve the existing format.\n\n    This is fairly fragile - in particular it knows nothing about\n    the file format. The existing value is simply the rest of the line after\n    the last space after the equals.\n    \"\"\"\n    with open(filename) as i:\n        contents = i.read()\n    result = replace_assignment_in_string(contents, name, value)\n    with open(filename, 'w') as o:\n        o.write(result)\n\n\nRELEASE_TYPE = re.compile(r\"^RELEASE_TYPE: +(major|minor|patch)\")\n\n\nMAJOR = 'major'\nMINOR = 'minor'\nPATCH = 'patch'\n\n\nVALID_RELEASE_TYPES = (MAJOR, MINOR, PATCH)\n\n\ndef parse_release_file(filename):\n    with open(filename) as i:\n        return parse_release_file_contents(i.read(), filename)\n\n\ndef parse_release_file_contents(release_contents, filename):\n    release_lines = release_contents.split('\\n')\n\n    m = RELEASE_TYPE.match(release_lines[0])\n    if m is not None:\n        release_type = m.group(1)\n        if release_type not in VALID_RELEASE_TYPES:\n            raise ValueError('Unrecognised release type %r' % (release_type,))\n        del release_lines[0]\n        release_contents = '\\n'.join(release_lines).strip()\n    else:\n        raise ValueError(\n            '%s does not start by specifying release type. The first '\n            'line of the file should be RELEASE_TYPE: followed by one of '\n            'major, minor, or patch, to specify the type of release that '\n            'this is (i.e. which version number to increment). Instead the '\n            'first line was %r' % (filename, release_lines[0],)\n        )\n\n    return release_type, release_contents\n\n\ndef bump_version_info(version_info, release_type):\n    new_version = list(version_info)\n    bump = VALID_RELEASE_TYPES.index(release_type)\n    new_version[bump] += 1\n    for i in range(bump + 1, len(new_version)):\n        new_version[i] = 0\n    new_version = tuple(new_version)\n    new_version_string = '.'.join(map(str, new_version))\n    return new_version_string, new_version\n\n\ndef update_markdown_changelog(changelog, name, version, entry):\n    with open(changelog) as i:\n        prev_contents = i.read()\n\n    title = '# %(name)s %(version)s (%(date)s)\\n\\n' % {\n        'name': name, 'version': version, 'date': release_date_string(),\n    }\n\n    with open(changelog, 'w') as o:\n        o.write(title)\n        o.write(entry.strip())\n        o.write('\\n\\n')\n        o.write(prev_contents)\n\n\ndef parse_version(version):\n    return tuple(map(int, version.split('.')))\n\n\ndef commit_pending_release(project):\n    \"\"\"Create a commit with the new release.\"\"\"\n    tools.git('rm', project.RELEASE_FILE)\n    tools.git('add', '-u', project.BASE_DIR)\n\n    tools.git(\n        'commit', '-m',\n        'Bump %s version to %s and update changelog'\n        '\\n\\n[skip ci]' % (project.PACKAGE_NAME, project.current_version(),)\n    )\n", "path": "tooling/src/hypothesistooling/releasemanagement.py"}, {"content": "# coding=utf-8\n#\n# This file is part of Hypothesis, which may be found at\n# https://github.com/HypothesisWorks/hypothesis-python\n#\n# Most of this work is copyright (C) 2013-2018 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# CONTRIBUTING.rst for a full list of people who may hold copyright, and\n# consult the git log if you need to determine who owns an individual\n# 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 re\nimport sys\nimport shutil\nimport subprocess\n\nimport hypothesistooling as tools\nimport hypothesistooling.releasemanagement as rm\nfrom hypothesistooling.releasemanagement import bump_version_info, \\\n    replace_assignment, release_date_string\n\nPACKAGE_NAME = 'hypothesis-python'\n\nHYPOTHESIS_PYTHON = os.path.join(tools.ROOT, PACKAGE_NAME)\nPYTHON_TAG_PREFIX = 'hypothesis-python-'\n\n\nBASE_DIR = HYPOTHESIS_PYTHON\n\nPYTHON_SRC = os.path.join(HYPOTHESIS_PYTHON, 'src')\nPYTHON_TESTS = os.path.join(HYPOTHESIS_PYTHON, 'tests')\n\nRELEASE_FILE = os.path.join(HYPOTHESIS_PYTHON, 'RELEASE.rst')\n\nassert os.path.exists(PYTHON_SRC)\n\n\n__version__ = None\n__version_info__ = None\n\nVERSION_FILE = os.path.join(PYTHON_SRC, 'hypothesis/version.py')\n\nwith open(VERSION_FILE) as o:\n    exec(o.read())\n\nassert __version__ is not None\nassert __version_info__ is not None\n\n\ndef has_release():\n    return os.path.exists(RELEASE_FILE)\n\n\ndef parse_release_file():\n    return rm.parse_release_file(RELEASE_FILE)\n\n\ndef has_source_changes():\n    return tools.has_changes([PYTHON_SRC])\n\n\nCHANGELOG_ANCHOR = re.compile(r\"^\\.\\. _v\\d+\\.\\d+\\.\\d+:$\")\nCHANGELOG_BORDER = re.compile(r\"^-+$\")\nCHANGELOG_HEADER = re.compile(r\"^\\d+\\.\\d+\\.\\d+ - \\d\\d\\d\\d-\\d\\d-\\d\\d$\")\n\n\ndef update_changelog_and_version():\n    global __version_info__\n    global __version__\n\n    contents = changelog()\n    assert '\\r' not in contents\n    lines = contents.split('\\n')\n    for i, l in enumerate(lines):\n        if CHANGELOG_ANCHOR.match(l):\n            assert CHANGELOG_BORDER.match(lines[i + 2]), repr(lines[i + 2])\n            assert CHANGELOG_HEADER.match(lines[i + 3]), repr(lines[i + 3])\n            assert CHANGELOG_BORDER.match(lines[i + 4]), repr(lines[i + 4])\n            beginning = '\\n'.join(lines[:i])\n            rest = '\\n'.join(lines[i:])\n            assert '\\n'.join((beginning, rest)) == contents\n            break\n\n    release_type, release_contents = parse_release_file()\n\n    new_version_string, new_version_info = bump_version_info(\n        __version_info__, release_type)\n\n    __version_info__ = new_version_info\n    __version__ = new_version_string\n\n    replace_assignment(\n        VERSION_FILE, '__version_info__', repr(new_version_info))\n\n    heading_for_new_version = ' - '.join((\n        new_version_string, release_date_string()))\n    border_for_new_version = '-' * len(heading_for_new_version)\n\n    new_changelog_parts = [\n        beginning.strip(),\n        '',\n        '.. _v%s:' % (new_version_string),\n        '',\n        border_for_new_version,\n        heading_for_new_version,\n        border_for_new_version,\n        '',\n        release_contents,\n        '',\n        rest\n    ]\n\n    with open(CHANGELOG_FILE, 'w') as o:\n        o.write('\\n'.join(new_changelog_parts))\n\n\nCHANGELOG_FILE = os.path.join(HYPOTHESIS_PYTHON, 'docs', 'changes.rst')\nDIST = os.path.join(HYPOTHESIS_PYTHON, 'dist')\n\n\ndef changelog():\n    with open(CHANGELOG_FILE) as i:\n        return i.read()\n\n\ndef build_distribution():\n    if os.path.exists(DIST):\n        shutil.rmtree(DIST)\n\n    subprocess.check_output([\n        sys.executable, 'setup.py', 'sdist', '--dist-dir', DIST,\n    ])\n\n\ndef upload_distribution():\n    tools.assert_can_release()\n    subprocess.check_call([\n        sys.executable, '-m', 'twine', 'upload',\n        '--config-file', tools.PYPIRC,\n        os.path.join(DIST, '*'),\n    ])\n\n\ndef current_version():\n    return __version__\n\n\ndef latest_version():\n    versions = []\n\n    for t in tools.tags():\n        if t.startswith(PYTHON_TAG_PREFIX):\n            t = t[len(PYTHON_TAG_PREFIX):]\n        else:\n            continue\n        assert t == t.strip()\n        parts = t.split('.')\n        assert len(parts) == 3\n        v = tuple(map(int, parts))\n        versions.append((v, t))\n\n    _, latest = max(versions)\n\n    return latest\n\n\ndef tag_name():\n    return PYTHON_TAG_PREFIX + __version__\n", "path": "tooling/src/hypothesistooling/projects/hypothesispython.py"}], "after_files": [{"content": "# coding=utf-8\n#\n# This file is part of Hypothesis, which may be found at\n# https://github.com/HypothesisWorks/hypothesis-python\n#\n# Most of this work is copyright (C) 2013-2018 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# CONTRIBUTING.rst for a full list of people who may hold copyright, and\n# consult the git log if you need to determine who owns an individual\n# 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\n\"\"\"Helpful common code for release management tasks that is shared across\nmultiple projects.\n\nNote that most code in here is brittle and specific to our build and\nprobably makes all sorts of undocumented assumptions, even as it looks\nlike a nice tidy reusable set of functionality.\n\"\"\"\n\n\nfrom __future__ import division, print_function, absolute_import\n\nimport re\nfrom datetime import datetime, timedelta\n\nimport hypothesistooling as tools\n\n\ndef release_date_string():\n    \"\"\"Returns a date string that represents what should be considered \"today\"\n    for the purposes of releasing. It is always measured in UTC, but if it's in\n    the last hour of the day it will actually be considered tomorrow.\n\n    The reason for counting it as the later day is that it ensures that\n    (unless our release process takes more than 23 hours) this value\n    remains consistent throughout the entire release.\n    \"\"\"\n    now = datetime.utcnow()\n\n    return max([\n        d.strftime('%Y-%m-%d') for d in (now, now + timedelta(hours=1))\n    ])\n\n\ndef assignment_matcher(name):\n    \"\"\"\n    Matches a single line of the form (some space)name = (some value). e.g.\n    \"  foo = 1\".\n    The whole line up to the assigned value is the first matching group,\n    the rest of the line is the second matching group.\n    i.e. group 1 is the assignment, group 2 is the value. In the above\n    example group 1 would be \"  foo = \" and group 2 would be \"1\"\n    \"\"\"\n    return re.compile(r'\\A(\\s*%s\\s*=\\s*)(.+)\\Z' % (re.escape(name),))\n\n\ndef extract_assignment_from_string(contents, name):\n    lines = contents.split('\\n')\n\n    matcher = assignment_matcher(name)\n\n    for i, l in enumerate(lines):\n        match = matcher.match(l)\n        if match is not None:\n            return match[2].strip()\n\n    raise ValueError('Key %s not found in %s' % (\n        name, contents\n    ))\n\n\ndef extract_assignment(filename, name):\n    with open(filename) as i:\n        return extract_assignment_from_string(i.read(), name)\n\n\ndef replace_assignment_in_string(contents, name, value):\n    lines = contents.split('\\n')\n\n    matcher = assignment_matcher(name)\n\n    count = 0\n\n    for i, l in enumerate(lines):\n        match = matcher.match(l)\n        if match is not None:\n            count += 1\n            lines[i] = match[1] + value\n\n    if count == 0:\n        raise ValueError('Key %s not found in %s' % (\n            name, contents\n        ))\n    if count > 1:\n        raise ValueError('Key %s found %d times in %s' % (\n            name, count, contents\n        ))\n\n    return '\\n'.join(lines)\n\n\ndef replace_assignment(filename, name, value):\n    \"\"\"Replaces a single assignment of the form key = value in a file with a\n    new value, attempting to preserve the existing format.\n\n    This is fairly fragile - in particular it knows nothing about\n    the file format. The existing value is simply the rest of the line after\n    the last space after the equals.\n    \"\"\"\n    with open(filename) as i:\n        contents = i.read()\n    result = replace_assignment_in_string(contents, name, value)\n    with open(filename, 'w') as o:\n        o.write(result)\n\n\nRELEASE_TYPE = re.compile(r\"^RELEASE_TYPE: +(major|minor|patch)\")\n\n\nMAJOR = 'major'\nMINOR = 'minor'\nPATCH = 'patch'\n\n\nVALID_RELEASE_TYPES = (MAJOR, MINOR, PATCH)\n\n\ndef parse_release_file(filename):\n    with open(filename) as i:\n        return parse_release_file_contents(i.read(), filename)\n\n\ndef parse_release_file_contents(release_contents, filename):\n    release_lines = [l.rstrip() for l in release_contents.split('\\n')]\n\n    m = RELEASE_TYPE.match(release_lines[0])\n    if m is not None:\n        release_type = m.group(1)\n        if release_type not in VALID_RELEASE_TYPES:\n            raise ValueError('Unrecognised release type %r' % (release_type,))\n        del release_lines[0]\n        release_contents = '\\n'.join(release_lines).strip()\n    else:\n        raise ValueError(\n            '%s does not start by specifying release type. The first '\n            'line of the file should be RELEASE_TYPE: followed by one of '\n            'major, minor, or patch, to specify the type of release that '\n            'this is (i.e. which version number to increment). Instead the '\n            'first line was %r' % (filename, release_lines[0],)\n        )\n\n    return release_type, release_contents\n\n\ndef bump_version_info(version_info, release_type):\n    new_version = list(version_info)\n    bump = VALID_RELEASE_TYPES.index(release_type)\n    new_version[bump] += 1\n    for i in range(bump + 1, len(new_version)):\n        new_version[i] = 0\n    new_version = tuple(new_version)\n    new_version_string = '.'.join(map(str, new_version))\n    return new_version_string, new_version\n\n\ndef update_markdown_changelog(changelog, name, version, entry):\n    with open(changelog) as i:\n        prev_contents = i.read()\n\n    title = '# %(name)s %(version)s (%(date)s)\\n\\n' % {\n        'name': name, 'version': version, 'date': release_date_string(),\n    }\n\n    with open(changelog, 'w') as o:\n        o.write(title)\n        o.write(entry.strip())\n        o.write('\\n\\n')\n        o.write(prev_contents)\n\n\ndef parse_version(version):\n    return tuple(map(int, version.split('.')))\n\n\ndef commit_pending_release(project):\n    \"\"\"Create a commit with the new release.\"\"\"\n    tools.git('rm', project.RELEASE_FILE)\n    tools.git('add', '-u', project.BASE_DIR)\n\n    tools.git(\n        'commit', '-m',\n        'Bump %s version to %s and update changelog'\n        '\\n\\n[skip ci]' % (project.PACKAGE_NAME, project.current_version(),)\n    )\n", "path": "tooling/src/hypothesistooling/releasemanagement.py"}, {"content": "# coding=utf-8\n#\n# This file is part of Hypothesis, which may be found at\n# https://github.com/HypothesisWorks/hypothesis-python\n#\n# Most of this work is copyright (C) 2013-2018 David R. MacIver\n# (david@drmaciver.com), but it contains contributions by others. See\n# CONTRIBUTING.rst for a full list of people who may hold copyright, and\n# consult the git log if you need to determine who owns an individual\n# 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 re\nimport sys\nimport shutil\nimport subprocess\n\nimport hypothesistooling as tools\nimport hypothesistooling.releasemanagement as rm\n\nPACKAGE_NAME = 'hypothesis-python'\n\nHYPOTHESIS_PYTHON = os.path.join(tools.ROOT, PACKAGE_NAME)\nPYTHON_TAG_PREFIX = 'hypothesis-python-'\n\n\nBASE_DIR = HYPOTHESIS_PYTHON\n\nPYTHON_SRC = os.path.join(HYPOTHESIS_PYTHON, 'src')\nPYTHON_TESTS = os.path.join(HYPOTHESIS_PYTHON, 'tests')\n\nRELEASE_FILE = os.path.join(HYPOTHESIS_PYTHON, 'RELEASE.rst')\n\nassert os.path.exists(PYTHON_SRC)\n\n\n__version__ = None\n__version_info__ = None\n\nVERSION_FILE = os.path.join(PYTHON_SRC, 'hypothesis/version.py')\n\nwith open(VERSION_FILE) as o:\n    exec(o.read())\n\nassert __version__ is not None\nassert __version_info__ is not None\n\n\ndef has_release():\n    return os.path.exists(RELEASE_FILE)\n\n\ndef parse_release_file():\n    return rm.parse_release_file(RELEASE_FILE)\n\n\ndef has_source_changes():\n    return tools.has_changes([PYTHON_SRC])\n\n\nCHANGELOG_ANCHOR = re.compile(r\"^\\.\\. _v\\d+\\.\\d+\\.\\d+:$\")\nCHANGELOG_BORDER = re.compile(r\"^-+$\")\nCHANGELOG_HEADER = re.compile(r\"^\\d+\\.\\d+\\.\\d+ - \\d\\d\\d\\d-\\d\\d-\\d\\d$\")\n\n\ndef update_changelog_and_version():\n    global __version_info__\n    global __version__\n\n    contents = changelog()\n    assert '\\r' not in contents\n    lines = contents.split('\\n')\n    for i, l in enumerate(lines):\n        if CHANGELOG_ANCHOR.match(l):\n            assert CHANGELOG_BORDER.match(lines[i + 2]), repr(lines[i + 2])\n            assert CHANGELOG_HEADER.match(lines[i + 3]), repr(lines[i + 3])\n            assert CHANGELOG_BORDER.match(lines[i + 4]), repr(lines[i + 4])\n            beginning = '\\n'.join(lines[:i])\n            rest = '\\n'.join(lines[i:])\n            assert '\\n'.join((beginning, rest)) == contents\n            break\n\n    release_type, release_contents = parse_release_file()\n\n    new_version_string, new_version_info = rm.bump_version_info(\n        __version_info__, release_type)\n\n    __version_info__ = new_version_info\n    __version__ = new_version_string\n\n    rm.replace_assignment(\n        VERSION_FILE, '__version_info__', repr(new_version_info))\n\n    heading_for_new_version = ' - '.join((\n        new_version_string, rm.release_date_string()))\n    border_for_new_version = '-' * len(heading_for_new_version)\n\n    new_changelog_parts = [\n        beginning.strip(),\n        '',\n        '.. _v%s:' % (new_version_string),\n        '',\n        border_for_new_version,\n        heading_for_new_version,\n        border_for_new_version,\n        '',\n        release_contents,\n        '',\n        rest\n    ]\n\n    with open(CHANGELOG_FILE, 'w') as o:\n        o.write('\\n'.join(new_changelog_parts))\n\n\nCHANGELOG_FILE = os.path.join(HYPOTHESIS_PYTHON, 'docs', 'changes.rst')\nDIST = os.path.join(HYPOTHESIS_PYTHON, 'dist')\n\n\ndef changelog():\n    with open(CHANGELOG_FILE) as i:\n        return i.read()\n\n\ndef build_distribution():\n    if os.path.exists(DIST):\n        shutil.rmtree(DIST)\n\n    subprocess.check_output([\n        sys.executable, 'setup.py', 'sdist', '--dist-dir', DIST,\n    ])\n\n\ndef upload_distribution():\n    tools.assert_can_release()\n    subprocess.check_call([\n        sys.executable, '-m', 'twine', 'upload',\n        '--config-file', tools.PYPIRC,\n        os.path.join(DIST, '*'),\n    ])\n\n\ndef current_version():\n    return __version__\n\n\ndef latest_version():\n    versions = []\n\n    for t in tools.tags():\n        if t.startswith(PYTHON_TAG_PREFIX):\n            t = t[len(PYTHON_TAG_PREFIX):]\n        else:\n            continue\n        assert t == t.strip()\n        parts = t.split('.')\n        assert len(parts) == 3\n        v = tuple(map(int, parts))\n        versions.append((v, t))\n\n    _, latest = max(versions)\n\n    return latest\n\n\ndef tag_name():\n    return PYTHON_TAG_PREFIX + __version__\n", "path": "tooling/src/hypothesistooling/projects/hypothesispython.py"}]}