Skip to content

Commit ca85030

Browse files
PredaaAeemeli
andauthored
Replace codecs.open() with built-in open() (#210)
* Set newline arg in open() to disable universal newlines * Refactor test util patch_files() to use TemporaryDirectory --------- Co-authored-by: Eemeli Aro <eemeli@mozilla.com>
1 parent addd1fc commit ca85030

File tree

9 files changed

+88
-121
lines changed

9 files changed

+88
-121
lines changed

fluent.runtime/fluent/runtime/fallback.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import codecs
21
import os
32
from collections.abc import Generator
43
from typing import TYPE_CHECKING, Any, Callable, Union, cast
@@ -158,7 +157,8 @@ def resources(
158157
path = self.localize_path(os.path.join(root, resource_id), locale)
159158
if not os.path.isfile(path):
160159
continue
161-
content = codecs.open(path, "r", "utf-8").read()
160+
with open(path, "r", encoding="utf-8", newline="\n") as file:
161+
content = file.read()
162162
resources.append(FluentParser().parse(content))
163163
if resources:
164164
yield resources

fluent.runtime/tests/test_fallback.py

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
from os.path import join
23
from .utils import patch_files
34

45
from fluent.runtime import FluentLocalization, FluentResourceLoader
@@ -11,30 +12,26 @@ def test_init(self):
1112
)
1213
self.assertTrue(callable(l10n.format_value))
1314

14-
@patch_files({
15-
"de/one.ftl": """one = in German
16-
.foo = one in German
17-
""",
18-
"de/two.ftl": """two = in German
19-
.foo = two in German
20-
""",
21-
"fr/two.ftl": """three = in French
22-
.foo = three in French
23-
""",
24-
"en/one.ftl": """four = exists
25-
.foo = four in English
26-
""",
27-
"en/two.ftl": """
28-
five = exists
29-
.foo = five in English
30-
bar =
31-
.foo = bar in English
32-
baz = baz in English
33-
""",
34-
})
35-
def test_bundles(self):
15+
@patch_files(
16+
{
17+
"de": {
18+
"one.ftl": "one = in German\n .foo = one in German\n",
19+
"two.ftl": "two = in German\n .foo = two in German\n",
20+
},
21+
"fr": {"two.ftl": "three = in French\n .foo = three in French\n"},
22+
"en": {
23+
"one.ftl": "four = exists\n .foo = four in English\n",
24+
"two.ftl": "five = exists\n .foo = five in English\n"
25+
+ "bar =\n .foo = bar in English\n"
26+
+ "baz = baz in English\n",
27+
},
28+
}
29+
)
30+
def test_bundles(self, root):
3631
l10n = FluentLocalization(
37-
["de", "fr", "en"], ["one.ftl", "two.ftl"], FluentResourceLoader("{locale}")
32+
["de", "fr", "en"],
33+
["one.ftl", "two.ftl"],
34+
FluentResourceLoader(join(root, "{locale}")),
3835
)
3936
bundles_gen = l10n._bundles()
4037
bundle_de = next(bundles_gen)
@@ -91,29 +88,31 @@ def test_bundles(self):
9188

9289

9390
class TestResourceLoader(unittest.TestCase):
94-
@patch_files({
95-
"en/one.ftl": "one = exists",
96-
"en/two.ftl": "two = exists",
97-
})
98-
def test_all_exist(self):
99-
loader = FluentResourceLoader("{locale}")
91+
@patch_files(
92+
{
93+
"en": {
94+
"one.ftl": "one = exists",
95+
"two.ftl": "two = exists",
96+
}
97+
}
98+
)
99+
def test_all_exist(self, root):
100+
loader = FluentResourceLoader(join(root, "{locale}"))
100101
resources_list = list(loader.resources("en", ["one.ftl", "two.ftl"]))
101102
self.assertEqual(len(resources_list), 1)
102103
resources = resources_list[0]
103104
self.assertEqual(len(resources), 2)
104105

105-
@patch_files({
106-
"en/two.ftl": "two = exists",
107-
})
108-
def test_one_exists(self):
109-
loader = FluentResourceLoader("{locale}")
106+
@patch_files({"en": {"two.ftl": "two = exists"}})
107+
def test_one_exists(self, root):
108+
loader = FluentResourceLoader(join(root, "{locale}"))
110109
resources_list = list(loader.resources("en", ["one.ftl", "two.ftl"]))
111110
self.assertEqual(len(resources_list), 1)
112111
resources = resources_list[0]
113112
self.assertEqual(len(resources), 1)
114113

115114
@patch_files({})
116-
def test_none_exist(self):
117-
loader = FluentResourceLoader("{locale}")
115+
def test_none_exist(self, root):
116+
loader = FluentResourceLoader(join(root, "{locale}"))
118117
resources_list = list(loader.resources("en", ["one.ftl", "two.ftl"]))
119118
self.assertEqual(len(resources_list), 0)

fluent.runtime/tests/test_utils.py

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,30 @@
11
import unittest
22
from .utils import patch_files
3-
import os
4-
import codecs
3+
from os.path import isdir, isfile, join
54

65

76
class TestFileSimulate(unittest.TestCase):
87
def test_basic(self):
9-
@patch_files({
10-
"the.txt": "The",
11-
"en/one.txt": "One",
12-
"en/two.txt": "Two"
13-
})
14-
def patch_me(a, b):
8+
@patch_files(
9+
{
10+
"the.txt": "The",
11+
"en": {
12+
"one.txt": "One",
13+
"two.txt": "Two",
14+
},
15+
}
16+
)
17+
def patch_me(a, b, root):
1518
self.assertEqual(a, 10)
1619
self.assertEqual(b, "b")
17-
self.assertFileIs(os.path.basename(__file__), None)
18-
self.assertFileIs("the.txt", "The")
19-
self.assertFileIs("en/one.txt", "One")
20-
self.assertFileIs("en\\one.txt", "One")
21-
self.assertFileIs("en/two.txt", "Two")
22-
self.assertFileIs("en\\two.txt", "Two")
23-
self.assertFileIs("en/three.txt", None)
24-
self.assertFileIs("en\\three.txt", None)
20+
with open(join(root, "the.txt")) as f:
21+
self.assertEqual(f.read(), "The")
22+
with open(join(root, "en", "one.txt")) as f:
23+
self.assertEqual(f.read(), "One")
24+
with open(join(root, "en", "two.txt")) as f:
25+
self.assertEqual(f.read(), "Two")
26+
self.assertTrue(isdir(join(root, "en")))
27+
self.assertFalse(isfile(join(root, "none.txt")))
28+
self.assertFalse(isfile(join(root, "en", "three.txt")))
2529

26-
with self.assertRaises(ValueError):
27-
os.path.isfile("en/")
2830
patch_me(10, "b")
29-
30-
def assertFileIs(self, filename, expect_contents):
31-
"""
32-
expect_contents is None: Expect file does not exist
33-
expect_contents is a str: Expect file to exist and contents to match
34-
"""
35-
if expect_contents is None:
36-
self.assertFalse(os.path.isfile(filename),
37-
f"Expected {filename} to not exist.")
38-
else:
39-
self.assertTrue(os.path.isfile(filename),
40-
f"Expected {filename} to exist.")
41-
with codecs.open(filename, "r", "utf-8") as f:
42-
self.assertEqual(f.read(), expect_contents)

fluent.runtime/tests/utils.py

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,35 @@
11
"""Utilities for testing."""
22

33
import textwrap
4-
from pathlib import PureWindowsPath, PurePosixPath
5-
from unittest import mock
6-
from io import StringIO
7-
import functools
4+
from functools import wraps
5+
from os import mkdir
6+
from os.path import join
7+
from tempfile import TemporaryDirectory
88

99

1010
def dedent_ftl(text):
1111
return textwrap.dedent(f"{text.rstrip()}\n")
1212

1313

14-
# Needed in test_falllback.py because it uses dict + string compare to make a virtual file structure
15-
def _normalize_file_path(path):
16-
"""Note: Does not support absolute paths or paths that
17-
contain '.' or '..' parts."""
18-
# Cannot use os.path or PurePath, because they only recognize
19-
# one kind of path separator
20-
if PureWindowsPath(path).is_absolute() or PurePosixPath(path).is_absolute():
21-
raise ValueError(f"Unsupported path: {path}")
22-
parts = path.replace("\\", "/").split("/")
23-
if "." in parts or ".." in parts:
24-
raise ValueError(f"Unsupported path: {path}")
25-
if parts and parts[-1] == "":
26-
# path ends with a trailing pathsep
27-
raise ValueError(f"Path appears to be a directory, not a file: {path}")
28-
return "/".join(parts)
29-
30-
31-
def patch_files(files: dict):
32-
"""Decorate a function to simulate files ``files`` during the function.
33-
34-
The keys of ``files`` are file names and must use '/' for path separator.
35-
The values are file contents. Directories or relative paths are not supported.
36-
Example: ``{"en/one.txt": "One", "en/two.txt": "Two"}``
37-
38-
The implementation may be changed to match the mechanism used.
39-
"""
40-
41-
# Here it is possible to validate file names, but skipped
42-
43-
def then(func):
44-
@mock.patch("os.path.isfile", side_effect=lambda p: _normalize_file_path(p) in files)
45-
@mock.patch("codecs.open", side_effect=lambda p, _, __: StringIO(files[_normalize_file_path(p)]))
46-
@functools.wraps(func) # Make ret look like func to later decorators
47-
def ret(*args, **kwargs):
48-
func(*args[:-2], **kwargs)
49-
return ret
50-
return then
14+
def patch_files(tree: dict):
15+
def build_file_tree(root: str, tree: dict) -> None:
16+
for name, value in tree.items():
17+
path = join(root, name)
18+
if isinstance(value, str):
19+
with open(path, "x", encoding="utf-8", newline="\n") as file:
20+
if value:
21+
file.write(value)
22+
else:
23+
mkdir(path)
24+
build_file_tree(path, value)
25+
26+
def decorator(fn):
27+
@wraps(fn)
28+
def wrapper(*args, **kwargs):
29+
with TemporaryDirectory() as root:
30+
build_file_tree(root, tree)
31+
return fn(*args, root, **kwargs)
32+
33+
return wrapper
34+
35+
return decorator

fluent.syntax/tests/syntax/test_reference.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import codecs
21
import json
32
import os
43
import unittest
@@ -7,7 +6,7 @@
76

87

98
def read_file(path):
10-
with codecs.open(path, "r", encoding="utf-8") as file:
9+
with open(path, "r", encoding="utf-8", newline="\n") as file:
1110
text = file.read()
1211
return text
1312

fluent.syntax/tests/syntax/test_structure.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import codecs
21
import json
32
import os
43
import unittest
@@ -7,7 +6,7 @@
76

87

98
def read_file(path):
10-
with codecs.open(path, "r", encoding="utf-8") as file:
9+
with open(path, "r", encoding="utf-8", newline="\n") as file:
1110
text = file.read()
1211
return text
1312

tools/fluentfmt.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/python
22

3-
import codecs
43
import sys
54

65
from fluent.syntax import parse, serialize
@@ -9,7 +8,7 @@
98

109

1110
def read_file(path):
12-
with codecs.open(path, "r", encoding="utf-8") as file:
11+
with open(path, "r", encoding="utf-8", newline="\n") as file:
1312
text = file.read()
1413
return text
1514

tools/parse.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/python
22

3-
import codecs
43
import json
54
import sys
65

@@ -10,7 +9,7 @@
109

1110

1211
def read_file(path):
13-
with codecs.open(path, "r", encoding="utf-8") as file:
12+
with open(path, "r", encoding="utf-8", newline="\n") as file:
1413
text = file.read()
1514
return text
1615

tools/serialize.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/python
22

3-
import codecs
43
import json
54
import sys
65

@@ -10,7 +9,7 @@
109

1110

1211
def read_json(path):
13-
with codecs.open(path, "r", encoding="utf-8") as file:
12+
with open(path, "r", encoding="utf-8", newline="\n") as file:
1413
return json.load(file)
1514

1615

0 commit comments

Comments
 (0)