geändert: .gitignore

geändert:       README.md
	geändert:       pyproject.toml
	geändert:       src/config_parser/__init__.py
	neue Datei:     src/config_parser/_configuration.py
	neue Datei:     src/config_parser/exceptions.py
	neue Datei:     src/config_parser/ini.py
	neue Datei:     src/config_parser/json.py
	neue Datei:     src/config_parser/parse/ini.py
	neue Datei:     src/config_parser/parse/json.py
	neue Datei:     tests/ini/test_ini.py
	neue Datei:     tests/json/test.json
	neue Datei:     tests/json/test_json.py
	neue Datei:     tests/json/test_parser.py
	neue Datei:     tests/test_configuration_class.py
This commit is contained in:
2026-02-07 15:28:28 +01:00
parent d34c40e52a
commit 467d0418ff
15 changed files with 1937 additions and 54 deletions
+168
View File
@@ -0,0 +1,168 @@
{
"level_1": {
"meta": {
"version": "1.0",
"generated": true,
"tags": ["test", "deep", "nested", "json"]
},
"level_2": {
"array": [
{
"id": 1,
"level_3": {
"level_4": {
"level_5": {
"config": {
"enabled": true,
"thresholds": {
"low": 0.1,
"medium": 0.5,
"high": 0.9
},
"modes": [
{
"name": "alpha",
"params": {
"retry": 3,
"timeout": {
"connect": 1000,
"read": 5000,
"deep": {
"even_deeper": {
"flag": false,
"notes": [
"still",
"going",
{
"deeper": {
"than": {
"most": {
"humans": {
"expect": {
"value": 42
}
}
}
}
}
}
]
}
}
}
}
}
]
}
}
}
}
}
],
"level_2_object": {
"a": {
"b": {
"c": {
"d": {
"e": {
"f": {
"g": {
"h": {
"i": {
"j": {
"k": "bottom"
}
}
}
}
}
}
}
}
}
}
}
}
},
"level3": {
"root": [
[
[
[
{
"a": [
{
"b": [
{
"c": [
{
"d": [
{ "e": [
{ "f": "bottom"
}
]
}
]
}
]
}
]
}
]
}
]
]
]
]
}
, "4": {
"level": {
"level": {
"level": {
"level": {
"level": {
"items": [
{"x": {"y": {"z": [1,2,3,4,5]}}},
{"x": {"y": {"z": [1,2,3,4,5]}}},
{"x": {"y": {"z": [1,2,3,4,5]}}},
{"x": {"y": {"z": [1,2,3,4,5]}}},
{"x": {"y": {"z": [1,2,3,4,5]}}}
]
}
}
}
}
}
}
,
"0":{
"n0": {
"n1": {
"n2": {
"n3": {
"n4": {
"n5": {
"n6": { "n7": { "n8": { "n9": { "n10": { "n11": { "n12": {
"n13": {
"n14": {
"n15": {
"n16": {
"n17": {
"n18": { "n19": { "n20": "bottom"
} } }
}
} }}
}
}
}
} }
}
}
}
}
}}
}
}
}
}
+209
View File
@@ -0,0 +1,209 @@
from src.config_parser.json import JSONConfiguration
from src.config_parser.exceptions import JSONObjectSyntaxError, JSONValueSyntaxError, JSONStringSyntaxError, JSONArraySyntaxError, EscapeSequenceSyntaxError
def test_json_configuration():
# Test valid JSON configuration parsing
assert dict(JSONConfiguration.from_string('{"key1": "value1", "key2": 42, "key3": true, "key4": {"k1": 42, "k2": [1, null, true]}}')) == {"key1": "value1", "key2": 42, "key3": True, "key4": {"k1": 42, "k2": [1, None, True]}}
assert dict(JSONConfiguration.from_string('''{
"name": "John Doe",
"active": true,
"score": 99.5,
"roles": ["admin", "developer"],
"meta": null
}''')) == {'name': 'John Doe', 'active': True, 'score': 99.5, 'roles': ['admin', 'developer'], 'meta': None}
assert dict(JSONConfiguration.from_string('''{
"items": [],
"config": {},
"enabled": false
}''')) == {'items': [], 'config': {}, 'enabled': False}
assert dict(JSONConfiguration.from_string('''{
"int": 1,
"float": 1.0,
"scientific": 1e3,
"negative": -42
}''')) == {
'int': 1,
'float': 1.0,
'scientific': 1000.0,
'negative': -42
}
assert dict(JSONConfiguration.from_string('''{
"text": "🌍",
"escaped": "Line1\\nLine2\\tTabbed",
"unicode_escape": "\u263A"
}''')) == {
'text': '🌍',
'escaped': 'Line1\nLine2\tTabbed',
'unicode_escape': ''
}
assert dict(JSONConfiguration.from_string('''{"m": [
{ "id": 1, "value": "a" },
{ "id": 2, "value": "b" },
{ "id": 3, "value": null }
]}
''')) == {
'm': [
{'id': 1, 'value': 'a'},
{'id': 2, 'value': 'b'},
{'id': 3, 'value': None},
]
}
assert dict(JSONConfiguration.from_string('''{
"1": "one",
"true": "yes",
"null": "nothing"
}
''')) == {
'1': 'one',
'true': 'yes',
'null': 'nothing'
}
assert dict(JSONConfiguration.from_string('''{
"a": {
"b": {
"c": {
"d": [1, 2, {"e": false}]
}
}
}
}
''')) == {
'a': {
'b': {
'c': {
'd': [1, 2, {'e': False}]
}
}
}
}
assert dict(JSONConfiguration.from_string('''{
"x": 1,
"x": 2,
"x": 3
}
''')) == {'x': 3}
assert dict(JSONConfiguration.from_string('''{"nested": [[[[[[[[[[[[[[[[[[[[0]]]]]]]]]]]]]]]]]]]]}''')) == {'nested': [[[[[[[[[[[[[[[[[[[[0]]]]]]]]]]]]]]]]]]]]}
assert dict(JSONConfiguration.from_string('''{
"id": 9007199254740993
}
''')) == {'id': 9007199254740993}
assert dict(JSONConfiguration.from_string('''{
"value": 0.1
}''')) == {'value': 0.1}
assert dict(JSONConfiguration.from_string('{ "x": null }')) == {'x': None}
assert dict(JSONConfiguration.from_string('{}')) == {}
assert dict(JSONConfiguration.from_string('''{
"a": "",
"b": null
}
''')) == {'a': '', 'b': None}
assert dict(JSONConfiguration.from_string('''{
"char1": "é",
"char2": "e\u0301"
}''')) == {
'char1': 'é',
'char2': ''
}
assert dict(JSONConfiguration.from_string('''{
"text": "hello\u0000world"
}''')) == {'text': 'hello\x00world'}
assert dict(JSONConfiguration.from_string('''{
"enabled": "false"
}
''')) == {'enabled': 'false'}
assert dict(JSONConfiguration.from_string('''{"array": [1, "1", true, null, {}, []]
}''')) == {'array': [1, '1', True, None, {}, []]}
assert dict(JSONConfiguration.from_string('''{ "a" :
[ 1 ,2
,3 ] }
''')) == {'a': [1, 2, 3]}
# Test exceptions
try:
JSONConfiguration.from_string('{"a": 1,}')
assert False, 'Excepted JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
JSONConfiguration.from_string('''{
// Comment
"a": 1
}''')
assert False, 'Excepted JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
JSONConfiguration.from_string('{"a": 1 /* Comment */}')
assert False, 'Excepted JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
JSONConfiguration.from_string('{a: 1}')
assert False, 'Excepted JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
JSONConfiguration.from_string('{\'a\': \'b\'}')
assert False, 'Excepted JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
JSONConfiguration.from_string('{"x": 012}')
assert False, 'Excepted JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
JSONConfiguration.from_string('{"x": NaN, "y": Infinity}')
assert False, 'Excepted JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
JSONConfiguration.from_string('{"text": "hello\nworld"}') # This is not a JSON escape sequence!
assert False, 'Excepted JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
try:
JSONConfiguration.from_string('{"x": "\q"}')
assert False, 'Excepted EscapeSequenceSyntaxError'
except EscapeSequenceSyntaxError:
pass
try:
JSONConfiguration.from_string('{"x": "\\u123"}')
assert False, 'Excepted EscapeSequenceSyntaxError'
except EscapeSequenceSyntaxError:
pass
try:
JSONConfiguration.from_string('{ "a": 1 } { "b": 2 }')
assert False, 'Excepted JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
JSONConfiguration.from_string('{"a": 1 "b": 2}')
assert False, 'Excepted JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
JSONConfiguration.from_string('{"a": [1, 2, 3}')
assert False, 'Excepted JSONArraySyntaxError'
except JSONArraySyntaxError:
pass
try:
JSONConfiguration.from_string('')
assert False, 'Excepted JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
+429
View File
@@ -0,0 +1,429 @@
from src.config_parser.parse.json import parse_escape_sequences, JSONNumber, JSONString, JSONBoolean, JSONNull, JSONArray, JSONObject, parse_type, parse_json
from src.config_parser.exceptions import EscapeSequenceSyntaxError, JSONNumberSyntaxError, JSONStringSyntaxError, JSONBooleanSyntaxError, JSONNullSyntaxError, JSONArraySyntaxError, JSONObjectSyntaxError, JSONValueSyntaxError
import os
def test_parse_escape_sequences():
# Test valid escape sequences
assert parse_escape_sequences(r'Hello\nWorld') == 'Hello\nWorld'
assert parse_escape_sequences(r'Hello\tWorld') == 'Hello\tWorld'
assert parse_escape_sequences(r'Hello\\World') == 'Hello\\World'
assert parse_escape_sequences(r'Hello\"World\"') == 'Hello"World"'
assert parse_escape_sequences(r'Hello\/World') == 'Hello/World'
assert parse_escape_sequences(r'Hello\bWorld') == 'Hello\bWorld'
assert parse_escape_sequences(r'Hello\fWorld') == 'Hello\fWorld'
assert parse_escape_sequences(r'Hello\rWorld') == 'Hello\rWorld'
assert parse_escape_sequences(r'Hello\u0041World') == 'HelloAWorld'
# Test invalid escape sequences
try:
parse_escape_sequences(r'Hello\qWorld')
assert False, 'Expected EscapeSequenceSyntaxError'
except EscapeSequenceSyntaxError:
pass
try:
parse_escape_sequences(r'Hello\u00G1World')
assert False, 'Expected EscapeSequenceSyntaxError'
except EscapeSequenceSyntaxError:
pass
try:
parse_escape_sequences(r'\u123')
assert False, 'Expected EscapeSequenceSyntaxError'
except EscapeSequenceSyntaxError:
pass
def test_parse_number():
# Test parsing integers
assert JSONNumber.parse('0').value == 0
assert JSONNumber.parse('42').value == 42
assert JSONNumber.parse('-42').value == -42
# Test parsing floats
assert JSONNumber.parse('0.0').value == 0.0
assert JSONNumber.parse('3.14').value == 3.14
assert JSONNumber.parse('-3.14').value == -3.14
# Test exceptions
try:
JSONNumber.parse('abc')
assert False, 'Expected JSONNumberSyntaxError'
except JSONNumberSyntaxError:
pass
try:
JSONNumber.parse('01')
assert False, 'Expected JSONNumberSyntaxError'
except JSONNumberSyntaxError:
pass
def test_parse_str():
# Test parsing strings
assert JSONString.parse('"Hello, World!"').value == 'Hello, World!'
assert JSONString.parse(r'"Hello,\nWorld!"').value == 'Hello,\nWorld!'
assert JSONString.parse(r'"Hello,\\World!"').value == 'Hello,\\World!'
# Test exceptions
try:
JSONString.parse('\'Hello, World!\'')
assert False, 'Excepted JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
try:
JSONString.parse('"Hello, World!')
assert False, 'Excepted JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
try:
JSONString.parse('Hello, World!"')
assert False, 'Excepted JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
try:
JSONString.parse('Hello, World!')
assert False, 'Excepted JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
try:
JSONString.parse('""Hello, World!"')
assert False, 'Excepted JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
def test_parse_boolean():
# Test parsing booleans
assert JSONBoolean.parse('true').value == True
assert JSONBoolean.parse('false').value == False
# Test exceptions
try:
JSONBoolean.parse('True')
assert False, 'Expected JSONBooleanSyntaxError'
except JSONBooleanSyntaxError:
pass
try:
JSONBoolean.parse('False')
assert False, 'Expected JSONBooleanSyntaxError'
except JSONBooleanSyntaxError:
pass
try:
JSONBoolean.parse('')
assert False, 'Expected JSONBooleanSyntaxError'
except JSONBooleanSyntaxError:
pass
def test_parse_null():
# Test parsing null
assert JSONNull.parse('null').value is None
# Test exceptions
try:
JSONNull.parse('NULL')
assert False, 'Expected JSONNullSyntaxError'
except JSONNullSyntaxError:
pass
try:
JSONNull.parse('Null')
assert False, 'Expected JSONNullSyntaxError'
except JSONNullSyntaxError:
pass
try:
JSONNull.parse('')
assert False, 'Expected JSONNullSyntaxError'
except JSONNullSyntaxError:
pass
def test_parse_array():
# Test parsing arrays
assert JSONArray.parse('[]').value == []
assert JSONArray.parse('[1, 2, 3]').value == [1, 2, 3]
assert JSONArray.parse('[1, "2", true, null]').value == [1, '2', True, None]
assert JSONArray.parse(r'[1, "2", true, null]').value == [1, '2', True, None]
# Test exceptions
try:
JSONArray.parse('[')
assert False, 'Expected JSONArraySyntaxError'
except JSONArraySyntaxError:
pass
try:
JSONArray.parse('[1, 2')
assert False, 'Expected JSONArraySyntaxError'
except JSONArraySyntaxError:
pass
def test_parse_object():
# Test parsing objects
assert JSONObject.parse('{}').value == {}
assert JSONObject.parse('{"key": "value"}').value == {'key': 'value'}
assert JSONObject.parse('{"key1": 1, "key2": "value2", "key3": true, "key4": null}').value == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
assert JSONObject.parse(r'{"key1": 1, "key2": "value2", "key3": true, "key4": null}').value == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
with open(os.path.dirname(__file__) + '/test.json') as jsonf:
assert JSONObject.parse(jsonf.read()).value == {
"level_1": {
"meta": {
"version": "1.0",
"generated": True,
"tags": ["test", "deep", "nested", "json"],
},
"level_2": {
"array": [
{
"id": 1,
"level_3": {
"level_4": {
"level_5": {
"config": {
"enabled": True,
"thresholds": {
"low": 0.1,
"medium": 0.5,
"high": 0.9,
},
"modes": [
{
"name": "alpha",
"params": {
"retry": 3,
"timeout": {
"connect": 1000,
"read": 5000,
"deep": {
"even_deeper": {
"flag": False,
"notes": [
"still",
"going",
{
"deeper": {
"than": {
"most": {
"humans": {
"expect": {
"value": 42
}
}
}
}
}
},
],
}
},
},
},
}
],
}
}
}
},
}
],
"level_2_object": {
"a": {
"b": {
"c": {
"d": {
"e": {
"f": {
"g": {
"h": {
"i": {
"j": {
"k": "bottom"
}
}
}
}
}
}
}
}
}
}
},
},
},
"level3": {
"root": [
[
[
[
{
"a": [
{
"b": [
{
"c": [
{
"d": [
{
"e": [
{
"f": "bottom"
}
]
}
]
}
]
}
]
}
]
}
]
]
]
]
},
"4": {
"level": {
"level": {
"level": {
"level": {
"level": {
"items": [
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
{"x": {"y": {"z": [1, 2, 3, 4, 5]}}},
]
}
}
}
}
}
},
"0": {
"n0": {
"n1": {
"n2": {
"n3": {
"n4": {
"n5": {
"n6": {
"n7": {
"n8": {
"n9": {
"n10": {
"n11": {
"n12": {
"n13": {
"n14": {
"n15": {
"n16": {
"n17": {
"n18": {
"n19": {
"n20": "bottom"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
},
}
jsonf.close()
# Test exceptions
def test_parse_type():
# Test detecting types
assert parse_type('42') == JSONNumber
assert parse_type('"abc"') == JSONString
assert parse_type('true') == JSONBoolean
assert parse_type('false') == JSONBoolean
assert parse_type('null') == JSONNull
assert parse_type('[1, 2, 3, "4", "5", "6", true, false, null]') == JSONArray
assert parse_type('{"1": 2}') == JSONObject
assert parse_type('{"1": \n\t 2 }') == JSONObject # test whitespaces
# Test very large data
with open(os.path.dirname(__file__) + '/test.json', 'r') as jsonf:
assert parse_type('[' + jsonf.read() + ']') == JSONArray
jsonf.close()
# Test exceptions
try:
parse_type('abc')
assert False, 'Expected JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
parse_type('1s')
assert False, 'Expected JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
parse_type('[')
assert False, 'Expected JSONArraySyntaxError'
except JSONArraySyntaxError:
pass
try:
parse_type('[a b]')
assert False, 'Expected JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
parse_type('"abc"abc')
assert False, 'Expected JSONStringSyntaxError'
except JSONStringSyntaxError:
pass
try:
parse_type('\'abc\'abc')
assert False, 'Expected JSONValueSyntaxError'
except JSONValueSyntaxError:
pass
try:
parse_type('{1: 2}')
assert False, 'Expected JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
try:
parse_type('{"1"}')
assert False, 'Expected JSONObjectSyntaxError'
except JSONObjectSyntaxError:
pass
def test_parse_json():
# Test parsing JSON
assert parse_json('{"key": "value"}') == {'key': 'value'}
assert parse_json('[1, 2, 3]') == [1, 2, 3]
assert parse_json('{"key1": 1, "key2": "value2", "key3": true, "key4": null}') == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}
assert parse_json(r'{"key1": 1, "key2": "value2", "key3": true, "key4": null}') == {'key1': 1, 'key2': 'value2', 'key3': True, 'key4': None}