dir-content-diff 1.12.1__tar.gz → 1.14.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.
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/workflows/commitlint.yml +2 -2
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/workflows/publish-sdist.yml +2 -2
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/workflows/run-tox.yml +2 -2
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.gitignore +1 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/CHANGELOG.md +33 -0
- {dir_content_diff-1.12.1/dir_content_diff.egg-info → dir_content_diff-1.14.0}/PKG-INFO +115 -6
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/README.md +113 -5
- dir_content_diff-1.14.0/dir_content_diff/__init__.py +53 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/base_comparators.py +1 -1
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/cli/__init__.py +34 -2
- dir_content_diff-1.14.0/dir_content_diff/config.py +225 -0
- dir_content_diff-1.14.0/dir_content_diff/core.py +463 -0
- dir_content_diff-1.14.0/dir_content_diff/parallel_utils.py +100 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/pytest_plugin.py +1 -1
- dir_content_diff-1.14.0/dir_content_diff/registry.py +114 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/util.py +1 -1
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0/dir_content_diff.egg-info}/PKG-INFO +115 -6
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff.egg-info/SOURCES.txt +6 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff.egg-info/requires.txt +1 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/docs/Makefile +1 -1
- dir_content_diff-1.14.0/docs/source/conf.py +209 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/docs/source/index.rst +1 -1
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/pyproject.toml +1 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/comparators/test_pandas.py +17 -16
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/comparators/test_voxcell.py +1 -1
- dir_content_diff-1.14.0/tests/test_attrs_validation.py +218 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/test_base.py +209 -75
- dir_content_diff-1.14.0/tests/test_parallel_execution.py +371 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/test_util.py +1 -1
- dir_content_diff-1.12.1/dir_content_diff/__init__.py +0 -423
- dir_content_diff-1.12.1/docs/source/conf.py +0 -107
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.auto-changelog +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.auto-changelog-template.hbs +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.codespellignorelines +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.codespellrc +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.copier-answers.yml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.copyright.tmpl +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.coveragerc +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.coveragerc_min_versions +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.gitattributes +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/ISSUE_TEMPLATE/how_to_use.yaml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/dependabot.yml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.github/pull_request_template.md +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.gitreview +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.pre-commit-config.yaml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.pylintrc +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/.readthedocs.yml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/AUTHORS.md +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/CONTRIBUTING.md +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/LICENSE.txt +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/MANIFEST.in +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/codecov.yml +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/commitlint.config.js +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/comparators/__init__.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/comparators/dependencies.json +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/comparators/morphio.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/comparators/pandas.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff/comparators/voxcell.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff.egg-info/dependency_links.txt +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff.egg-info/entry_points.txt +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/dir_content_diff.egg-info/top_level.txt +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/docs/source/.gitignore +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/docs/source/api_ref.rst +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/docs/source/changelog.rst +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/docs/source/contributing.rst +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/package.json +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/setup.cfg +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/setup.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/__init__.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/comparators/__init__.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/comparators/test_morphio.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/conftest.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/generate_test_files.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/test_cli.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/test_missing_deps.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/test_pytest_plugin.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tests/test_version.py +0 -0
- {dir_content_diff-1.12.1 → dir_content_diff-1.14.0}/tox.ini +0 -0
|
@@ -11,8 +11,8 @@ jobs:
|
|
|
11
11
|
env:
|
|
12
12
|
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
15
|
-
- uses: actions/setup-node@
|
|
14
|
+
- uses: actions/checkout@v5
|
|
15
|
+
- uses: actions/setup-node@v5
|
|
16
16
|
with:
|
|
17
17
|
node-version: '20'
|
|
18
18
|
- run: npm install -g --force commitlint @commitlint/cli commitlint-plugin-cleanfeet
|
|
@@ -10,9 +10,9 @@ jobs:
|
|
|
10
10
|
name: Build and publish on PyPI
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
13
|
+
- uses: actions/checkout@v5
|
|
14
14
|
- name: Set up Python 3.10
|
|
15
|
-
uses: actions/setup-python@
|
|
15
|
+
uses: actions/setup-python@v6
|
|
16
16
|
with:
|
|
17
17
|
python-version: "3.10"
|
|
18
18
|
- name: Build a wheel and a source tarball
|
|
@@ -24,9 +24,9 @@ jobs:
|
|
|
24
24
|
min_versions: "min_versions"
|
|
25
25
|
|
|
26
26
|
steps:
|
|
27
|
-
- uses: actions/checkout@
|
|
27
|
+
- uses: actions/checkout@v5
|
|
28
28
|
- name: Set up Python ${{ matrix.python-version }}
|
|
29
|
-
uses: actions/setup-python@
|
|
29
|
+
uses: actions/setup-python@v6
|
|
30
30
|
with:
|
|
31
31
|
python-version: ${{ matrix.python-version }}
|
|
32
32
|
- name: Get current month
|
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.14.0](https://github.com/BlueBrain/dir-content-diff/compare/1.13.0..1.14.0)
|
|
4
|
+
|
|
5
|
+
> 13 September 2025
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
|
|
9
|
+
- Add parallel iteration over compared files (Adrien Berchet - [#89](https://github.com/BlueBrain/dir-content-diff/pull/89))
|
|
10
|
+
|
|
11
|
+
### Documentation Changes
|
|
12
|
+
|
|
13
|
+
- Improve README to describe parallel execution (Adrien Berchet - [#91](https://github.com/BlueBrain/dir-content-diff/pull/91))
|
|
14
|
+
|
|
15
|
+
### Refactoring and Updates
|
|
16
|
+
|
|
17
|
+
- Reorganize the code (Adrien Berchet - [#90](https://github.com/BlueBrain/dir-content-diff/pull/90))
|
|
18
|
+
|
|
19
|
+
## [1.13.0](https://github.com/BlueBrain/dir-content-diff/compare/1.12.1..1.13.0)
|
|
20
|
+
|
|
21
|
+
> 12 September 2025
|
|
22
|
+
|
|
23
|
+
### New Features
|
|
24
|
+
|
|
25
|
+
- Add patterns to ignore files in the reference directory (Adrien Berchet - [#86](https://github.com/BlueBrain/dir-content-diff/pull/86))
|
|
26
|
+
|
|
27
|
+
### Documentation Changes
|
|
28
|
+
|
|
29
|
+
- Fix format in Sphinx (Adrien Berchet - [#85](https://github.com/BlueBrain/dir-content-diff/pull/85))
|
|
30
|
+
- Clarify how reference and compared directories are processed (Adrien Berchet - [#83](https://github.com/BlueBrain/dir-content-diff/pull/83))
|
|
31
|
+
|
|
32
|
+
### Performance Improvements
|
|
33
|
+
|
|
34
|
+
- Build comparator registry only once before iterating over all files (Adrien Berchet - [#87](https://github.com/BlueBrain/dir-content-diff/pull/87))
|
|
35
|
+
|
|
3
36
|
## [1.12.1](https://github.com/BlueBrain/dir-content-diff/compare/1.12.0..1.12.1)
|
|
4
37
|
|
|
5
38
|
> 3 July 2025
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dir-content-diff
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.0
|
|
4
4
|
Summary: Simple tool to compare directory contents.
|
|
5
5
|
Author: Blue Brain Project, EPFL
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -21,6 +21,7 @@ Requires-Python: >=3.9
|
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE.txt
|
|
23
23
|
License-File: AUTHORS.md
|
|
24
|
+
Requires-Dist: attrs>=21.3.0
|
|
24
25
|
Requires-Dist: click>=8
|
|
25
26
|
Requires-Dist: dictdiffer>=0.8
|
|
26
27
|
Requires-Dist: dicttoxml>=1.7.12
|
|
@@ -75,7 +76,7 @@ Dynamic: provides-extra
|
|
|
75
76
|
This project provides simple tools to compare the content of a directory against a reference
|
|
76
77
|
directory.
|
|
77
78
|
|
|
78
|
-
This is useful to check the results of a process that
|
|
79
|
+
This is useful to check the results of a process that generates several files, like a luigi
|
|
79
80
|
workflow for example.
|
|
80
81
|
|
|
81
82
|
|
|
@@ -101,7 +102,9 @@ the files, so it is not able to report which values are different.
|
|
|
101
102
|
|
|
102
103
|
### Compare two directories
|
|
103
104
|
|
|
104
|
-
|
|
105
|
+
#### Basic Directory Comparison
|
|
106
|
+
|
|
107
|
+
Let's compare two directories with the following structures:
|
|
105
108
|
|
|
106
109
|
```bash
|
|
107
110
|
└── reference_dir
|
|
@@ -120,6 +123,9 @@ If one wants to compare two directories with the following structures:
|
|
|
120
123
|
└── file_1.c
|
|
121
124
|
```
|
|
122
125
|
|
|
126
|
+
The `reference` directory contains all the files that should be checked in the `compared` directory,
|
|
127
|
+
which means that extraneous files in the `compared` directory are just ignored.
|
|
128
|
+
|
|
123
129
|
These two directories can be compared with the following code:
|
|
124
130
|
|
|
125
131
|
```python
|
|
@@ -128,7 +134,17 @@ import dir_content_diff
|
|
|
128
134
|
dir_content_diff.compare_trees("reference_dir", "compared_dir")
|
|
129
135
|
```
|
|
130
136
|
|
|
131
|
-
|
|
137
|
+
> [!WARNING]
|
|
138
|
+
> The order of the parameters is important: the first path is considered as the `reference`
|
|
139
|
+
> directory while the second one is the `compared` directory. Inverting the parameters may return
|
|
140
|
+
> a different result (in this example it would return that the file `sub_file_3.b` is missing).
|
|
141
|
+
|
|
142
|
+
If all the files are identical, this code will return an empty dictionary because no difference
|
|
143
|
+
was detected. As mentioned previously, this is because `dir-content-diff` is only looking for files
|
|
144
|
+
in the compared directory that are also present in the reference directory, so the file
|
|
145
|
+
`sub_file_3.b` is just ignored in this case.
|
|
146
|
+
|
|
147
|
+
#### Using Custom Comparators
|
|
132
148
|
|
|
133
149
|
If ``reference_dir/file_1.c`` is the following JSON-like file:
|
|
134
150
|
|
|
@@ -171,6 +187,8 @@ The previous code will output the following dictionary:
|
|
|
171
187
|
}
|
|
172
188
|
```
|
|
173
189
|
|
|
190
|
+
#### Assertion-based Comparison
|
|
191
|
+
|
|
174
192
|
It is also possible to check whether the two directories are equal or not with the following code:
|
|
175
193
|
|
|
176
194
|
```python
|
|
@@ -189,7 +207,9 @@ Changed the value of '[a]' from 1 to 2.
|
|
|
189
207
|
Changed the value of '[b][0]' from 1 to 10.
|
|
190
208
|
```
|
|
191
209
|
|
|
192
|
-
|
|
210
|
+
#### Advanced Configuration Options
|
|
211
|
+
|
|
212
|
+
The comparators have parameters that can be passed either to be used for all files of a
|
|
193
213
|
given extension or only for a specific file:
|
|
194
214
|
|
|
195
215
|
```python
|
|
@@ -217,6 +237,8 @@ dir_content_diff.assert_equal_trees(
|
|
|
217
237
|
|
|
218
238
|
Each comparator has different arguments that are detailed in the documentation.
|
|
219
239
|
|
|
240
|
+
##### File-specific Comparators
|
|
241
|
+
|
|
220
242
|
It's also possible to specify a arbitrary comparator for a specific file:
|
|
221
243
|
|
|
222
244
|
```python
|
|
@@ -228,7 +250,9 @@ specific_args = {
|
|
|
228
250
|
}
|
|
229
251
|
```
|
|
230
252
|
|
|
231
|
-
|
|
253
|
+
##### Pattern-based Configuration
|
|
254
|
+
|
|
255
|
+
Another possibility is to use regular expressions to associate specific arguments to
|
|
232
256
|
a set of files:
|
|
233
257
|
|
|
234
258
|
```python
|
|
@@ -240,6 +264,91 @@ specific_args = {
|
|
|
240
264
|
}
|
|
241
265
|
```
|
|
242
266
|
|
|
267
|
+
##### File Filtering
|
|
268
|
+
|
|
269
|
+
Last but not least, it's possible to filter files from the reference directory (for example
|
|
270
|
+
because the reference directory contains temporary files that should not be compared). For
|
|
271
|
+
example, the following code will ignore all files whose name does not start with `file_` and does
|
|
272
|
+
not ends with `_tmp.yaml`:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
import dir_content_diff
|
|
276
|
+
|
|
277
|
+
dir_content_diff.compare_trees(
|
|
278
|
+
"reference_dir",
|
|
279
|
+
"compared_dir",
|
|
280
|
+
include_patterns=[r"file_.*"],
|
|
281
|
+
exclude_patterns=[r".*_tmp\.yaml"],
|
|
282
|
+
)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
### Parallel Execution
|
|
287
|
+
|
|
288
|
+
By default, `dir-content-diff` runs file comparisons sequentially. However, for improved performance when comparing large numbers of files, parallel execution is available using either thread-based or process-based concurrency.
|
|
289
|
+
|
|
290
|
+
#### Configuration Options
|
|
291
|
+
|
|
292
|
+
Parallel execution can be configured using the following parameters:
|
|
293
|
+
|
|
294
|
+
- **`executor_type`**: Controls the type of parallel execution:
|
|
295
|
+
- `"sequential"` (default): No parallel execution, files are compared one by one
|
|
296
|
+
- `"thread"`: Uses `ThreadPoolExecutor` (recommended for I/O-bound tasks)
|
|
297
|
+
- `"process"`: Uses `ProcessPoolExecutor` (recommended for CPU-intensive comparisons)
|
|
298
|
+
|
|
299
|
+
- **`max_workers`**: Maximum number of worker threads/processes. If `None` (default), it defaults to `min(32, (os.cpu_count() or 1) + 4)`.
|
|
300
|
+
|
|
301
|
+
#### Usage Examples
|
|
302
|
+
|
|
303
|
+
Enable thread-based parallel execution:
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
import dir_content_diff
|
|
307
|
+
|
|
308
|
+
dir_content_diff.compare_trees(
|
|
309
|
+
"reference_dir",
|
|
310
|
+
"compared_dir",
|
|
311
|
+
executor_type="thread",
|
|
312
|
+
max_workers=8
|
|
313
|
+
)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Enable process-based parallel execution with automatic worker count:
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
import dir_content_diff
|
|
320
|
+
|
|
321
|
+
dir_content_diff.compare_trees(
|
|
322
|
+
"reference_dir",
|
|
323
|
+
"compared_dir",
|
|
324
|
+
executor_type="process"
|
|
325
|
+
)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Using a configuration object:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
import dir_content_diff
|
|
332
|
+
|
|
333
|
+
config = dir_content_diff.ComparisonConfig(
|
|
334
|
+
executor_type="thread",
|
|
335
|
+
max_workers=4
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
dir_content_diff.compare_trees(
|
|
339
|
+
"reference_dir",
|
|
340
|
+
"compared_dir",
|
|
341
|
+
config=config
|
|
342
|
+
)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Performance Considerations
|
|
346
|
+
|
|
347
|
+
- **Thread-based execution** (`executor_type="thread"`) is generally recommended for most use cases as file comparisons are typically I/O-bound operations
|
|
348
|
+
- **Process-based execution** (`executor_type="process"`) may be beneficial when using computationally intensive comparators or when dealing with very large files
|
|
349
|
+
- Parallel execution is automatically disabled for single file comparisons and falls back to sequential execution when only one file needs to be compared
|
|
350
|
+
- The optimal number of workers depends on your system's capabilities and the nature of your files; too many workers may actually decrease performance due to overhead
|
|
351
|
+
|
|
243
352
|
|
|
244
353
|
### Export formatted data
|
|
245
354
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
This project provides simple tools to compare the content of a directory against a reference
|
|
11
11
|
directory.
|
|
12
12
|
|
|
13
|
-
This is useful to check the results of a process that
|
|
13
|
+
This is useful to check the results of a process that generates several files, like a luigi
|
|
14
14
|
workflow for example.
|
|
15
15
|
|
|
16
16
|
|
|
@@ -36,7 +36,9 @@ the files, so it is not able to report which values are different.
|
|
|
36
36
|
|
|
37
37
|
### Compare two directories
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
#### Basic Directory Comparison
|
|
40
|
+
|
|
41
|
+
Let's compare two directories with the following structures:
|
|
40
42
|
|
|
41
43
|
```bash
|
|
42
44
|
└── reference_dir
|
|
@@ -55,6 +57,9 @@ If one wants to compare two directories with the following structures:
|
|
|
55
57
|
└── file_1.c
|
|
56
58
|
```
|
|
57
59
|
|
|
60
|
+
The `reference` directory contains all the files that should be checked in the `compared` directory,
|
|
61
|
+
which means that extraneous files in the `compared` directory are just ignored.
|
|
62
|
+
|
|
58
63
|
These two directories can be compared with the following code:
|
|
59
64
|
|
|
60
65
|
```python
|
|
@@ -63,7 +68,17 @@ import dir_content_diff
|
|
|
63
68
|
dir_content_diff.compare_trees("reference_dir", "compared_dir")
|
|
64
69
|
```
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
> [!WARNING]
|
|
72
|
+
> The order of the parameters is important: the first path is considered as the `reference`
|
|
73
|
+
> directory while the second one is the `compared` directory. Inverting the parameters may return
|
|
74
|
+
> a different result (in this example it would return that the file `sub_file_3.b` is missing).
|
|
75
|
+
|
|
76
|
+
If all the files are identical, this code will return an empty dictionary because no difference
|
|
77
|
+
was detected. As mentioned previously, this is because `dir-content-diff` is only looking for files
|
|
78
|
+
in the compared directory that are also present in the reference directory, so the file
|
|
79
|
+
`sub_file_3.b` is just ignored in this case.
|
|
80
|
+
|
|
81
|
+
#### Using Custom Comparators
|
|
67
82
|
|
|
68
83
|
If ``reference_dir/file_1.c`` is the following JSON-like file:
|
|
69
84
|
|
|
@@ -106,6 +121,8 @@ The previous code will output the following dictionary:
|
|
|
106
121
|
}
|
|
107
122
|
```
|
|
108
123
|
|
|
124
|
+
#### Assertion-based Comparison
|
|
125
|
+
|
|
109
126
|
It is also possible to check whether the two directories are equal or not with the following code:
|
|
110
127
|
|
|
111
128
|
```python
|
|
@@ -124,7 +141,9 @@ Changed the value of '[a]' from 1 to 2.
|
|
|
124
141
|
Changed the value of '[b][0]' from 1 to 10.
|
|
125
142
|
```
|
|
126
143
|
|
|
127
|
-
|
|
144
|
+
#### Advanced Configuration Options
|
|
145
|
+
|
|
146
|
+
The comparators have parameters that can be passed either to be used for all files of a
|
|
128
147
|
given extension or only for a specific file:
|
|
129
148
|
|
|
130
149
|
```python
|
|
@@ -152,6 +171,8 @@ dir_content_diff.assert_equal_trees(
|
|
|
152
171
|
|
|
153
172
|
Each comparator has different arguments that are detailed in the documentation.
|
|
154
173
|
|
|
174
|
+
##### File-specific Comparators
|
|
175
|
+
|
|
155
176
|
It's also possible to specify a arbitrary comparator for a specific file:
|
|
156
177
|
|
|
157
178
|
```python
|
|
@@ -163,7 +184,9 @@ specific_args = {
|
|
|
163
184
|
}
|
|
164
185
|
```
|
|
165
186
|
|
|
166
|
-
|
|
187
|
+
##### Pattern-based Configuration
|
|
188
|
+
|
|
189
|
+
Another possibility is to use regular expressions to associate specific arguments to
|
|
167
190
|
a set of files:
|
|
168
191
|
|
|
169
192
|
```python
|
|
@@ -175,6 +198,91 @@ specific_args = {
|
|
|
175
198
|
}
|
|
176
199
|
```
|
|
177
200
|
|
|
201
|
+
##### File Filtering
|
|
202
|
+
|
|
203
|
+
Last but not least, it's possible to filter files from the reference directory (for example
|
|
204
|
+
because the reference directory contains temporary files that should not be compared). For
|
|
205
|
+
example, the following code will ignore all files whose name does not start with `file_` and does
|
|
206
|
+
not ends with `_tmp.yaml`:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
import dir_content_diff
|
|
210
|
+
|
|
211
|
+
dir_content_diff.compare_trees(
|
|
212
|
+
"reference_dir",
|
|
213
|
+
"compared_dir",
|
|
214
|
+
include_patterns=[r"file_.*"],
|
|
215
|
+
exclude_patterns=[r".*_tmp\.yaml"],
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
### Parallel Execution
|
|
221
|
+
|
|
222
|
+
By default, `dir-content-diff` runs file comparisons sequentially. However, for improved performance when comparing large numbers of files, parallel execution is available using either thread-based or process-based concurrency.
|
|
223
|
+
|
|
224
|
+
#### Configuration Options
|
|
225
|
+
|
|
226
|
+
Parallel execution can be configured using the following parameters:
|
|
227
|
+
|
|
228
|
+
- **`executor_type`**: Controls the type of parallel execution:
|
|
229
|
+
- `"sequential"` (default): No parallel execution, files are compared one by one
|
|
230
|
+
- `"thread"`: Uses `ThreadPoolExecutor` (recommended for I/O-bound tasks)
|
|
231
|
+
- `"process"`: Uses `ProcessPoolExecutor` (recommended for CPU-intensive comparisons)
|
|
232
|
+
|
|
233
|
+
- **`max_workers`**: Maximum number of worker threads/processes. If `None` (default), it defaults to `min(32, (os.cpu_count() or 1) + 4)`.
|
|
234
|
+
|
|
235
|
+
#### Usage Examples
|
|
236
|
+
|
|
237
|
+
Enable thread-based parallel execution:
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
import dir_content_diff
|
|
241
|
+
|
|
242
|
+
dir_content_diff.compare_trees(
|
|
243
|
+
"reference_dir",
|
|
244
|
+
"compared_dir",
|
|
245
|
+
executor_type="thread",
|
|
246
|
+
max_workers=8
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Enable process-based parallel execution with automatic worker count:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
import dir_content_diff
|
|
254
|
+
|
|
255
|
+
dir_content_diff.compare_trees(
|
|
256
|
+
"reference_dir",
|
|
257
|
+
"compared_dir",
|
|
258
|
+
executor_type="process"
|
|
259
|
+
)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Using a configuration object:
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
import dir_content_diff
|
|
266
|
+
|
|
267
|
+
config = dir_content_diff.ComparisonConfig(
|
|
268
|
+
executor_type="thread",
|
|
269
|
+
max_workers=4
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
dir_content_diff.compare_trees(
|
|
273
|
+
"reference_dir",
|
|
274
|
+
"compared_dir",
|
|
275
|
+
config=config
|
|
276
|
+
)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Performance Considerations
|
|
280
|
+
|
|
281
|
+
- **Thread-based execution** (`executor_type="thread"`) is generally recommended for most use cases as file comparisons are typically I/O-bound operations
|
|
282
|
+
- **Process-based execution** (`executor_type="process"`) may be beneficial when using computationally intensive comparators or when dealing with very large files
|
|
283
|
+
- Parallel execution is automatically disabled for single file comparisons and falls back to sequential execution when only one file needs to be compared
|
|
284
|
+
- The optimal number of workers depends on your system's capabilities and the nature of your files; too many workers may actually decrease performance due to overhead
|
|
285
|
+
|
|
178
286
|
|
|
179
287
|
### Export formatted data
|
|
180
288
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""dir-content-diff package.
|
|
2
|
+
|
|
3
|
+
Simple tool to compare directory contents.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# LICENSE HEADER MANAGED BY add-license-header
|
|
7
|
+
# Copyright (c) 2023-2025 Blue Brain Project, EPFL.
|
|
8
|
+
#
|
|
9
|
+
# This file is part of dir-content-diff.
|
|
10
|
+
# See https://github.com/BlueBrain/dir-content-diff for further info.
|
|
11
|
+
#
|
|
12
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
13
|
+
# LICENSE HEADER MANAGED BY add-license-header
|
|
14
|
+
|
|
15
|
+
import importlib.metadata
|
|
16
|
+
|
|
17
|
+
# Import base comparators
|
|
18
|
+
from dir_content_diff.base_comparators import BaseComparator
|
|
19
|
+
from dir_content_diff.base_comparators import DefaultComparator
|
|
20
|
+
|
|
21
|
+
# Import core comparison functionality
|
|
22
|
+
from dir_content_diff.config import ComparisonConfig
|
|
23
|
+
from dir_content_diff.core import ComparatorType
|
|
24
|
+
from dir_content_diff.core import assert_equal_trees
|
|
25
|
+
from dir_content_diff.core import compare_files
|
|
26
|
+
from dir_content_diff.core import compare_trees
|
|
27
|
+
from dir_content_diff.core import export_formatted_file
|
|
28
|
+
from dir_content_diff.core import pick_comparator
|
|
29
|
+
|
|
30
|
+
# Import comparator registry functionality
|
|
31
|
+
from dir_content_diff.registry import get_comparators
|
|
32
|
+
from dir_content_diff.registry import register_comparator
|
|
33
|
+
from dir_content_diff.registry import reset_comparators
|
|
34
|
+
from dir_content_diff.registry import unregister_comparator
|
|
35
|
+
|
|
36
|
+
__version__ = importlib.metadata.version("dir-content-diff")
|
|
37
|
+
|
|
38
|
+
# For backward compatibility, ensure all public functions are available
|
|
39
|
+
__all__ = [
|
|
40
|
+
"ComparisonConfig",
|
|
41
|
+
"ComparatorType",
|
|
42
|
+
"assert_equal_trees",
|
|
43
|
+
"compare_files",
|
|
44
|
+
"compare_trees",
|
|
45
|
+
"export_formatted_file",
|
|
46
|
+
"pick_comparator",
|
|
47
|
+
"get_comparators",
|
|
48
|
+
"register_comparator",
|
|
49
|
+
"reset_comparators",
|
|
50
|
+
"unregister_comparator",
|
|
51
|
+
"BaseComparator",
|
|
52
|
+
"DefaultComparator",
|
|
53
|
+
]
|
|
@@ -513,7 +513,7 @@ class XmlComparator(DictComparator):
|
|
|
513
513
|
elif str(text).lower() == "false":
|
|
514
514
|
res = False
|
|
515
515
|
else:
|
|
516
|
-
raise ValueError("
|
|
516
|
+
raise ValueError("Boolean attributes expect 'true' or 'false'.")
|
|
517
517
|
elif value_type == "list":
|
|
518
518
|
res = []
|
|
519
519
|
elif value_type == "dict":
|
|
@@ -13,15 +13,17 @@ import json
|
|
|
13
13
|
import logging
|
|
14
14
|
import sys
|
|
15
15
|
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
from typing import Union
|
|
16
18
|
|
|
17
19
|
import click
|
|
18
20
|
from yaml import safe_load
|
|
19
21
|
|
|
20
|
-
from dir_content_diff import _DEFAULT_EXPORT_SUFFIX
|
|
21
22
|
from dir_content_diff import compare_files
|
|
22
23
|
from dir_content_diff import compare_trees
|
|
23
24
|
from dir_content_diff import export_formatted_file
|
|
24
25
|
from dir_content_diff import pick_comparator
|
|
26
|
+
from dir_content_diff.core import _DEFAULT_EXPORT_SUFFIX
|
|
25
27
|
from dir_content_diff.util import LOGGER
|
|
26
28
|
|
|
27
29
|
|
|
@@ -102,6 +104,19 @@ def load_config(ctx, param, value): # pylint: disable=unused-argument
|
|
|
102
104
|
default=False,
|
|
103
105
|
help="Sort the differences by file name.",
|
|
104
106
|
)
|
|
107
|
+
@click.option(
|
|
108
|
+
"--executor-type",
|
|
109
|
+
type=click.Choice(["sequential", "thread", "process"]),
|
|
110
|
+
default="sequential",
|
|
111
|
+
help="Type of executor for execution. 'sequential' for single-threaded, "
|
|
112
|
+
"'thread' for I/O-bound tasks, 'process' for CPU-bound tasks.",
|
|
113
|
+
)
|
|
114
|
+
@click.option(
|
|
115
|
+
"--max-workers",
|
|
116
|
+
type=int,
|
|
117
|
+
help="Maximum number of worker threads/processes for parallel execution. "
|
|
118
|
+
"Only used with 'thread' or 'process' executor types.",
|
|
119
|
+
)
|
|
105
120
|
@click.version_option()
|
|
106
121
|
@click.pass_context
|
|
107
122
|
def main(ctx, *args, **kwargs):
|
|
@@ -120,16 +135,31 @@ def main(ctx, *args, **kwargs):
|
|
|
120
135
|
|
|
121
136
|
ref = Path(kwargs.pop("reference_input"))
|
|
122
137
|
comp = Path(kwargs.pop("compared_input"))
|
|
138
|
+
|
|
139
|
+
# Extract execution options
|
|
140
|
+
executor_type = kwargs.pop("executor_type", "sequential")
|
|
141
|
+
max_workers = kwargs.pop("max_workers", None)
|
|
142
|
+
|
|
123
143
|
input_diff(
|
|
124
144
|
ref,
|
|
125
145
|
comp,
|
|
126
146
|
ctx.config,
|
|
127
147
|
kwargs.pop("export_formatted_files", False),
|
|
128
148
|
kwargs.pop("sort_diffs", False),
|
|
149
|
+
executor_type,
|
|
150
|
+
max_workers,
|
|
129
151
|
)
|
|
130
152
|
|
|
131
153
|
|
|
132
|
-
def input_diff(
|
|
154
|
+
def input_diff(
|
|
155
|
+
ref: Union[str, Path],
|
|
156
|
+
comp: Union[str, Path],
|
|
157
|
+
config,
|
|
158
|
+
export_formatted_files: Union[bool, str] = False,
|
|
159
|
+
sort_diffs: bool = False,
|
|
160
|
+
executor_type: str = "sequential",
|
|
161
|
+
max_workers: Optional[int] = None,
|
|
162
|
+
):
|
|
133
163
|
"""Compute and display differences from given inputs."""
|
|
134
164
|
ref = Path(ref)
|
|
135
165
|
comp = Path(comp)
|
|
@@ -145,6 +175,8 @@ def input_diff(ref, comp, config, export_formatted_files=False, sort_diffs=False
|
|
|
145
175
|
comp,
|
|
146
176
|
specific_args=config,
|
|
147
177
|
export_formatted_files=export_formatted_files,
|
|
178
|
+
executor_type=executor_type,
|
|
179
|
+
max_workers=max_workers,
|
|
148
180
|
)
|
|
149
181
|
else:
|
|
150
182
|
comparator_name = config.pop("comparator", None)
|