File size: 3,255 Bytes
ceea884 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | """์คํธ๋ฆฌ๋ฐ JSON ๋ฆฌ๋ โ ๊ฒฝ๊ณ ์กฐ๊ฑด ์์ฃผ.
์ด ํ์๋ 1.35 GB ํ์ผ์ ๋ค๋ฃจ๋ฏ๋ก '๋ฒํผ ๊ฒฝ๊ณ์ ์์๊ฐ ๊ฑธ์น๋' ๊ฒฝ์ฐ๊ฐ ๋ฐ๋์ ์๊ธด๋ค.
์ฒญํฌ ํฌ๊ธฐ๋ฅผ ์์ฃผ ์๊ฒ ๊ฐ์ ํด ๊ทธ ๊ฒฝ๋ก๋ฅผ ์ผ๋ถ๋ฌ ๋ฐ๋๋ค.
"""
import json
import pytest
from nusim.io.jsonstream import index_table, iter_json_array, load_table
def _write(tmp_path, obj, name="t.json"):
p = tmp_path / name
p.write_text(json.dumps(obj, ensure_ascii=False))
return p
def test_roundtrip_simple(tmp_path):
data = [{"token": f"t{i}", "v": i} for i in range(100)]
assert list(iter_json_array(_write(tmp_path, data))) == data
def test_empty_array(tmp_path):
assert list(iter_json_array(_write(tmp_path, []))) == []
def test_single_element(tmp_path):
assert list(iter_json_array(_write(tmp_path, [{"a": 1}]))) == [{"a": 1}]
@pytest.mark.parametrize("chunk", [1, 2, 3, 7, 16, 64, 4096])
def test_tiny_chunks_force_boundary_splits(tmp_path, chunk):
"""์ฒญํฌ๊ฐ 1๋ฐ์ดํธ์ฌ๋ ๊ฒฐ๊ณผ๊ฐ ๊ฐ์์ผ ํ๋ค. ๊ฒฝ๊ณ ์ฌ์๋ ๋ก์ง์ ํต์ฌ ๊ฒ์ฆ."""
data = [{"token": f"tok{i}", "nested": {"x": [1, 2, 3]}, "s": "a" * i} for i in range(40)]
p = _write(tmp_path, data)
assert list(iter_json_array(p, chunk_size=chunk)) == data
def test_unicode_and_escapes(tmp_path):
data = [
{"desc": "์ฑ๊ฐํฌ๋ฅด onenorth, ์ฐ์ฒ"},
{"desc": 'quote " and backslash \\ and bracket ] inside'},
{"desc": "์\n์ค\tํญ"},
]
p = _write(tmp_path, data)
# ']' ๊ฐ ๋ฌธ์์ด ์์ ์์ด๋ ๋ฐฐ์ด ๋์ผ๋ก ์ค์ธํ๋ฉด ์ ๋๋ค
assert list(iter_json_array(p, chunk_size=3)) == data
def test_whitespace_and_pretty_printed(tmp_path):
data = [{"a": 1}, {"b": 2}]
p = tmp_path / "pretty.json"
p.write_text("\n\n " + json.dumps(data, indent=4) + "\n\n")
assert list(iter_json_array(p, chunk_size=5)) == data
def test_not_an_array(tmp_path):
p = tmp_path / "obj.json"
p.write_text('{"a": 1}')
with pytest.raises(ValueError, match="๋ฐฐ์ด"):
list(iter_json_array(p))
def test_truncated_file_raises(tmp_path):
p = tmp_path / "cut.json"
p.write_text('[{"a": 1}, {"b": ')
with pytest.raises(ValueError, match="์๋ ธ"):
list(iter_json_array(p, chunk_size=4))
def test_load_table_filters_rows_and_fields(tmp_path):
data = [{"token": str(i), "keep": i % 2 == 0, "big": "x" * 50} for i in range(10)]
p = _write(tmp_path, data)
got = load_table(p, keep=lambda r: r["keep"], fields=("token",))
assert got == [{"token": str(i)} for i in range(0, 10, 2)]
def test_index_table(tmp_path):
data = [{"token": "a", "v": 1}, {"token": "b", "v": 2}]
got = index_table(_write(tmp_path, data), fields=("v",))
assert got == {"a": {"v": 1}, "b": {"v": 2}}
def test_memory_stays_bounded(tmp_path):
"""์์๋ฅผ ๋ค ๋ชจ์ผ์ง ์๊ณ ํ๋ ค๋ณด๋ด๋์ง. ์ ๋๋ ์ดํฐ๊ฐ ๋ฆฌ์คํธ๋ก ํดํํ๋ฉด ์คํจํ๋ค."""
import sys
data = [{"i": i, "pad": "y" * 200} for i in range(5000)]
p = _write(tmp_path, data)
it = iter_json_array(p, chunk_size=1024)
first = next(it)
assert first["i"] == 0
# ์ ๋๋ ์ดํฐ ์์ฒด๋ ์์์ผ ํ๋ค
assert sys.getsizeof(it) < 1000
|