diffx-python 0.5.2__py3-none-win_amd64.whl
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.
- diffx/__init__.py +46 -0
- diffx/compat.py +56 -0
- diffx/diffx.py +293 -0
- diffx_python-0.5.2.data/scripts/diffx.exe +0 -0
- diffx_python-0.5.2.dist-info/METADATA +127 -0
- diffx_python-0.5.2.dist-info/RECORD +7 -0
- diffx_python-0.5.2.dist-info/WHEEL +4 -0
diffx/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
diffx: Python wrapper for the diffx CLI tool
|
|
3
|
+
|
|
4
|
+
This package provides a Python interface to the diffx CLI tool for semantic
|
|
5
|
+
diffing of structured data formats like JSON, YAML, TOML, XML, INI, and CSV.
|
|
6
|
+
|
|
7
|
+
The diffx binary is embedded in the wheel for offline installation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .diffx import (
|
|
11
|
+
diff,
|
|
12
|
+
diff_string,
|
|
13
|
+
is_diffx_available,
|
|
14
|
+
DiffOptions,
|
|
15
|
+
DiffResult,
|
|
16
|
+
DiffError,
|
|
17
|
+
Format,
|
|
18
|
+
OutputFormat,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# For backward compatibility with existing diffx_python users
|
|
22
|
+
from .compat import run_diffx
|
|
23
|
+
|
|
24
|
+
# Version is now managed dynamically from pyproject.toml
|
|
25
|
+
# This prevents hardcoded version mismatches during releases
|
|
26
|
+
try:
|
|
27
|
+
from importlib.metadata import version
|
|
28
|
+
__version__ = version("diffx-python")
|
|
29
|
+
except ImportError:
|
|
30
|
+
# Fallback for Python < 3.8
|
|
31
|
+
try:
|
|
32
|
+
import pkg_resources
|
|
33
|
+
__version__ = pkg_resources.get_distribution("diffx-python").version
|
|
34
|
+
except Exception:
|
|
35
|
+
__version__ = "unknown"
|
|
36
|
+
__all__ = [
|
|
37
|
+
"diff",
|
|
38
|
+
"diff_string",
|
|
39
|
+
"is_diffx_available",
|
|
40
|
+
"DiffOptions",
|
|
41
|
+
"DiffResult",
|
|
42
|
+
"DiffError",
|
|
43
|
+
"Format",
|
|
44
|
+
"OutputFormat",
|
|
45
|
+
"run_diffx", # Backward compatibility
|
|
46
|
+
]
|
diffx/compat.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backward compatibility layer for existing diffx_python users
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import platform
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run_diffx(args):
|
|
13
|
+
"""
|
|
14
|
+
Run diffx command with given arguments (backward compatibility)
|
|
15
|
+
|
|
16
|
+
This function maintains compatibility with the original diffx_python API.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
args: List of command line arguments for diffx
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
subprocess.CompletedProcess object with stdout, stderr, and returncode
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
>>> result = run_diffx(["file1.json", "file2.json"])
|
|
26
|
+
>>> print(result.stdout)
|
|
27
|
+
"""
|
|
28
|
+
# Determine the path to the diffx binary
|
|
29
|
+
package_dir = Path(__file__).parent.parent.parent
|
|
30
|
+
binary_name = "diffx.exe" if platform.system() == "Windows" else "diffx"
|
|
31
|
+
diffx_binary_path = package_dir / "bin" / binary_name
|
|
32
|
+
|
|
33
|
+
# Fall back to system PATH if local binary doesn't exist
|
|
34
|
+
if not diffx_binary_path.exists():
|
|
35
|
+
diffx_binary_path = "diffx"
|
|
36
|
+
|
|
37
|
+
command = [str(diffx_binary_path)] + args
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
result = subprocess.run(command, capture_output=True, text=True, check=False)
|
|
41
|
+
|
|
42
|
+
if result.returncode != 0 and result.stderr:
|
|
43
|
+
print(f"Error running diffx: {result.stderr}", file=sys.stderr)
|
|
44
|
+
|
|
45
|
+
return result
|
|
46
|
+
except FileNotFoundError:
|
|
47
|
+
# Create a mock result object for consistency
|
|
48
|
+
class MockResult:
|
|
49
|
+
def __init__(self):
|
|
50
|
+
self.stdout = ""
|
|
51
|
+
self.stderr = "diffx binary not found. Please ensure the package is installed correctly."
|
|
52
|
+
self.returncode = -1
|
|
53
|
+
|
|
54
|
+
result = MockResult()
|
|
55
|
+
print(f"Error: {result.stderr}", file=sys.stderr)
|
|
56
|
+
return result
|
diffx/diffx.py
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main diffx wrapper implementation
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Union, List, Dict, Any, Optional, Literal
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Type definitions
|
|
16
|
+
Format = Literal["json", "yaml", "toml", "xml", "ini", "csv"]
|
|
17
|
+
OutputFormat = Literal["cli", "json", "yaml", "unified"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class DiffOptions:
|
|
22
|
+
"""Options for the diff operation"""
|
|
23
|
+
format: Optional[Format] = None
|
|
24
|
+
output: Optional[OutputFormat] = None
|
|
25
|
+
recursive: bool = False
|
|
26
|
+
path: Optional[str] = None
|
|
27
|
+
ignore_keys_regex: Optional[str] = None
|
|
28
|
+
epsilon: Optional[float] = None
|
|
29
|
+
array_id_key: Optional[str] = None
|
|
30
|
+
context: Optional[int] = None
|
|
31
|
+
ignore_whitespace: bool = False
|
|
32
|
+
ignore_case: bool = False
|
|
33
|
+
quiet: bool = False
|
|
34
|
+
brief: bool = False
|
|
35
|
+
debug: bool = False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DiffResult:
|
|
39
|
+
"""Result of a diff operation when output format is 'json'"""
|
|
40
|
+
def __init__(self, data: Dict[str, Any]):
|
|
41
|
+
self.data = data
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def added(self) -> Optional[tuple]:
|
|
45
|
+
"""Get Added result if present"""
|
|
46
|
+
return tuple(self.data["Added"]) if "Added" in self.data else None
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def removed(self) -> Optional[tuple]:
|
|
50
|
+
"""Get Removed result if present"""
|
|
51
|
+
return tuple(self.data["Removed"]) if "Removed" in self.data else None
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def modified(self) -> Optional[tuple]:
|
|
55
|
+
"""Get Modified result if present"""
|
|
56
|
+
return tuple(self.data["Modified"]) if "Modified" in self.data else None
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def type_changed(self) -> Optional[tuple]:
|
|
60
|
+
"""Get TypeChanged result if present"""
|
|
61
|
+
return tuple(self.data["TypeChanged"]) if "TypeChanged" in self.data else None
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
return f"DiffResult({self.data})"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DiffError(Exception):
|
|
68
|
+
"""Error thrown when diffx command fails"""
|
|
69
|
+
def __init__(self, message: str, exit_code: int, stderr: str):
|
|
70
|
+
super().__init__(message)
|
|
71
|
+
self.exit_code = exit_code
|
|
72
|
+
self.stderr = stderr
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _get_diffx_binary_path() -> str:
|
|
76
|
+
"""Get the path to the diffx binary embedded in the wheel"""
|
|
77
|
+
import sys
|
|
78
|
+
binary_name = "diffx.exe" if platform.system() == "Windows" else "diffx"
|
|
79
|
+
|
|
80
|
+
# For maturin wheel with bindings = "bin", binary is installed in Scripts/bin
|
|
81
|
+
# Check the Python environment's Scripts/bin directory first
|
|
82
|
+
if hasattr(sys, 'prefix'):
|
|
83
|
+
env_scripts_paths = [
|
|
84
|
+
Path(sys.prefix) / "Scripts" / binary_name, # Windows
|
|
85
|
+
Path(sys.prefix) / "bin" / binary_name, # Unix
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
for path in env_scripts_paths:
|
|
89
|
+
if path.exists():
|
|
90
|
+
return str(path)
|
|
91
|
+
|
|
92
|
+
# PyInstaller case
|
|
93
|
+
if hasattr(sys, '_MEIPASS'):
|
|
94
|
+
wheel_binary_path = Path(sys._MEIPASS) / binary_name
|
|
95
|
+
if wheel_binary_path.exists():
|
|
96
|
+
return str(wheel_binary_path)
|
|
97
|
+
|
|
98
|
+
# Fall back to system PATH (for development)
|
|
99
|
+
return "diffx"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _execute_diffx(args: List[str]) -> tuple[str, str]:
|
|
103
|
+
"""Execute diffx command and return stdout, stderr"""
|
|
104
|
+
diffx_path = _get_diffx_binary_path()
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
result = subprocess.run(
|
|
108
|
+
[diffx_path] + args,
|
|
109
|
+
capture_output=True,
|
|
110
|
+
text=True,
|
|
111
|
+
check=False # Don't raise exception on non-zero exit
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Exit codes:
|
|
115
|
+
# 0 = No differences found
|
|
116
|
+
# 1 = Differences found (normal diff result)
|
|
117
|
+
# 2+ = Error conditions
|
|
118
|
+
if result.returncode in (0, 1):
|
|
119
|
+
return result.stdout, result.stderr
|
|
120
|
+
else:
|
|
121
|
+
raise DiffError(
|
|
122
|
+
f"diffx exited with code {result.returncode}",
|
|
123
|
+
result.returncode,
|
|
124
|
+
result.stderr or ""
|
|
125
|
+
)
|
|
126
|
+
except FileNotFoundError:
|
|
127
|
+
raise DiffError(
|
|
128
|
+
"diffx command not found. Please install diffx CLI tool.",
|
|
129
|
+
-1,
|
|
130
|
+
""
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def diff(
|
|
135
|
+
input1: str,
|
|
136
|
+
input2: str,
|
|
137
|
+
options: Optional[DiffOptions] = None
|
|
138
|
+
) -> Union[str, List[DiffResult]]:
|
|
139
|
+
"""
|
|
140
|
+
Compare two files or directories using diffx
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
input1: Path to first file/directory or '-' for stdin
|
|
144
|
+
input2: Path to second file/directory
|
|
145
|
+
options: Comparison options
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
String output for CLI format, or list of DiffResult for JSON format
|
|
149
|
+
|
|
150
|
+
Examples:
|
|
151
|
+
>>> result = diff('file1.json', 'file2.json')
|
|
152
|
+
>>> print(result)
|
|
153
|
+
|
|
154
|
+
>>> json_result = diff('config1.yaml', 'config2.yaml',
|
|
155
|
+
... DiffOptions(format='yaml', output='json'))
|
|
156
|
+
>>> for diff_item in json_result:
|
|
157
|
+
... print(diff_item)
|
|
158
|
+
|
|
159
|
+
>>> dir_result = diff('dir1/', 'dir2/',
|
|
160
|
+
... DiffOptions(recursive=True, path='config'))
|
|
161
|
+
"""
|
|
162
|
+
if options is None:
|
|
163
|
+
options = DiffOptions()
|
|
164
|
+
|
|
165
|
+
args = [input1, input2]
|
|
166
|
+
|
|
167
|
+
# Add format option
|
|
168
|
+
if options.format:
|
|
169
|
+
args.extend(["--format", options.format])
|
|
170
|
+
|
|
171
|
+
# Add output format option
|
|
172
|
+
if options.output:
|
|
173
|
+
args.extend(["--output", options.output])
|
|
174
|
+
|
|
175
|
+
# Add recursive option
|
|
176
|
+
if options.recursive:
|
|
177
|
+
args.append("--recursive")
|
|
178
|
+
|
|
179
|
+
# Add path filter option
|
|
180
|
+
if options.path:
|
|
181
|
+
args.extend(["--path", options.path])
|
|
182
|
+
|
|
183
|
+
# Add ignore keys regex option
|
|
184
|
+
if options.ignore_keys_regex:
|
|
185
|
+
args.extend(["--ignore-keys-regex", options.ignore_keys_regex])
|
|
186
|
+
|
|
187
|
+
# Add epsilon option
|
|
188
|
+
if options.epsilon is not None:
|
|
189
|
+
args.extend(["--epsilon", str(options.epsilon)])
|
|
190
|
+
|
|
191
|
+
# Add array ID key option
|
|
192
|
+
if options.array_id_key:
|
|
193
|
+
args.extend(["--array-id-key", options.array_id_key])
|
|
194
|
+
|
|
195
|
+
# Add context option
|
|
196
|
+
if options.context is not None:
|
|
197
|
+
args.extend(["--context", str(options.context)])
|
|
198
|
+
|
|
199
|
+
# Add ignore whitespace option
|
|
200
|
+
if options.ignore_whitespace:
|
|
201
|
+
args.append("--ignore-whitespace")
|
|
202
|
+
|
|
203
|
+
# Add ignore case option
|
|
204
|
+
if options.ignore_case:
|
|
205
|
+
args.append("--ignore-case")
|
|
206
|
+
|
|
207
|
+
# Add quiet option
|
|
208
|
+
if options.quiet:
|
|
209
|
+
args.append("--quiet")
|
|
210
|
+
|
|
211
|
+
# Add brief option
|
|
212
|
+
if options.brief:
|
|
213
|
+
args.append("--brief")
|
|
214
|
+
|
|
215
|
+
# Add debug option
|
|
216
|
+
if options.debug:
|
|
217
|
+
args.append("--debug")
|
|
218
|
+
|
|
219
|
+
stdout, stderr = _execute_diffx(args)
|
|
220
|
+
|
|
221
|
+
# If output format is JSON, parse the result
|
|
222
|
+
if options.output == "json":
|
|
223
|
+
try:
|
|
224
|
+
json_data = json.loads(stdout)
|
|
225
|
+
return [DiffResult(item) for item in json_data]
|
|
226
|
+
except json.JSONDecodeError as e:
|
|
227
|
+
raise DiffError(f"Failed to parse JSON output: {e}", -1, "")
|
|
228
|
+
|
|
229
|
+
# Return raw output for other formats
|
|
230
|
+
return stdout
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def diff_string(
|
|
234
|
+
content1: str,
|
|
235
|
+
content2: str,
|
|
236
|
+
format: Format,
|
|
237
|
+
options: Optional[DiffOptions] = None
|
|
238
|
+
) -> Union[str, List[DiffResult]]:
|
|
239
|
+
"""
|
|
240
|
+
Compare two strings directly (writes to temporary files)
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
content1: First content string
|
|
244
|
+
content2: Second content string
|
|
245
|
+
format: Content format
|
|
246
|
+
options: Comparison options
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
String output for CLI format, or list of DiffResult for JSON format
|
|
250
|
+
|
|
251
|
+
Examples:
|
|
252
|
+
>>> json1 = '{"name": "Alice", "age": 30}'
|
|
253
|
+
>>> json2 = '{"name": "Alice", "age": 31}'
|
|
254
|
+
>>> result = diff_string(json1, json2, 'json',
|
|
255
|
+
... DiffOptions(output='json'))
|
|
256
|
+
>>> print(result)
|
|
257
|
+
"""
|
|
258
|
+
if options is None:
|
|
259
|
+
options = DiffOptions()
|
|
260
|
+
|
|
261
|
+
# Ensure format is set
|
|
262
|
+
options.format = format
|
|
263
|
+
|
|
264
|
+
# Create temporary files
|
|
265
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
266
|
+
tmp_file1 = Path(tmp_dir) / f"file1.{format}"
|
|
267
|
+
tmp_file2 = Path(tmp_dir) / f"file2.{format}"
|
|
268
|
+
|
|
269
|
+
# Write content to temporary files
|
|
270
|
+
tmp_file1.write_text(content1, encoding="utf-8")
|
|
271
|
+
tmp_file2.write_text(content2, encoding="utf-8")
|
|
272
|
+
|
|
273
|
+
# Perform diff
|
|
274
|
+
return diff(str(tmp_file1), str(tmp_file2), options)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def is_diffx_available() -> bool:
|
|
278
|
+
"""
|
|
279
|
+
Check if diffx command is available in the system
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
True if diffx is available, False otherwise
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
>>> if not is_diffx_available():
|
|
286
|
+
... print("Please install diffx CLI tool")
|
|
287
|
+
... exit(1)
|
|
288
|
+
"""
|
|
289
|
+
try:
|
|
290
|
+
_execute_diffx(["--version"])
|
|
291
|
+
return True
|
|
292
|
+
except DiffError:
|
|
293
|
+
return False
|
|
Binary file
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: diffx-python
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Classifier: Topic :: Text Processing
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Dist: pytest>=6.0 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
19
|
+
Requires-Dist: black ; extra == 'dev'
|
|
20
|
+
Requires-Dist: isort ; extra == 'dev'
|
|
21
|
+
Requires-Dist: mypy ; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff ; extra == 'dev'
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Summary: Python wrapper for diffx - semantic diffing of JSON, YAML, TOML, XML, INI, and CSV files. Focuses on structural meaning rather than formatting.
|
|
25
|
+
Keywords: diff,semantic,json,yaml,toml,xml,ini,csv,structured-data,comparison,devops,ci-cd,automation,data-analysis,configuration
|
|
26
|
+
Author: kako-jun
|
|
27
|
+
License: MIT
|
|
28
|
+
Requires-Python: >=3.8
|
|
29
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
30
|
+
Project-URL: Homepage, https://github.com/kako-jun/diffx
|
|
31
|
+
Project-URL: Repository, https://github.com/kako-jun/diffx
|
|
32
|
+
Project-URL: Issues, https://github.com/kako-jun/diffx/issues
|
|
33
|
+
Project-URL: Documentation, https://github.com/kako-jun/diffx/tree/main/docs
|
|
34
|
+
|
|
35
|
+
# diffx-python
|
|
36
|
+
|
|
37
|
+
Python wrapper for the `diffx` CLI tool - semantic diff for structured data.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install diffx-python
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The `diffx` binary is automatically included in the wheel - no additional downloads required! This package uses [maturin](https://github.com/PyO3/maturin) to embed the native binary directly in the Python wheel, similar to tools like `ruff`.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import diffx
|
|
51
|
+
|
|
52
|
+
# Compare two JSON files
|
|
53
|
+
result = diffx.diff('file1.json', 'file2.json')
|
|
54
|
+
print(result)
|
|
55
|
+
|
|
56
|
+
# Get structured output as JSON
|
|
57
|
+
json_result = diffx.diff(
|
|
58
|
+
'config1.yaml',
|
|
59
|
+
'config2.yaml',
|
|
60
|
+
diffx.DiffOptions(format='yaml', output='json')
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
for diff_item in json_result:
|
|
64
|
+
if diff_item.added:
|
|
65
|
+
print(f"Added: {diff_item.added}")
|
|
66
|
+
elif diff_item.modified:
|
|
67
|
+
print(f"Modified: {diff_item.modified}")
|
|
68
|
+
|
|
69
|
+
# Compare directory trees
|
|
70
|
+
dir_result = diffx.diff(
|
|
71
|
+
'dir1/',
|
|
72
|
+
'dir2/',
|
|
73
|
+
diffx.DiffOptions(recursive=True, path='config')
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Compare strings directly
|
|
77
|
+
json1 = '{"name": "Alice", "age": 30}'
|
|
78
|
+
json2 = '{"name": "Alice", "age": 31}'
|
|
79
|
+
string_result = diffx.diff_string(
|
|
80
|
+
json1, json2, 'json',
|
|
81
|
+
diffx.DiffOptions(output='json')
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## Features
|
|
87
|
+
|
|
88
|
+
- **Multiple formats**: JSON, YAML, TOML, XML, INI, CSV
|
|
89
|
+
- **Smart diffing**: Understands structure, not just text
|
|
90
|
+
- **Flexible output**: CLI, JSON, YAML, unified diff formats
|
|
91
|
+
- **Advanced options**:
|
|
92
|
+
- Regex-based key filtering
|
|
93
|
+
- Floating-point tolerance
|
|
94
|
+
- Array element identification
|
|
95
|
+
- Path-based filtering
|
|
96
|
+
- **Cross-platform**: Native binary embedded in platform-specific wheels
|
|
97
|
+
|
|
98
|
+
## Key Benefits
|
|
99
|
+
|
|
100
|
+
- **🚀 Zero setup**: No external downloads or binary management
|
|
101
|
+
- **📦 Self-contained**: Everything needed is in the wheel
|
|
102
|
+
- **⚡ Fast installation**: No network dependencies after `pip install`
|
|
103
|
+
- **🔒 Secure**: No runtime downloads from external sources
|
|
104
|
+
- **🌐 Offline-ready**: Works in air-gapped environments
|
|
105
|
+
|
|
106
|
+
## Development
|
|
107
|
+
|
|
108
|
+
To install in development mode:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install -e .[dev]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Verification
|
|
115
|
+
|
|
116
|
+
Verify the installation:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import diffx
|
|
120
|
+
print("diffx available:", diffx.is_diffx_available())
|
|
121
|
+
print("Version:", diffx.__version__)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
This project is licensed under the MIT License.
|
|
127
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
diffx/__init__.py,sha256=UqUxAz-5h4Wfp_PO9Qfe-pbxBvYxx5PXIL8bd6p-o_Y,1200
|
|
2
|
+
diffx/compat.py,sha256=tvL1YMBKIHRCFzJMNuIvhO5zOHuXdX4IIRoKmVPoto0,1806
|
|
3
|
+
diffx/diffx.py,sha256=b4WMgcBrgmar4zo08KDb2ZdXsyjWgTZXOc813HlgHYc,8853
|
|
4
|
+
diffx_python-0.5.2.data/scripts/diffx.exe,sha256=o2SAoSq95J3kewOdlBmS_x_ZkQIUjAxgFlaR1opGE1A,3560960
|
|
5
|
+
diffx_python-0.5.2.dist-info/METADATA,sha256=skeY5kKErOUy0NWPzr5if3SU6xKkJeI9_y6y6yqZu7M,3935
|
|
6
|
+
diffx_python-0.5.2.dist-info/WHEEL,sha256=T1-x9ZAB-aE3ewIGbYuockW5ywV7fI-Nla9FsiR1vW4,93
|
|
7
|
+
diffx_python-0.5.2.dist-info/RECORD,,
|