Skip to content

Commit 1b1feb5

Browse files
Ken KundertKen Kundert
authored andcommitted
allow binary file objects to be passed to load() and dump()
1 parent 528a176 commit 1b1feb5

File tree

4 files changed

+45
-13
lines changed

4 files changed

+45
-13
lines changed

doc/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# General
44

55
project = u'NestedText'
6-
copyright = u'2020-2024, Ken and Kale Kundert'
6+
copyright = u'2020-2025, Ken and Kale Kundert'
77
release = '3.7'
88
version = '.'.join(release.split('.'))
99

doc/releases.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Latest development version
1616
| Version: 3.7
1717
| Released: 2024-04-27
1818
19+
- Allow binary files to be passed to :func:`load()` and :func:`dump()`.
20+
1921

2022
v3.7 (2024-04-27)
2123
-----------------

nestedtext/nestedtext.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"""
1313

1414
# MIT License {{{1
15-
# Copyright (c) 2020-2024 Ken and Kale Kundert
15+
# Copyright (c) 2020-2025 Ken and Kale Kundert
1616
#
1717
# Permission is hereby granted, free of charge, to any person obtaining a copy
1818
# of this software and associated documentation files (the "Software"), to deal
@@ -338,6 +338,11 @@ def read_lines(self):
338338
for lineno, line in enumerate(self.lines):
339339
key = None
340340
value = None
341+
try:
342+
# decode to utf8 if a byte string or binary file is given
343+
line = line.decode('utf8')
344+
except AttributeError:
345+
pass
341346
line = line.rstrip("\n")
342347

343348
# compute indentation
@@ -2197,16 +2202,21 @@ def dump(obj, dest, **kwargs):
21972202

21982203
try:
21992204
dest.write(content + "\n")
2200-
except AttributeError:
2205+
except (AttributeError, TypeError) as e:
22012206
# Avoid nested try-except blocks, since they lead to chained exceptions
22022207
# (e.g. if the file isn’t found, etc.) that unnecessarily complicate the
22032208
# stack trace.
2204-
pass
2209+
exception = e
22052210
else:
22062211
return
22072212

2208-
with open(dest, "w", encoding="utf-8") as f:
2209-
f.write(content + "\n")
2213+
if isinstance(exception, TypeError):
2214+
# file may be binary, encode in utf8 and try again
2215+
dest.write((content + "\n").encode('utf8'))
2216+
else:
2217+
# dest is a file name rather than a file pointer
2218+
with open(dest, "w", encoding="utf-8") as f:
2219+
f.write(content + "\n")
22102220

22112221

22122222
# NestedText Utilities {{{1

tests/test_nestedtext.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from quantiphy import Quantity
1212
import subprocess
1313
import json
14+
import os
1415

1516
test_api = Path(__file__).parent / 'official_tests' / 'api'
1617
import sys; sys.path.append(str(test_api))
@@ -1232,6 +1233,10 @@ def normalize_key(key, parent_keys):
12321233
except TypeError:
12331234
assert value == expected_value
12341235

1236+
with pytest.raises(KeyError) as exception:
1237+
value = nt.get_value("", normalized_keys)
1238+
assert exception.value.args == (normalized_keys[0],)
1239+
12351240
# print(value)
12361241
# try:
12371242
# assert index == int(value)
@@ -2027,8 +2032,8 @@ def test_dump_dialect():
20272032
list:
20282033
""", strip_nl="b")
20292034

2030-
# Test round-trip {{{1
2031-
# test_file_descriptors {{{2
2035+
# Misc tests {{{1
2036+
# test_file_descriptors() {{{2
20322037
def test_file_descriptors(tmp_path):
20332038
# program that writes out a simple NT document to stdout
20342039
write_data = dedent("""
@@ -2052,14 +2057,29 @@ def test_file_descriptors(tmp_path):
20522057
f"python {writer!s} | python {reader!s}",
20532058
shell = True,
20542059
capture_output = True,
2055-
env = {"COVERAGE_PROCESS_START":""},
2060+
env = {"COVERAGE_PROCESS_START":"", "PATH": os.environ["PATH"]}
20562061
)
2057-
assert results.stdout == b""
2058-
assert results.stderr == b""
2062+
assert results.stdout.decode('utf8') == ""
2063+
assert results.stderr.decode('utf8') == ""
20592064
assert results.returncode == 0
20602065

2061-
# test_andyde {{{2
2062-
def test_andyde(tmp_path):
2066+
# test_binary_files() {{{2
2067+
def test_binary_round_trip(tmp_path):
2068+
orig_data = {"dict": {}, "list": [], "str": "", "mls":"\\n"}
2069+
nt_path = tmp_path / "test.nt"
2070+
2071+
# dump nestedtext to a binary file
2072+
with nt_path.open('wb') as f:
2073+
test_nt = nt.dump(orig_data, f)
2074+
2075+
# read nestedtext from a binary file
2076+
with nt_path.open('rb') as f:
2077+
data_as_read = nt.load(f)
2078+
2079+
assert data_as_read == orig_data
2080+
2081+
# test_round_trip() {{{2
2082+
def test_round_trip(tmp_path):
20632083
data_python = {
20642084
"http://www.kde.org/standards/kcfg/1.0}kcfgfile": None,
20652085
"http://www.kde.org/standards/kcfg/1.0}group": {

0 commit comments

Comments
 (0)