Skip to content
Snippets Groups Projects
Commit f81298c8 authored by Piotr Maślanka's avatar Piotr Maślanka
Browse files

add json-related routines

parent f8567ed1
No related branches found
No related tags found
No related merge requests found
# v2.14.23 # v2.14.23
* added `write_json_to_file`
* added `read_json_from_file`
* added `write_json_to_file_if_different`
...@@ -21,3 +21,11 @@ This will serialize unknown objects in the following way. ...@@ -21,3 +21,11 @@ This will serialize unknown objects in the following way.
First, **__dict__** will be extracted out of this object. The dictionary First, **__dict__** will be extracted out of this object. The dictionary
will be constructed in such a way, that for each key in this **__dict__**, will be constructed in such a way, that for each key in this **__dict__**,
it's value's **repr** will be assigned. it's value's **repr** will be assigned.
.. autofunction:: satella.json.read_json_from_file
.. autofunction:: satella.json.write_json_to_file
.. autofunction:: satella.json.write_json_to_file_if_different
__version__ = '2.14.23_a1' __version__ = '2.14.23_a2'
import json import json
import typing as tp import typing as tp
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
try:
import ujson
except ImportError:
pass
__all__ = ['JSONEncoder', 'JSONAble', 'json_encode'] __all__ = ['JSONEncoder', 'JSONAble', 'json_encode', 'read_json_from_file',
'write_json_to_file', 'write_json_to_file_if_different']
Jsonable = tp.TypeVar('Jsonable', list, dict, str, int, float, None) Jsonable = tp.TypeVar('Jsonable', list, dict, str, int, float, None)
...@@ -39,3 +44,61 @@ def json_encode(x: tp.Any) -> str: ...@@ -39,3 +44,61 @@ def json_encode(x: tp.Any) -> str:
:param x: object to convert :param x: object to convert
""" """
return JSONEncoder().encode(x) return JSONEncoder().encode(x)
def write_json_to_file(path: str, value: JSONAble) -> None:
"""
Write out a JSON to a file as UTF-8 encoded plain text.
:param path: path to the file
:param value: JSON-able content
"""
if isinstance(value, JSONAble):
value = value.to_json()
with open(path, 'w') as f_out:
try:
ujson.dump(value, f_out)
except NameError:
json.dump(value, f_out)
def write_json_to_file_if_different(path: str, value: JSONAble) -> bool:
"""
Read JSON from a file. Write out a JSON to a file if it's value is different,
as UTF-8 encoded plain text.
:param path: path to the file
:param value: JSON-able content
:return: whether the write actually happened
"""
if isinstance(value, JSONAble):
value = value.to_json()
try:
val = read_json_from_file(path)
if val != value:
write_json_to_file(path, value)
return True
return False
except (OSError, ValueError):
write_json_to_file(path, value)
return True
def read_json_from_file(path: str) -> JSONAble:
"""
Load a JSON from a provided file, as UTF-8 encoded plain text.
:param path: path to the file
:return: JSON content
:raises ValueError: the file contained an invalid JSON
:raises OSError: the file was not readable or did not exist
"""
try:
with open(path, 'r') as f_in:
return ujson.load(f_in)
except NameError:
with open(path, 'r') as f_in:
try:
return json.load(f_in)
except json.decoder.JSONDecodeError as e:
raise ValueError(str(e))
...@@ -14,7 +14,7 @@ setup(keywords=['ha', 'high availability', 'scalable', 'scalability', 'server', ...@@ -14,7 +14,7 @@ setup(keywords=['ha', 'high availability', 'scalable', 'scalability', 'server',
'HTTPJSONSource': ['requests'], 'HTTPJSONSource': ['requests'],
'YAMLSource': ['pyyaml'], 'YAMLSource': ['pyyaml'],
'TOMLSource': ['toml'], 'TOMLSource': ['toml'],
'FasterJSONSource': ['ujson'], 'FasterJSON': ['ujson'],
'cassandra': ['cassandra-driver'], 'cassandra': ['cassandra-driver'],
'opentracing': ['opentracing'] 'opentracing': ['opentracing']
} }
......
...@@ -2,11 +2,25 @@ import json ...@@ -2,11 +2,25 @@ import json
import typing as tp import typing as tp
import unittest import unittest
from satella.json import JSONAble, json_encode from satella.json import JSONAble, json_encode, read_json_from_file, write_json_to_file, \
write_json_to_file_if_different
class TestJson(unittest.TestCase): class TestJson(unittest.TestCase):
def test_write_json_to_file_if_different(self):
d = {'test': 4}
self.assertTrue(write_json_to_file_if_different('test2.json', d))
self.assertFalse(write_json_to_file_if_different('test2.json', d))
d = {'test': 5}
self.assertTrue(write_json_to_file_if_different('test2.json', d))
self.assertFalse(write_json_to_file_if_different('test2.json', d))
def test_load_json_from_file(self):
d = {'test': 2}
write_json_to_file('test.json', d)
self.assertEqual(read_json_from_file('test.json'), d)
def test_jsonable_objects(self): def test_jsonable_objects(self):
class MyClass(JSONAble): class MyClass(JSONAble):
def to_json(self) -> tp.Union[list, dict, str, int, float, None]: def to_json(self) -> tp.Union[list, dict, str, int, float, None]:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment