sdcvalidator 4.5.1__tar.gz → 4.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/PKG-INFO +6 -2
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/README.md +5 -1
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/pyproject.toml +1 -1
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/__init__.py +1 -1
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/mcp_server.py +44 -9
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator.egg-info/PKG-INFO +6 -2
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator.egg-info/SOURCES.txt +1 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_mcp_server.py +52 -0
- sdcvalidator-4.5.3/tests/test_registry_launch.py +79 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/LICENSE +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/setup.cfg +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/cli.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/constants.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/converters.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/error_classifier.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/error_mapper.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/exceptions.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/instance_modifier.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/schema_checker.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/validator.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator/xsd11_restriction.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator.egg-info/dependency_links.txt +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator.egg-info/entry_points.txt +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator.egg-info/requires.txt +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/src/sdcvalidator.egg-info/top_level.txt +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_converters.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_error_classifier.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_error_mapper.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_instance_modifier.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_recovery.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_schema_checker.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_validator.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_xsd11_restriction.py +0 -0
- {sdcvalidator-4.5.1 → sdcvalidator-4.5.3}/tests/test_xsi_type.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdcvalidator
|
|
3
|
-
Version: 4.5.
|
|
3
|
+
Version: 4.5.3
|
|
4
4
|
Summary: SDC4 structural validator with ExceptionalValue recovery — thin wrapper over xmlschema
|
|
5
5
|
Author: Axius SDC, Inc.
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -151,9 +151,13 @@ sdcvalidator-json2xml data.json schema.xsd -o output.xml
|
|
|
151
151
|
`sdcvalidator` ships a stdio MCP (Model Context Protocol) server so any MCP-capable agent can validate SDC4 data without importing the Python library. It implements JSON-RPC 2.0 directly over stdio — no external MCP SDK dependency.
|
|
152
152
|
|
|
153
153
|
```bash
|
|
154
|
-
sdcvalidator
|
|
154
|
+
sdcvalidator # what an MCP registry client runs (uvx sdcvalidator)
|
|
155
|
+
sdcvalidator mcp # the same, spelled like the other Semantic Data Charter servers
|
|
156
|
+
sdcvalidator-mcp serve --mcp # still works for existing client configurations
|
|
155
157
|
```
|
|
156
158
|
|
|
159
|
+
A client configuration needs only `"command": "uvx", "args": ["sdcvalidator"]`.
|
|
160
|
+
|
|
157
161
|
### Tools
|
|
158
162
|
|
|
159
163
|
| Tool | Purpose |
|
|
@@ -129,9 +129,13 @@ sdcvalidator-json2xml data.json schema.xsd -o output.xml
|
|
|
129
129
|
`sdcvalidator` ships a stdio MCP (Model Context Protocol) server so any MCP-capable agent can validate SDC4 data without importing the Python library. It implements JSON-RPC 2.0 directly over stdio — no external MCP SDK dependency.
|
|
130
130
|
|
|
131
131
|
```bash
|
|
132
|
-
sdcvalidator
|
|
132
|
+
sdcvalidator # what an MCP registry client runs (uvx sdcvalidator)
|
|
133
|
+
sdcvalidator mcp # the same, spelled like the other Semantic Data Charter servers
|
|
134
|
+
sdcvalidator-mcp serve --mcp # still works for existing client configurations
|
|
133
135
|
```
|
|
134
136
|
|
|
137
|
+
A client configuration needs only `"command": "uvx", "args": ["sdcvalidator"]`.
|
|
138
|
+
|
|
135
139
|
### Tools
|
|
136
140
|
|
|
137
141
|
| Tool | Purpose |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sdcvalidator"
|
|
7
|
-
version = "4.5.
|
|
7
|
+
version = "4.5.3"
|
|
8
8
|
description = "SDC4 structural validator with ExceptionalValue recovery — thin wrapper over xmlschema"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -14,7 +14,7 @@ Thin wrapper over xmlschema with two-tier error classification and the SDC4
|
|
|
14
14
|
"quarantine-and-tag" recovery pattern (ISO 21090 ExceptionalValues).
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
__version__ = "4.5.
|
|
17
|
+
__version__ = "4.5.3"
|
|
18
18
|
|
|
19
19
|
# etree_tostring is re-exported from xmlschema so callers can serialize the
|
|
20
20
|
# ElementTree returned by validate_with_recovery() without importing xmlschema
|
|
@@ -36,7 +36,7 @@ from sdcvalidator.schema_checker import validate_sdc4_schema_compliance
|
|
|
36
36
|
JSONRPC_VERSION = "2.0"
|
|
37
37
|
MCP_PROTOCOL_VERSION = "2024-11-05"
|
|
38
38
|
SERVER_NAME = "sdcvalidator"
|
|
39
|
-
SERVER_VERSION = "4.5.
|
|
39
|
+
SERVER_VERSION = "4.5.3"
|
|
40
40
|
|
|
41
41
|
# Validator cache: schema_path -> SDC4Validator
|
|
42
42
|
_validators: dict[str, SDC4Validator] = {}
|
|
@@ -201,10 +201,24 @@ def _jsonrpc_error(id: Any, code: int, message: str, data: Any = None) -> dict:
|
|
|
201
201
|
return {"jsonrpc": JSONRPC_VERSION, "id": id, "error": error}
|
|
202
202
|
|
|
203
203
|
|
|
204
|
-
def _handle_request(request
|
|
204
|
+
def _handle_request(request) -> dict | None:
|
|
205
|
+
# ★ Attacker-reachable input over stdio with no authentication. A JSON
|
|
206
|
+
# value that is not an object, or an object whose params is not one,
|
|
207
|
+
# used to raise AttributeError and kill the server (VSL rollout R9).
|
|
208
|
+
# Every shape the protocol does not allow is answered with -32600.
|
|
209
|
+
if not isinstance(request, dict):
|
|
210
|
+
return _jsonrpc_error(None, -32600, "Invalid Request: a JSON-RPC request is an object")
|
|
211
|
+
req_id = request.get("id")
|
|
212
|
+
if not isinstance(req_id, (str, int, type(None))):
|
|
213
|
+
return _jsonrpc_error(None, -32600, "Invalid Request: id must be a string, a number or null")
|
|
205
214
|
method = request.get("method", "")
|
|
215
|
+
if not isinstance(method, str):
|
|
216
|
+
return _jsonrpc_error(req_id, -32600, "Invalid Request: method must be a string")
|
|
206
217
|
params = request.get("params", {})
|
|
207
|
-
|
|
218
|
+
if params is None:
|
|
219
|
+
params = {}
|
|
220
|
+
if not isinstance(params, dict):
|
|
221
|
+
return _jsonrpc_error(req_id, -32600, "Invalid Request: params must be an object")
|
|
208
222
|
|
|
209
223
|
if method == "initialize":
|
|
210
224
|
result = {
|
|
@@ -228,6 +242,12 @@ def _handle_request(request: dict) -> dict | None:
|
|
|
228
242
|
elif method == "tools/call":
|
|
229
243
|
tool_name = params.get("name", "")
|
|
230
244
|
tool_args = params.get("arguments", {})
|
|
245
|
+
if tool_args is None:
|
|
246
|
+
tool_args = {}
|
|
247
|
+
if not isinstance(tool_name, str) or not isinstance(tool_args, dict):
|
|
248
|
+
return _jsonrpc_error(
|
|
249
|
+
req_id, -32602, "Invalid params: name must be a string and arguments an object"
|
|
250
|
+
)
|
|
231
251
|
|
|
232
252
|
handler = TOOL_HANDLERS.get(tool_name)
|
|
233
253
|
if handler is None:
|
|
@@ -270,22 +290,37 @@ def run_stdio() -> None:
|
|
|
270
290
|
sys.stdout.flush()
|
|
271
291
|
|
|
272
292
|
|
|
273
|
-
def main() -> None:
|
|
274
|
-
"""Entry point
|
|
293
|
+
def main(argv: list[str] | None = None) -> None:
|
|
294
|
+
"""Entry point of the ``sdcvalidator`` and ``sdcvalidator-mcp`` console scripts.
|
|
295
|
+
|
|
296
|
+
Three spellings start the stdio server: no arguments at all (what an MCP
|
|
297
|
+
registry client runs: ``uvx sdcvalidator``), ``mcp`` (the spelling the other
|
|
298
|
+
Semantic Data Charter servers use) and ``serve --mcp`` (kept for existing
|
|
299
|
+
client configurations). Until 4.5.3 the bare command printed this help and
|
|
300
|
+
exited, so the registry entry never started a server (issue #15).
|
|
301
|
+
"""
|
|
275
302
|
import argparse
|
|
276
303
|
|
|
277
304
|
parser = argparse.ArgumentParser(
|
|
278
|
-
prog="sdcvalidator
|
|
279
|
-
description="SDC4 structural validator - MCP server",
|
|
305
|
+
prog="sdcvalidator",
|
|
306
|
+
description="SDC4 structural validator - MCP stdio server",
|
|
307
|
+
epilog="With no command the MCP server starts, reading JSON-RPC on stdin.",
|
|
280
308
|
)
|
|
281
309
|
subparsers = parser.add_subparsers(dest="command")
|
|
282
310
|
|
|
311
|
+
subparsers.add_parser("mcp", help="Run the MCP stdio server")
|
|
283
312
|
serve_parser = subparsers.add_parser("serve", help="Start the validation server")
|
|
284
313
|
serve_parser.add_argument("--mcp", action="store_true", help="Run as MCP stdio server")
|
|
285
314
|
|
|
286
|
-
args = parser.parse_args()
|
|
315
|
+
args = parser.parse_args(argv)
|
|
287
316
|
|
|
288
|
-
if args.command == "serve" and args.mcp:
|
|
317
|
+
if args.command is None or args.command == "mcp" or (args.command == "serve" and args.mcp):
|
|
318
|
+
if args.command is None and sys.stdin.isatty():
|
|
319
|
+
sys.stderr.write(
|
|
320
|
+
"sdcvalidator: MCP server reading JSON-RPC 2.0 on stdin "
|
|
321
|
+
"(sdcvalidator --help for options, Ctrl-D to stop)\n"
|
|
322
|
+
)
|
|
323
|
+
sys.stderr.flush()
|
|
289
324
|
run_stdio()
|
|
290
325
|
else:
|
|
291
326
|
parser.print_help()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdcvalidator
|
|
3
|
-
Version: 4.5.
|
|
3
|
+
Version: 4.5.3
|
|
4
4
|
Summary: SDC4 structural validator with ExceptionalValue recovery — thin wrapper over xmlschema
|
|
5
5
|
Author: Axius SDC, Inc.
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -151,9 +151,13 @@ sdcvalidator-json2xml data.json schema.xsd -o output.xml
|
|
|
151
151
|
`sdcvalidator` ships a stdio MCP (Model Context Protocol) server so any MCP-capable agent can validate SDC4 data without importing the Python library. It implements JSON-RPC 2.0 directly over stdio — no external MCP SDK dependency.
|
|
152
152
|
|
|
153
153
|
```bash
|
|
154
|
-
sdcvalidator
|
|
154
|
+
sdcvalidator # what an MCP registry client runs (uvx sdcvalidator)
|
|
155
|
+
sdcvalidator mcp # the same, spelled like the other Semantic Data Charter servers
|
|
156
|
+
sdcvalidator-mcp serve --mcp # still works for existing client configurations
|
|
155
157
|
```
|
|
156
158
|
|
|
159
|
+
A client configuration needs only `"command": "uvx", "args": ["sdcvalidator"]`.
|
|
160
|
+
|
|
157
161
|
### Tools
|
|
158
162
|
|
|
159
163
|
| Tool | Purpose |
|
|
@@ -137,3 +137,55 @@ class TestConsistentSerialization:
|
|
|
137
137
|
assert content[0]["type"] == "text"
|
|
138
138
|
parsed = json.loads(content[0]["text"])
|
|
139
139
|
assert parsed is not None
|
|
140
|
+
|
|
141
|
+
class TestMalformedRequestsDoNotKillTheServer:
|
|
142
|
+
"""
|
|
143
|
+
VSL rollout inventory R9. Six inputs that are valid JSON but not a
|
|
144
|
+
JSON-RPC request raised AttributeError in _handle_request and ended the
|
|
145
|
+
process. Each is now answered with -32600 (or -32602 for bad tool params)
|
|
146
|
+
and the loop keeps reading.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
@pytest.mark.parametrize("payload", [[], ["tools/list"], "tools/list", 7, None, True])
|
|
150
|
+
def test_a_non_object_request_is_invalid_request(self, payload):
|
|
151
|
+
response = _handle_request(payload)
|
|
152
|
+
assert response["error"]["code"] == -32600
|
|
153
|
+
assert response["id"] is None
|
|
154
|
+
|
|
155
|
+
@pytest.mark.parametrize("params", [[], "x", 3, True])
|
|
156
|
+
def test_non_object_params_are_invalid_request(self, params):
|
|
157
|
+
response = _handle_request({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": params})
|
|
158
|
+
assert response["error"]["code"] == -32600
|
|
159
|
+
assert response["id"] == 1
|
|
160
|
+
|
|
161
|
+
def test_null_params_are_treated_as_empty(self):
|
|
162
|
+
response = _handle_request({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": None})
|
|
163
|
+
assert "result" in response
|
|
164
|
+
|
|
165
|
+
def test_a_non_string_method_is_invalid_request(self):
|
|
166
|
+
response = _handle_request({"jsonrpc": "2.0", "id": 1, "method": ["tools/list"]})
|
|
167
|
+
assert response["error"]["code"] == -32600
|
|
168
|
+
|
|
169
|
+
@pytest.mark.parametrize("arguments", [[], "x", 3])
|
|
170
|
+
def test_non_object_tool_arguments_are_invalid_params(self, arguments):
|
|
171
|
+
response = _handle_request({
|
|
172
|
+
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
|
173
|
+
"params": {"name": list(TOOL_HANDLERS)[0], "arguments": arguments},
|
|
174
|
+
})
|
|
175
|
+
assert response["error"]["code"] == -32602
|
|
176
|
+
|
|
177
|
+
def test_the_stdio_loop_survives_every_one_of_them(self, monkeypatch, capsys):
|
|
178
|
+
import io
|
|
179
|
+
import sys
|
|
180
|
+
|
|
181
|
+
from sdcvalidator.mcp_server import run_stdio
|
|
182
|
+
|
|
183
|
+
lines = ["[]", "\"tools/list\"", "7", "null",
|
|
184
|
+
json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": []}),
|
|
185
|
+
json.dumps({"jsonrpc": "2.0", "id": 2, "method": "ping"})]
|
|
186
|
+
monkeypatch.setattr(sys, "stdin", io.StringIO("\n".join(lines) + "\n"))
|
|
187
|
+
run_stdio()
|
|
188
|
+
out = [json.loads(l) for l in capsys.readouterr().out.strip().splitlines()]
|
|
189
|
+
assert len(out) == 6
|
|
190
|
+
assert [o.get("error", {}).get("code") for o in out[:5]] == [-32600] * 5
|
|
191
|
+
assert out[5] == {"jsonrpc": "2.0", "id": 2, "result": {}}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The MCP registry entry starts the server (issue #15).
|
|
3
|
+
|
|
4
|
+
``server.json`` declared no ``packageArguments``, so ``uvx sdcvalidator`` ran
|
|
5
|
+
``main()`` with no arguments, which printed the CLI help and exited. These
|
|
6
|
+
tests start the console script's entry point the way a registry client, a
|
|
7
|
+
hand-written configuration and the pre-4.5.3 configuration do, and check that
|
|
8
|
+
``server.json`` names the released version.
|
|
9
|
+
"""
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
import sdcvalidator
|
|
19
|
+
from sdcvalidator.mcp_server import SERVER_VERSION
|
|
20
|
+
|
|
21
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
22
|
+
SERVER_JSON = json.loads((ROOT / "server.json").read_text(encoding="utf-8"))
|
|
23
|
+
|
|
24
|
+
INITIALIZE = {"jsonrpc": "2.0", "id": 1, "method": "initialize",
|
|
25
|
+
"params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "t", "version": "0"}}}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _launch(args):
|
|
29
|
+
proc = subprocess.run(
|
|
30
|
+
[sys.executable, "-m", "sdcvalidator.mcp_server", *args],
|
|
31
|
+
input=json.dumps(INITIALIZE) + "\n",
|
|
32
|
+
capture_output=True, text=True, timeout=60,
|
|
33
|
+
)
|
|
34
|
+
return proc
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _registry_args():
|
|
38
|
+
package = SERVER_JSON["packages"][0]
|
|
39
|
+
return [a["value"] for a in package.get("packageArguments", []) if a["type"] == "positional"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.parametrize("args", [
|
|
43
|
+
pytest.param([], id="bare, what a registry client without packageArguments runs"),
|
|
44
|
+
pytest.param(_registry_args(), id="server.json packageArguments"),
|
|
45
|
+
pytest.param(["mcp"], id="mcp"),
|
|
46
|
+
pytest.param(["serve", "--mcp"], id="serve --mcp (pre-4.5.3 configurations)"),
|
|
47
|
+
])
|
|
48
|
+
def test_each_launch_spelling_answers_initialize(args):
|
|
49
|
+
proc = _launch(args)
|
|
50
|
+
assert proc.returncode == 0, proc.stderr
|
|
51
|
+
lines = [line for line in proc.stdout.splitlines() if line.strip()]
|
|
52
|
+
assert lines, f"no JSON-RPC response; stdout was help text?\n{proc.stdout}"
|
|
53
|
+
response = json.loads(lines[0])
|
|
54
|
+
assert response["id"] == 1
|
|
55
|
+
assert response["result"]["serverInfo"]["name"] == "sdcvalidator"
|
|
56
|
+
assert response["result"]["serverInfo"]["version"] == SERVER_VERSION
|
|
57
|
+
assert "usage:" not in proc.stdout
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_serve_without_mcp_still_prints_help():
|
|
61
|
+
proc = _launch(["serve"])
|
|
62
|
+
assert proc.returncode == 0
|
|
63
|
+
assert "usage:" in proc.stdout
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_server_json_declares_the_mcp_launch():
|
|
67
|
+
package = SERVER_JSON["packages"][0]
|
|
68
|
+
assert package["identifier"] == "sdcvalidator"
|
|
69
|
+
assert package["runtimeHint"] == "uvx"
|
|
70
|
+
assert _registry_args() == ["mcp"]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_server_json_names_the_released_version():
|
|
74
|
+
# A regex, not tomllib: the test matrix includes Python 3.10.
|
|
75
|
+
pyproject = re.search(r'^version = "([^"]+)"', (ROOT / "pyproject.toml").read_text(encoding="utf-8"), re.M).group(1)
|
|
76
|
+
assert SERVER_JSON["version"] == pyproject
|
|
77
|
+
assert SERVER_JSON["packages"][0]["version"] == pyproject
|
|
78
|
+
assert sdcvalidator.__version__ == pyproject
|
|
79
|
+
assert SERVER_VERSION == pyproject
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|