dir-content-diff 1.6.0__tar.gz → 1.8.0__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.
Files changed (62) hide show
  1. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.copier-answers.yml +1 -1
  2. dir-content-diff-1.8.0/.coveragerc_py38 +11 -0
  3. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/workflows/commitlint.yml +1 -1
  4. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/workflows/run-tox.yml +2 -1
  5. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.pylintrc +1 -0
  6. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/CHANGELOG.md +17 -0
  7. {dir-content-diff-1.6.0/dir_content_diff.egg-info → dir-content-diff-1.8.0}/PKG-INFO +19 -2
  8. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/README.md +12 -0
  9. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff/__init__.py +25 -2
  10. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff/base_comparators.py +85 -4
  11. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff/pandas.py +39 -0
  12. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0/dir_content_diff.egg-info}/PKG-INFO +19 -2
  13. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff.egg-info/SOURCES.txt +1 -0
  14. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff.egg-info/requires.txt +6 -1
  15. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/setup.py +4 -1
  16. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/conftest.py +6 -0
  17. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/test_base.py +187 -14
  18. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/test_pandas.py +211 -26
  19. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/test_pytest_plugin.py +22 -15
  20. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tox.ini +1 -0
  21. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.auto-changelog +0 -0
  22. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.auto-changelog-template.hbs +0 -0
  23. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.codespellignorelines +0 -0
  24. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.codespellrc +0 -0
  25. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.coveragerc +0 -0
  26. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.flake8 +0 -0
  27. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.gitattributes +0 -0
  28. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  29. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  30. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  31. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/ISSUE_TEMPLATE/how_to_use.yaml +0 -0
  32. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/dependabot.yml +0 -0
  33. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/pull_request_template.md +0 -0
  34. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.github/workflows/publish-sdist.yml +0 -0
  35. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.gitignore +0 -0
  36. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.gitreview +0 -0
  37. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.pre-commit-config.yaml +0 -0
  38. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/.readthedocs.yml +0 -0
  39. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/AUTHORS.md +0 -0
  40. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/CONTRIBUTING.md +0 -0
  41. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/LICENSE.txt +0 -0
  42. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/MANIFEST.in +0 -0
  43. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/codecov.yml +0 -0
  44. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/commitlint.config.js +0 -0
  45. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff/pytest_plugin.py +0 -0
  46. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff/util.py +0 -0
  47. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff.egg-info/dependency_links.txt +0 -0
  48. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff.egg-info/entry_points.txt +0 -0
  49. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/dir_content_diff.egg-info/top_level.txt +0 -0
  50. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/Makefile +0 -0
  51. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/source/.gitignore +0 -0
  52. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/source/api_ref.rst +0 -0
  53. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/source/changelog.rst +0 -0
  54. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/source/conf.py +0 -0
  55. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/source/contributing.rst +0 -0
  56. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/docs/source/index.rst +0 -0
  57. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/package.json +0 -0
  58. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/pyproject.toml +0 -0
  59. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/setup.cfg +0 -0
  60. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/__init__.py +0 -0
  61. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/generate_test_files.py +0 -0
  62. {dir-content-diff-1.6.0 → dir-content-diff-1.8.0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  # Changes here will be overwritten by Copier
2
2
 
3
- _commit: 0.1.53
3
+ _commit: 0.1.54
4
4
  _src_path: git@bbpgitlab.epfl.ch:neuromath/python-template.git
5
5
  author_email: ''
6
6
  author_name: Blue Brain Project, EPFL
@@ -0,0 +1,11 @@
1
+ [paths]
2
+ source_paths =
3
+ dir_content_diff
4
+ */site-packages/dir_content_diff
5
+ */dir_content_diff/dir_content_diff
6
+ */dir-content-diff/dir_content_diff
7
+
8
+ [report]
9
+ exclude_also =
10
+ return pd\.read_feather\(path, \*\*kwargs\)
11
+ data\.to_feather\(path, \*\*kwargs\)
@@ -12,7 +12,7 @@ jobs:
12
12
  - uses: actions/checkout@v4
13
13
  - uses: actions/setup-node@v4
14
14
  with:
15
- node-version: '16'
15
+ node-version: '20'
16
16
  - run: npm install -g --force commitlint @commitlint/cli commitlint-plugin-cleanfeet
17
17
  - run: npm install conventional-changelog-conventionalcommits
18
18
  - run: touch .git/COMMIT_EDITMSG
@@ -64,12 +64,13 @@ jobs:
64
64
  with:
65
65
  report_paths: 'reports/pytest-*.xml'
66
66
  - name: Upload to codecov
67
- uses: codecov/codecov-action@v3
67
+ uses: codecov/codecov-action@v4
68
68
  with:
69
69
  fail_ci_if_error: false
70
70
  directory: ./reports
71
71
  flags: pytest
72
72
  name: "dir-content-diff-py${{ matrix.python-version }}-${{ matrix.min_versions }}"
73
+ token: ${{ secrets.CODECOV_TOKEN }}
73
74
  - name: Upload test artifacts
74
75
  uses: actions/upload-artifact@v4
75
76
  if: always()
@@ -2,6 +2,7 @@
2
2
  disable=
3
3
  arguments-differ,
4
4
  invalid-name,
5
+ too-many-lines,
5
6
 
6
7
  [FORMAT]
7
8
  # Regexp for a line that is allowed to be longer than the limit.
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.0](https://github.com/BlueBrain/dir-content-diff/compare/1.7.0..1.8.0)
4
+
5
+ > 8 April 2024
6
+
7
+ ### New Features
8
+
9
+ - Add new comparators for Feather, Parquet and Stata files (Adrien Berchet - [#56](https://github.com/BlueBrain/dir-content-diff/pull/56))
10
+
11
+ ## [1.7.0](https://github.com/BlueBrain/dir-content-diff/compare/1.6.0..1.7.0)
12
+
13
+ > 5 February 2024
14
+
15
+ ### New Features
16
+
17
+ - Add 'replace_pattern' entry for DictComparator and several related save capabilities (Adrien Berchet - [#53](https://github.com/BlueBrain/dir-content-diff/pull/53))
18
+ - Add 'patterns' entry in specific_args (Adrien Berchet - [#52](https://github.com/BlueBrain/dir-content-diff/pull/52))
19
+
3
20
  ## [1.6.0](https://github.com/BlueBrain/dir-content-diff/compare/1.5.0..1.6.0)
4
21
 
5
22
  > 29 January 2024
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dir-content-diff
3
- Version: 1.6.0
3
+ Version: 1.8.0
4
4
  Summary: Simple tool to compare directory contents.
5
5
  Home-page: https://dir-content-diff.readthedocs.io
6
6
  Author: Blue Brain Project, EPFL
@@ -22,25 +22,30 @@ Description-Content-Type: text/markdown
22
22
  License-File: LICENSE.txt
23
23
  License-File: AUTHORS.md
24
24
  Requires-Dist: dictdiffer>=0.8
25
+ Requires-Dist: dicttoxml>=1.7.12
25
26
  Requires-Dist: diff_pdf_visually>=1.7
27
+ Requires-Dist: jsonpath-ng>=1.5
26
28
  Requires-Dist: PyYaml>=6
27
29
  Provides-Extra: pandas
28
30
  Requires-Dist: pandas>=1.4; extra == "pandas"
31
+ Requires-Dist: pyarrow>=11; extra == "pandas"
29
32
  Requires-Dist: tables>=3.7; extra == "pandas"
30
33
  Provides-Extra: docs
31
34
  Requires-Dist: m2r2; extra == "docs"
32
35
  Requires-Dist: sphinx; extra == "docs"
33
36
  Requires-Dist: sphinx-bluebrain-theme; extra == "docs"
34
37
  Requires-Dist: pandas>=1.4; extra == "docs"
38
+ Requires-Dist: pyarrow>=11; extra == "docs"
35
39
  Requires-Dist: tables>=3.7; extra == "docs"
36
40
  Provides-Extra: test
37
- Requires-Dist: coverage>=6; extra == "test"
41
+ Requires-Dist: coverage>=7.2; extra == "test"
38
42
  Requires-Dist: dicttoxml>=1.7.16; extra == "test"
39
43
  Requires-Dist: matplotlib>=3; extra == "test"
40
44
  Requires-Dist: rst2pdf>=0.99; extra == "test"
41
45
  Requires-Dist: pytest>=6.2; extra == "test"
42
46
  Requires-Dist: pytest-html<4,>=2; extra == "test"
43
47
  Requires-Dist: pandas>=1.4; extra == "test"
48
+ Requires-Dist: pyarrow>=11; extra == "test"
44
49
  Requires-Dist: tables>=3.7; extra == "test"
45
50
 
46
51
  [![Version](https://img.shields.io/pypi/v/dir-content-diff)](https://github.com/BlueBrain/dir-content-diff/releases)
@@ -208,6 +213,18 @@ specific_args = {
208
213
  }
209
214
  ```
210
215
 
216
+ And last but not least, it's possible to use regular expressions to associate specific arguments to
217
+ a set of files:
218
+
219
+ ```python
220
+ specific_args = {
221
+ "all files with *.a of *.b extensions": {
222
+ "patterns": [r".*\.[a,b]$"],
223
+ "comparator": dir_content_diff.BaseComparator(),
224
+ }
225
+ }
226
+ ```
227
+
211
228
 
212
229
  ### Export formatted data
213
230
 
@@ -163,6 +163,18 @@ specific_args = {
163
163
  }
164
164
  ```
165
165
 
166
+ And last but not least, it's possible to use regular expressions to associate specific arguments to
167
+ a set of files:
168
+
169
+ ```python
170
+ specific_args = {
171
+ "all files with *.a of *.b extensions": {
172
+ "patterns": [r".*\.[a,b]$"],
173
+ "comparator": dir_content_diff.BaseComparator(),
174
+ }
175
+ }
176
+ ```
177
+
166
178
 
167
179
  ### Export formatted data
168
180
 
@@ -5,6 +5,7 @@ Simple tool to compare directory contents.
5
5
  import copy
6
6
  import importlib.metadata
7
7
  import logging
8
+ import re
8
9
  from pathlib import Path
9
10
 
10
11
  from dir_content_diff.base_comparators import DefaultComparator
@@ -240,9 +241,19 @@ def compare_trees(
240
241
  kwarg_name_2: kwarg_value_2,
241
242
  }
242
243
  },
243
- <another_file_path>: {...}
244
+ <another_file_path>: {...},
245
+ <a name for this category>: {
246
+ "patterns": ["regex1", "regex2", ...],
247
+ ... (other arguments)
248
+ }
244
249
  }
245
250
 
251
+ If the "patterns" entry is present, then the name is not considered and is only used as
252
+ a helper for the user. When a "patterns" entry is detected, the other arguments are
253
+ applied to all files whose relative name matches one of the given regular expression
254
+ patterns. If a file could match multiple patterns of different groups, only the first
255
+ one is considered.
256
+
246
257
  Note that all entries in this ``dict`` are optional.
247
258
  return_raw_diffs (bool): If set to ``True``, only the raw differences are returned instead
248
259
  of a formatted report.
@@ -275,6 +286,11 @@ def compare_trees(
275
286
  else:
276
287
  specific_args = copy.deepcopy(specific_args)
277
288
 
289
+ pattern_specific_args = {}
290
+ for v in specific_args.values():
291
+ for pattern in v.pop("patterns", []):
292
+ pattern_specific_args[re.compile(pattern)] = v
293
+
278
294
  # Loop over all files and call the correct comparator
279
295
  different_files = {}
280
296
  for ref_file in ref_path.glob("**/*"):
@@ -285,7 +301,14 @@ def compare_trees(
285
301
  comp_file = comp_path / relative_path
286
302
 
287
303
  if comp_file.exists():
288
- specific_file_args = specific_args.get(relative_path, {})
304
+ specific_file_args = specific_args.get(relative_path, None)
305
+ if specific_file_args is None:
306
+ for pattern, pattern_args in pattern_specific_args.items():
307
+ if pattern.match(relative_path):
308
+ specific_file_args = copy.deepcopy(pattern_args)
309
+ break
310
+ if specific_file_args is None:
311
+ specific_file_args = {}
289
312
  comparator = specific_file_args.pop(
290
313
  "comparator",
291
314
  comparators.get(
@@ -2,12 +2,15 @@
2
2
  import configparser
3
3
  import filecmp
4
4
  import json
5
+ import re
5
6
  from abc import ABC
6
7
  from abc import abstractmethod
7
8
  from xml.etree import ElementTree
8
9
 
9
10
  import dictdiffer
11
+ import jsonpath_ng
10
12
  import yaml
13
+ from dicttoxml import dicttoxml
11
14
  from diff_pdf_visually import pdf_similar
12
15
 
13
16
  from dir_content_diff.util import diff_msg_formatter
@@ -282,6 +285,14 @@ class DictComparator(BaseComparator):
282
285
  "add": "Added the value(s) '{value}' in the '{key}' key.",
283
286
  "change": "Changed the value of '{key}' from {value[0]} to {value[1]}.",
284
287
  "remove": "Removed the value(s) '{value}' from '{key}' key.",
288
+ "missing_ref_entry": (
289
+ "The path '{key}' is missing in the reference dictionary, please fix the "
290
+ "'replace_pattern' argument."
291
+ ),
292
+ "missing_comp_entry": (
293
+ "The path '{key}' is missing in the compared dictionary, please fix the "
294
+ "'replace_pattern' argument."
295
+ ),
285
296
  }
286
297
 
287
298
  def __init__(self, *args, **kwargs):
@@ -318,6 +329,43 @@ class DictComparator(BaseComparator):
318
329
  value[num] = str(i)
319
330
  return value
320
331
 
332
+ def format_data(self, data, ref=None, replace_pattern=None, **kwargs):
333
+ """Format the loaded data."""
334
+ # pylint: disable=too-many-nested-blocks
335
+ self.current_state["format_errors"] = errors = []
336
+
337
+ if replace_pattern is not None:
338
+ for pat, paths in replace_pattern.items():
339
+ pattern = pat[0]
340
+ new_value = pat[1]
341
+ count = pat[2] if len(pat) > 2 else 0
342
+ flags = pat[3] if len(pat) > 3 else 0
343
+ for raw_path in paths:
344
+ path = jsonpath_ng.parse(raw_path)
345
+ if ref is not None and len(path.find(ref)) == 0:
346
+ errors.append(
347
+ (
348
+ "missing_ref_entry",
349
+ raw_path,
350
+ None,
351
+ )
352
+ )
353
+ elif len(path.find(data)) == 0:
354
+ errors.append(
355
+ (
356
+ "missing_comp_entry",
357
+ raw_path,
358
+ None,
359
+ )
360
+ )
361
+ else:
362
+ for i in path.find(data):
363
+ if isinstance(i.value, str):
364
+ i.full_path.update(
365
+ data, re.sub(pattern, new_value, i.value, count, flags)
366
+ )
367
+ return data
368
+
321
369
  def diff(self, ref, comp, *args, **kwargs):
322
370
  """Compare 2 dictionaries.
323
371
 
@@ -332,13 +380,16 @@ class DictComparator(BaseComparator):
332
380
  path_limit (list[str]): List of path limit tuples or :class:`dictdiffer.utils.PathLimit`
333
381
  object to limit the diff recursion depth.
334
382
  """
383
+ errors = self.current_state.get("format_errors", [])
384
+
335
385
  if len(args) > 5:
336
386
  dot_notation = args[5]
337
387
  args = args[:5] + args[6:]
338
388
  else:
339
389
  dot_notation = kwargs.pop("dot_notation", False)
340
390
  kwargs["dot_notation"] = dot_notation
341
- return list(dictdiffer.diff(ref, comp, *args, **kwargs))
391
+ errors.extend(list(dictdiffer.diff(ref, comp, *args, **kwargs)))
392
+ return errors
342
393
 
343
394
  def format_diff(self, difference):
344
395
  """Format one element difference."""
@@ -361,6 +412,11 @@ class JsonComparator(DictComparator):
361
412
  data = json.load(file)
362
413
  return data
363
414
 
415
+ def save(self, data, path):
416
+ """Save formatted data into a JSON file."""
417
+ with open(path, "w", encoding="utf-8") as file:
418
+ json.dump(data, file)
419
+
364
420
 
365
421
  class YamlComparator(DictComparator):
366
422
  """Comparator for YAML files.
@@ -374,6 +430,11 @@ class YamlComparator(DictComparator):
374
430
  data = yaml.full_load(file)
375
431
  return data
376
432
 
433
+ def save(self, data, path):
434
+ """Save formatted data into a YAML file."""
435
+ with open(path, "w", encoding="utf-8") as file:
436
+ yaml.dump(data, file)
437
+
377
438
 
378
439
  class XmlComparator(DictComparator):
379
440
  """Comparator for XML files.
@@ -407,9 +468,14 @@ class XmlComparator(DictComparator):
407
468
  data = self.xmltodict(file.read())
408
469
  return data
409
470
 
471
+ def save(self, data, path):
472
+ """Save formatted data into a XML file."""
473
+ with open(path, "w", encoding="utf-8") as file:
474
+ file.write(dicttoxml(data["root"]).decode())
475
+
410
476
  @staticmethod
411
477
  def _cast_from_attribute(text, attr):
412
- """Converts XML text into a Python data format based on the tag attribute."""
478
+ """Convert XML text into a Python data format based on the tag attribute."""
413
479
  if "type" not in attr:
414
480
  return text
415
481
  value_type = attr.get("type", "").lower()
@@ -453,7 +519,7 @@ class XmlComparator(DictComparator):
453
519
 
454
520
  @staticmethod
455
521
  def xmltodict(obj):
456
- """Converts an XML string into a Python object based on each tag's attribute."""
522
+ """Convert an XML string into a Python object based on each tag's attribute."""
457
523
  root = ElementTree.fromstring(obj)
458
524
  output = {}
459
525
 
@@ -473,11 +539,16 @@ class IniComparator(DictComparator):
473
539
  """
474
540
 
475
541
  def load(self, path, **kwargs): # pylint: disable=arguments-differ
476
- """Open a XML file."""
542
+ """Open a INI file."""
477
543
  data = configparser.ConfigParser(**kwargs)
478
544
  data.read(path)
479
545
  return self.configparser_to_dict(data)
480
546
 
547
+ def save(self, data, path):
548
+ """Save formatted data into a INI file."""
549
+ with open(path, "w", encoding="utf-8") as file:
550
+ self.dict_to_configparser(data).write(file)
551
+
481
552
  @staticmethod
482
553
  def configparser_to_dict(config):
483
554
  """Transform a ConfigParser object into a dict."""
@@ -494,6 +565,16 @@ class IniComparator(DictComparator):
494
565
  dict_config[section][option] = val
495
566
  return dict_config
496
567
 
568
+ @staticmethod
569
+ def dict_to_configparser(data, **kwargs):
570
+ """Transform a dict object into a ConfigParser."""
571
+ config = configparser.ConfigParser(**kwargs)
572
+ for k, v in data.items():
573
+ config.add_section(k)
574
+ for opt, val in v.items():
575
+ config[k][opt] = json.dumps(val)
576
+ return config
577
+
497
578
 
498
579
  class PdfComparator(BaseComparator):
499
580
  """Comparator for PDF files."""
@@ -147,6 +147,42 @@ class HdfComparator(DataframeComparator):
147
147
  data.to_hdf(path, index=index, key=key, **kwargs)
148
148
 
149
149
 
150
+ class FeatherComparator(DataframeComparator):
151
+ """Comparator for Feather files."""
152
+
153
+ def load(self, path, **kwargs):
154
+ """Load a Feather file into a :class:`pandas.DataFrame` object."""
155
+ return pd.read_feather(path, **kwargs)
156
+
157
+ def save(self, data, path, **kwargs):
158
+ """Save data to a Feather file."""
159
+ data.to_feather(path, **kwargs)
160
+
161
+
162
+ class ParquetComparator(DataframeComparator):
163
+ """Comparator for Parquet files."""
164
+
165
+ def load(self, path, **kwargs):
166
+ """Load a Parquet file into a :class:`pandas.DataFrame` object."""
167
+ return pd.read_parquet(path, **kwargs)
168
+
169
+ def save(self, data, path, **kwargs):
170
+ """Save data to a Parquet file."""
171
+ data.to_parquet(path, **kwargs)
172
+
173
+
174
+ class StataComparator(DataframeComparator):
175
+ """Comparator for Stata files."""
176
+
177
+ def load(self, path, **kwargs):
178
+ """Load a Stata file into a :class:`pandas.DataFrame` object."""
179
+ return pd.read_stata(path, **kwargs)
180
+
181
+ def save(self, data, path, **kwargs):
182
+ """Save data to a Stata file."""
183
+ data.to_stata(path, **kwargs)
184
+
185
+
150
186
  def register():
151
187
  """Register Pandas extensions."""
152
188
  register_comparator(".csv", CsvComparator())
@@ -156,3 +192,6 @@ def register():
156
192
  register_comparator(".hdf", HdfComparator())
157
193
  register_comparator(".hdf4", HdfComparator())
158
194
  register_comparator(".hdf5", HdfComparator())
195
+ register_comparator(".feather", FeatherComparator())
196
+ register_comparator(".parquet", ParquetComparator())
197
+ register_comparator(".dta", StataComparator())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dir-content-diff
3
- Version: 1.6.0
3
+ Version: 1.8.0
4
4
  Summary: Simple tool to compare directory contents.
5
5
  Home-page: https://dir-content-diff.readthedocs.io
6
6
  Author: Blue Brain Project, EPFL
@@ -22,25 +22,30 @@ Description-Content-Type: text/markdown
22
22
  License-File: LICENSE.txt
23
23
  License-File: AUTHORS.md
24
24
  Requires-Dist: dictdiffer>=0.8
25
+ Requires-Dist: dicttoxml>=1.7.12
25
26
  Requires-Dist: diff_pdf_visually>=1.7
27
+ Requires-Dist: jsonpath-ng>=1.5
26
28
  Requires-Dist: PyYaml>=6
27
29
  Provides-Extra: pandas
28
30
  Requires-Dist: pandas>=1.4; extra == "pandas"
31
+ Requires-Dist: pyarrow>=11; extra == "pandas"
29
32
  Requires-Dist: tables>=3.7; extra == "pandas"
30
33
  Provides-Extra: docs
31
34
  Requires-Dist: m2r2; extra == "docs"
32
35
  Requires-Dist: sphinx; extra == "docs"
33
36
  Requires-Dist: sphinx-bluebrain-theme; extra == "docs"
34
37
  Requires-Dist: pandas>=1.4; extra == "docs"
38
+ Requires-Dist: pyarrow>=11; extra == "docs"
35
39
  Requires-Dist: tables>=3.7; extra == "docs"
36
40
  Provides-Extra: test
37
- Requires-Dist: coverage>=6; extra == "test"
41
+ Requires-Dist: coverage>=7.2; extra == "test"
38
42
  Requires-Dist: dicttoxml>=1.7.16; extra == "test"
39
43
  Requires-Dist: matplotlib>=3; extra == "test"
40
44
  Requires-Dist: rst2pdf>=0.99; extra == "test"
41
45
  Requires-Dist: pytest>=6.2; extra == "test"
42
46
  Requires-Dist: pytest-html<4,>=2; extra == "test"
43
47
  Requires-Dist: pandas>=1.4; extra == "test"
48
+ Requires-Dist: pyarrow>=11; extra == "test"
44
49
  Requires-Dist: tables>=3.7; extra == "test"
45
50
 
46
51
  [![Version](https://img.shields.io/pypi/v/dir-content-diff)](https://github.com/BlueBrain/dir-content-diff/releases)
@@ -208,6 +213,18 @@ specific_args = {
208
213
  }
209
214
  ```
210
215
 
216
+ And last but not least, it's possible to use regular expressions to associate specific arguments to
217
+ a set of files:
218
+
219
+ ```python
220
+ specific_args = {
221
+ "all files with *.a of *.b extensions": {
222
+ "patterns": [r".*\.[a,b]$"],
223
+ "comparator": dir_content_diff.BaseComparator(),
224
+ }
225
+ }
226
+ ```
227
+
211
228
 
212
229
  ### Export formatted data
213
230
 
@@ -4,6 +4,7 @@
4
4
  .codespellrc
5
5
  .copier-answers.yml
6
6
  .coveragerc
7
+ .coveragerc_py38
7
8
  .flake8
8
9
  .gitattributes
9
10
  .gitignore
@@ -1,5 +1,7 @@
1
1
  dictdiffer>=0.8
2
+ dicttoxml>=1.7.12
2
3
  diff_pdf_visually>=1.7
4
+ jsonpath-ng>=1.5
3
5
  PyYaml>=6
4
6
 
5
7
  [docs]
@@ -7,18 +9,21 @@ m2r2
7
9
  sphinx
8
10
  sphinx-bluebrain-theme
9
11
  pandas>=1.4
12
+ pyarrow>=11
10
13
  tables>=3.7
11
14
 
12
15
  [pandas]
13
16
  pandas>=1.4
17
+ pyarrow>=11
14
18
  tables>=3.7
15
19
 
16
20
  [test]
17
- coverage>=6
21
+ coverage>=7.2
18
22
  dicttoxml>=1.7.16
19
23
  matplotlib>=3
20
24
  rst2pdf>=0.99
21
25
  pytest>=6.2
22
26
  pytest-html<4,>=2
23
27
  pandas>=1.4
28
+ pyarrow>=11
24
29
  tables>=3.7
@@ -6,7 +6,9 @@ from setuptools import setup
6
6
 
7
7
  reqs = [
8
8
  "dictdiffer>=0.8",
9
+ "dicttoxml>=1.7.12",
9
10
  "diff_pdf_visually>=1.7",
11
+ "jsonpath-ng>=1.5",
10
12
  "PyYaml>=6",
11
13
  ]
12
14
 
@@ -18,11 +20,12 @@ doc_reqs = [
18
20
 
19
21
  pandas_reqs = [
20
22
  "pandas>=1.4",
23
+ "pyarrow>=11",
21
24
  "tables>=3.7",
22
25
  ]
23
26
 
24
27
  test_reqs = [
25
- "coverage>=6",
28
+ "coverage>=7.2",
26
29
  "dicttoxml>=1.7.16",
27
30
  "matplotlib>=3",
28
31
  "rst2pdf>=0.99",
@@ -125,6 +125,12 @@ def dict_diff():
125
125
  return diff
126
126
 
127
127
 
128
+ @pytest.fixture
129
+ def base_diff():
130
+ """The diff that should be reported for the XML files."""
131
+ return r"The files '\S*/file\..{3,4}' and '\S*/file\..{3,4}' are different\."
132
+
133
+
128
134
  @pytest.fixture
129
135
  def xml_diff(dict_diff):
130
136
  """The diff that should be reported for the XML files."""