patch-fixer 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/PKG-INFO +3 -2
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/README.md +1 -1
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer/patch_fixer.py +33 -2
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer.egg-info/PKG-INFO +3 -2
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer.egg-info/SOURCES.txt +1 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer.egg-info/requires.txt +1 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/pyproject.toml +2 -1
- patch_fixer-0.3.1/tests/test_norm.py +87 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/tests/test_repos.py +9 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/LICENSE +0 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer/__init__.py +0 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer.egg-info/dependency_links.txt +0 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/patch_fixer.egg-info/top_level.txt +0 -0
- {patch_fixer-0.3.0 → patch_fixer-0.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: patch-fixer
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.1
|
4
4
|
Summary: Fixes erroneous git apply patches to the best of its ability.
|
5
5
|
Maintainer-email: Alex Mueller <amueller474@gmail.com>
|
6
6
|
License-Expression: Apache-2.0
|
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
23
23
|
Requires-Dist: GitPython
|
24
24
|
Provides-Extra: test
|
25
|
+
Requires-Dist: hypothesis; extra == "test"
|
25
26
|
Requires-Dist: pytest; extra == "test"
|
26
27
|
Requires-Dist: requests; extra == "test"
|
27
28
|
Dynamic: license-file
|
@@ -71,7 +72,7 @@ pytest
|
|
71
72
|
```
|
72
73
|
From version `0.3.0` onward (at least until version `1.0`), some test failures are expected
|
73
74
|
in bugfix versions as I like to use test-driven development to build out new features.
|
74
|
-
Please only report test failures if the same test passed in the most recent `0.x.0` version.
|
75
|
+
Please only report test failures if the same test existed and passed in the most recent `0.x.0` version.
|
75
76
|
|
76
77
|
## License
|
77
78
|
|
@@ -43,7 +43,7 @@ pytest
|
|
43
43
|
```
|
44
44
|
From version `0.3.0` onward (at least until version `1.0`), some test failures are expected
|
45
45
|
in bugfix versions as I like to use test-driven development to build out new features.
|
46
|
-
Please only report test failures if the same test passed in the most recent `0.x.0` version.
|
46
|
+
Please only report test failures if the same test existed and passed in the most recent `0.x.0` version.
|
47
47
|
|
48
48
|
## License
|
49
49
|
|
@@ -25,9 +25,40 @@ class MissingHunkError(Exception):
|
|
25
25
|
pass
|
26
26
|
|
27
27
|
|
28
|
+
class BadCarriageReturn(ValueError):
|
29
|
+
pass
|
30
|
+
|
31
|
+
|
28
32
|
def normalize_line(line):
|
29
|
-
|
30
|
-
|
33
|
+
"""Normalize line endings while preserving whitespace."""
|
34
|
+
if not isinstance(line, str):
|
35
|
+
raise TypeError(f"Cannot normalize non-string object {line}")
|
36
|
+
|
37
|
+
# edge case: empty string
|
38
|
+
if line == "":
|
39
|
+
return "\n"
|
40
|
+
|
41
|
+
# special malformed ending: ...\n\r
|
42
|
+
if line.endswith("\n\r"):
|
43
|
+
raise BadCarriageReturn(f"carriage return after line feed: {line}")
|
44
|
+
|
45
|
+
# handle CRLF and simple CR/LF endings
|
46
|
+
if line.endswith("\r\n"):
|
47
|
+
core = line[:-2]
|
48
|
+
elif line.endswith("\r"):
|
49
|
+
core = line[:-1]
|
50
|
+
elif line.endswith("\n"):
|
51
|
+
core = line[:-1]
|
52
|
+
else:
|
53
|
+
core = line
|
54
|
+
|
55
|
+
# check for interior CR/LF (anything before the final terminator)
|
56
|
+
if "\n" in core:
|
57
|
+
raise ValueError(f"line feed in middle of line: {line}")
|
58
|
+
if "\r" in core:
|
59
|
+
raise BadCarriageReturn(f"carriage return in middle of line: {line}")
|
60
|
+
|
61
|
+
return core + "\n"
|
31
62
|
|
32
63
|
|
33
64
|
def find_hunk_start(context_lines, original_lines):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: patch-fixer
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.1
|
4
4
|
Summary: Fixes erroneous git apply patches to the best of its ability.
|
5
5
|
Maintainer-email: Alex Mueller <amueller474@gmail.com>
|
6
6
|
License-Expression: Apache-2.0
|
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
23
23
|
Requires-Dist: GitPython
|
24
24
|
Provides-Extra: test
|
25
|
+
Requires-Dist: hypothesis; extra == "test"
|
25
26
|
Requires-Dist: pytest; extra == "test"
|
26
27
|
Requires-Dist: requests; extra == "test"
|
27
28
|
Dynamic: license-file
|
@@ -71,7 +72,7 @@ pytest
|
|
71
72
|
```
|
72
73
|
From version `0.3.0` onward (at least until version `1.0`), some test failures are expected
|
73
74
|
in bugfix versions as I like to use test-driven development to build out new features.
|
74
|
-
Please only report test failures if the same test passed in the most recent `0.x.0` version.
|
75
|
+
Please only report test failures if the same test existed and passed in the most recent `0.x.0` version.
|
75
76
|
|
76
77
|
## License
|
77
78
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "patch-fixer"
|
7
|
-
version = "0.3.
|
7
|
+
version = "0.3.1"
|
8
8
|
description = "Fixes erroneous git apply patches to the best of its ability."
|
9
9
|
maintainers = [
|
10
10
|
{name = "Alex Mueller", email="amueller474@gmail.com"},
|
@@ -34,6 +34,7 @@ license-files = [
|
|
34
34
|
|
35
35
|
[project.optional-dependencies]
|
36
36
|
test = [
|
37
|
+
"hypothesis",
|
37
38
|
"pytest",
|
38
39
|
"requests"
|
39
40
|
]
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import pytest
|
2
|
+
from hypothesis import given, strategies as st
|
3
|
+
|
4
|
+
from patch_fixer.patch_fixer import normalize_line, BadCarriageReturn
|
5
|
+
|
6
|
+
# --- Good cases --------------------------------------------------
|
7
|
+
|
8
|
+
@pytest.mark.parametrize("line, expected", [
|
9
|
+
("", "\n"), # empty string -> newline
|
10
|
+
("foo", "foo\n"), # no terminator
|
11
|
+
("foo\r", "foo\n"), # CR terminator normalized
|
12
|
+
("foo\n", "foo\n"), # LF terminator unchanged
|
13
|
+
("foo\r\n", "foo\n"), # CRLF normalized
|
14
|
+
])
|
15
|
+
def test_normalize_good(line, expected):
|
16
|
+
assert normalize_line(line) == expected
|
17
|
+
|
18
|
+
|
19
|
+
# --- Type errors -------------------------------------------------
|
20
|
+
|
21
|
+
@pytest.mark.parametrize("bad", [
|
22
|
+
123,
|
23
|
+
4.56,
|
24
|
+
None,
|
25
|
+
True,
|
26
|
+
["list"],
|
27
|
+
{"set"},
|
28
|
+
{"dict": "val"},
|
29
|
+
("tuple",),
|
30
|
+
])
|
31
|
+
def test_normalize_type_error(bad):
|
32
|
+
with pytest.raises(TypeError):
|
33
|
+
normalize_line(bad)
|
34
|
+
|
35
|
+
|
36
|
+
# --- Bad endings -------------------------------------------------
|
37
|
+
|
38
|
+
@pytest.mark.parametrize("line", [
|
39
|
+
"foo\n\r", # LF then CR
|
40
|
+
"foo\rx", # CR not followed by LF at end
|
41
|
+
])
|
42
|
+
def test_normalize_bad_endings(line):
|
43
|
+
with pytest.raises(BadCarriageReturn):
|
44
|
+
normalize_line(line)
|
45
|
+
|
46
|
+
|
47
|
+
# --- Interior CR/LF ----------------------------------------------
|
48
|
+
|
49
|
+
def test_interior_lf_raises():
|
50
|
+
line = "bad\nline\n"
|
51
|
+
with pytest.raises(ValueError):
|
52
|
+
normalize_line(line)
|
53
|
+
|
54
|
+
def test_interior_cr_raises():
|
55
|
+
line = "bad\rcarriage\n"
|
56
|
+
with pytest.raises(BadCarriageReturn):
|
57
|
+
normalize_line(line)
|
58
|
+
|
59
|
+
# --- Hypothesis testing ------------------------------------------
|
60
|
+
|
61
|
+
# generate arbitrary strings including \r and \n
|
62
|
+
line_strategy = st.text(alphabet=st.characters(), min_size=0, max_size=100)
|
63
|
+
|
64
|
+
@given(line=line_strategy)
|
65
|
+
def test_normalize_line_hypothesis(line):
|
66
|
+
# we want to see that normalize_line either:
|
67
|
+
# 1. returns a string ending with exactly one "\n", or
|
68
|
+
# 2. raises ValueError for interior LF, or
|
69
|
+
# 3. raises BadCarriageReturn for interior CR or malformed endings
|
70
|
+
try:
|
71
|
+
result = normalize_line(line)
|
72
|
+
except BadCarriageReturn:
|
73
|
+
# must have an interior CR somewhere, or malformed ending
|
74
|
+
cr_condition = (("\r" in line[:-2])
|
75
|
+
or (line.endswith("\r") and not line.endswith("\r\n"))
|
76
|
+
or line.endswith("\n\r"))
|
77
|
+
assert cr_condition, f"BadCarriageReturn raised unexpectedly for line: {line!r}"
|
78
|
+
except ValueError:
|
79
|
+
# must have an interior LF somewhere
|
80
|
+
assert "\n" in line[:-1], f"ValueError raised unexpectedly for line: {line!r}"
|
81
|
+
else:
|
82
|
+
# function returned normally
|
83
|
+
assert result.endswith("\n"), f"Returned line does not end with \\n: {result!r}"
|
84
|
+
|
85
|
+
core = result[:-1]
|
86
|
+
assert "\n" not in core
|
87
|
+
assert "\r" not in core
|
@@ -27,11 +27,20 @@ import pytest
|
|
27
27
|
from patch_fixer import fix_patch
|
28
28
|
|
29
29
|
REPOS = {
|
30
|
+
("apache", "airflow"): ("26f6e54","2136f56"), # big repo
|
30
31
|
("asottile", "astpretty"): ("5b68c7e", "5a8296f"),
|
32
|
+
("astral-sh", "ruff"): ("7fee877", "11dae2c"),
|
33
|
+
("gabrielecirulli", "2048"): ("878098f", "478b6ec"), # adds binary files
|
34
|
+
("mrdoob", "three.js"): ("5f3a718", "b97f111"), # replaces images
|
35
|
+
("myriadrf", "LimeSDR-Mini"): ("0bb75e7", "fb012c8"), # gigantic diffs
|
31
36
|
("numpy", "numpy"): ("dca33b3", "5f82966"),
|
32
37
|
("pallets", "click"): ("93c6966", "e11a1ef"),
|
38
|
+
("psf", "black"): ("8d9d18c", "903bef5"), # whole year's worth of changes
|
39
|
+
("PyCQA", "flake8"): ("8bdec0b", "d45bdc0"), # two years of changes
|
33
40
|
("scipy", "scipy"): ("c2220c0", "4ca6dd9"),
|
41
|
+
("tox-dev", "tox"): ("fb3fe66", "01442da"), # four years
|
34
42
|
("yaml", "pyyaml"): ("48838a3", "a2d19c0"),
|
43
|
+
("zertovitch", "hac"): ("c563d18", "17207ee") # renamed binary files
|
35
44
|
}
|
36
45
|
|
37
46
|
CACHE_DIR = Path.home() / ".patch-testing"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|