peter-diff 0.1.1__tar.gz → 0.1.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.
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: peter-diff
3
+ Version: 0.1.3
4
+ Summary: A simple CLI tool to diff two text files.
5
+ License: MIT
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+
9
+ # peter-diff
10
+
11
+ A Python CLI tool for side-by-side diffing of files and folders.
12
+
13
+ ![](image/2026-05-23T01_18_19.png)
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install -e .
19
+ ```
20
+ OR
21
+
22
+ ```sh
23
+ pip install peter-diff
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Compare two files
29
+
30
+ ```sh
31
+ peter-diff file1.txt file2.txt
32
+ ```
33
+
34
+ Displays a side-by-side diff with line numbers. Changed characters within a line are highlighted. Identical files print `same`.
35
+
36
+ ![](image/file.png)
37
+
38
+ ### Compare two folders
39
+
40
+ ```sh
41
+ peter-diff dir1/ dir2/
42
+ ```
43
+
44
+ Displays a side-by-side tree view of both directories:
45
+
46
+ ![](image/folder.png)
47
+
48
+ Status indicators:
49
+ | Symbol | Meaning |
50
+ |--------|---------|
51
+ | *(none)* | identical |
52
+ | `diff` | file differs |
53
+ | `←` | only in left folder |
54
+ | `→` | only in right folder |
55
+ | `bin` | binary / unreadable, skipped |
56
+
57
+
58
+ ### Flags
59
+
60
+ | Flag | Description |
61
+ |------|-------------|
62
+ | `-o` | Only show differences — hide identical files/lines |
63
+
64
+ ## Publishing to PyPI
65
+
66
+ ```
67
+ pip install build twine
68
+ python -m build
69
+ twine upload dist/*
70
+ ```
71
+
72
+ When prompted, use `__token__` as the username and your PyPI API token as the password.
73
+
74
+ To skip the prompt, create a `~/.pypirc` file:
75
+
76
+ ```ini
77
+ [pypi]
78
+ username = __token__
79
+ password = pypi-YOUR-TOKEN-HERE
80
+ ```
81
+
82
+ > **Note:** Bump the `version` in `pyproject.toml` before each upload — PyPI rejects duplicate version numbers.
83
+
84
+ ## Requirements
85
+
86
+ - Python >= 3.9
87
+ - No external dependencies
88
+
89
+ ## License
90
+
91
+ MIT
@@ -0,0 +1,83 @@
1
+ # peter-diff
2
+
3
+ A Python CLI tool for side-by-side diffing of files and folders.
4
+
5
+ ![](image/2026-05-23T01_18_19.png)
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install -e .
11
+ ```
12
+ OR
13
+
14
+ ```sh
15
+ pip install peter-diff
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Compare two files
21
+
22
+ ```sh
23
+ peter-diff file1.txt file2.txt
24
+ ```
25
+
26
+ Displays a side-by-side diff with line numbers. Changed characters within a line are highlighted. Identical files print `same`.
27
+
28
+ ![](image/file.png)
29
+
30
+ ### Compare two folders
31
+
32
+ ```sh
33
+ peter-diff dir1/ dir2/
34
+ ```
35
+
36
+ Displays a side-by-side tree view of both directories:
37
+
38
+ ![](image/folder.png)
39
+
40
+ Status indicators:
41
+ | Symbol | Meaning |
42
+ |--------|---------|
43
+ | *(none)* | identical |
44
+ | `diff` | file differs |
45
+ | `←` | only in left folder |
46
+ | `→` | only in right folder |
47
+ | `bin` | binary / unreadable, skipped |
48
+
49
+
50
+ ### Flags
51
+
52
+ | Flag | Description |
53
+ |------|-------------|
54
+ | `-o` | Only show differences — hide identical files/lines |
55
+
56
+ ## Publishing to PyPI
57
+
58
+ ```
59
+ pip install build twine
60
+ python -m build
61
+ twine upload dist/*
62
+ ```
63
+
64
+ When prompted, use `__token__` as the username and your PyPI API token as the password.
65
+
66
+ To skip the prompt, create a `~/.pypirc` file:
67
+
68
+ ```ini
69
+ [pypi]
70
+ username = __token__
71
+ password = pypi-YOUR-TOKEN-HERE
72
+ ```
73
+
74
+ > **Note:** Bump the `version` in `pyproject.toml` before each upload — PyPI rejects duplicate version numbers.
75
+
76
+ ## Requirements
77
+
78
+ - Python >= 3.9
79
+ - No external dependencies
80
+
81
+ ## License
82
+
83
+ MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "peter-diff"
3
- version = "0.1.1"
3
+ version = "0.1.3"
4
4
  description = "A simple CLI tool to diff two text files."
5
5
  license = { text = "MIT" }
6
6
  readme = "README.md"
@@ -1,6 +1,8 @@
1
+ import os
1
2
  import sys
2
3
  import difflib
3
4
  import shutil
5
+ from pathlib import Path
4
6
 
5
7
  # ANSI codes
6
8
  _RESET = "\033[0m"
@@ -76,6 +78,10 @@ def _char_diff(left, right, col):
76
78
 
77
79
 
78
80
  def side_by_side_diff(lines1, lines2, file1, file2, only_diff=False):
81
+ # if lines1 same as lines2, just print one of them
82
+ if lines1 == lines2:
83
+ print("same")
84
+ return
79
85
  term_w = shutil.get_terminal_size((160, 40)).columns
80
86
  # layout: "NNNN M content … │ NNNN M content …"
81
87
  # 4 + 1+1+1 = 7 chars overhead each side + 3 for " │ "
@@ -146,6 +152,107 @@ def side_by_side_diff(lines1, lines2, file1, file2, only_diff=False):
146
152
  print(sep)
147
153
 
148
154
 
155
+ def compare_dirs(dir1, dir2, only_diff=False):
156
+ p1, p2 = Path(dir1), Path(dir2)
157
+
158
+ def get_files(base):
159
+ result = set()
160
+ for f in base.rglob("*"):
161
+ if f.is_file():
162
+ result.add(f.relative_to(base))
163
+ return result
164
+
165
+ files1 = get_files(p1)
166
+ files2 = get_files(p2)
167
+ all_paths = sorted(files1 | files2, key=str)
168
+
169
+ # Precompute per-file status
170
+ status_map = {}
171
+ for rel in all_paths:
172
+ in1, in2 = rel in files1, rel in files2
173
+ if not in1:
174
+ status_map[rel] = "only2"
175
+ elif not in2:
176
+ status_map[rel] = "only1"
177
+ else:
178
+ try:
179
+ l1 = (p1 / rel).read_text(errors="replace").splitlines(keepends=True)
180
+ l2 = (p2 / rel).read_text(errors="replace").splitlines(keepends=True)
181
+ status_map[rel] = "same" if l1 == l2 else "diff"
182
+ except Exception:
183
+ status_map[rel] = "binary"
184
+
185
+ entries = [(rel, status_map[rel]) for rel in all_paths
186
+ if not only_diff or status_map[rel] != "same"]
187
+
188
+ _LABEL = {"same": "", "diff": "diff", "only1": "←", "only2": "→", "binary": "bin"}
189
+ rows = [] # (left_str, status_label, right_str)
190
+
191
+ def walk(items, pl, pr):
192
+ groups = {}
193
+ for rel, st in items:
194
+ k = rel.parts[0]
195
+ if k not in groups:
196
+ groups[k] = {"status": None, "children": []}
197
+ if len(rel.parts) == 1:
198
+ groups[k]["status"] = st
199
+ else:
200
+ groups[k]["children"].append((Path(*rel.parts[1:]), st))
201
+
202
+ sorted_keys = sorted(groups)
203
+ for idx, k in enumerate(sorted_keys):
204
+ g = groups[k]
205
+ last = (idx == len(sorted_keys) - 1)
206
+ conn = "└── " if last else "├── "
207
+ cont = " " if last else "│ "
208
+
209
+ if g["status"] is not None: # file
210
+ st = g["status"]
211
+ in1, in2 = st != "only2", st != "only1"
212
+ rows.append((
213
+ (pl + conn + k) if in1 else "",
214
+ _LABEL[st],
215
+ (pr + conn + k) if in2 else "",
216
+ ))
217
+ else: # directory
218
+ ch = g["children"]
219
+ in1 = any(s != "only2" for _, s in ch)
220
+ in2 = any(s != "only1" for _, s in ch)
221
+ rows.append((
222
+ (pl + conn + k + "/") if in1 else "",
223
+ "",
224
+ (pr + conn + k + "/") if in2 else "",
225
+ ))
226
+ walk(ch,
227
+ pl + (cont if in1 else " "),
228
+ pr + (cont if in2 else " "))
229
+
230
+ walk(entries, "", "")
231
+
232
+ # Render side by side
233
+ term_w = shutil.get_terminal_size((160, 40)).columns
234
+ st_w = 5
235
+ col = max(24, (term_w - st_w - 4) // 2)
236
+
237
+ use_color = _color_ok()
238
+ _ST_COLOR = {"diff": _YELLOW, "←": _RED, "→": _GREEN}
239
+
240
+ def fmt_st(s):
241
+ padded = s.ljust(st_w)
242
+ if use_color and s in _ST_COLOR:
243
+ return _ST_COLOR[s] + padded + _RESET
244
+ return padded
245
+
246
+ div = "─" * (col + 1 + st_w) + "─┼─" + "─" * col
247
+ lh = (_BOLD if use_color else "") + _pad(dir1, col) + (_RESET if use_color else "")
248
+ rh = (_BOLD if use_color else "") + dir2 + (_RESET if use_color else "")
249
+ print(f"{lh} {' ' * st_w} │ {rh}")
250
+ print(div)
251
+ for left, st, right in rows:
252
+ print(f"{_pad(left, col)} {fmt_st(st)} │ {right}")
253
+ print(div)
254
+
255
+
149
256
  def main():
150
257
  args = sys.argv[1:]
151
258
  only_diff = "-o" in args
@@ -153,15 +260,23 @@ def main():
153
260
  if len(args) != 2:
154
261
  print("Usage: peter-diff [-o] file1 file2", file=sys.stderr)
155
262
  sys.exit(1)
156
- file1, file2 = args[0], args[1]
263
+ path1, path2 = args[0], args[1]
264
+
265
+ if os.path.isdir(path1) and os.path.isdir(path2):
266
+ compare_dirs(path1, path2, only_diff=only_diff)
267
+ return
268
+ if os.path.isdir(path1) or os.path.isdir(path2):
269
+ print("Error: cannot compare a file with a directory", file=sys.stderr)
270
+ sys.exit(1)
271
+
157
272
  try:
158
- with open(file1) as f1, open(file2) as f2:
273
+ with open(path1) as f1, open(path2) as f2:
159
274
  lines1 = f1.readlines()
160
275
  lines2 = f2.readlines()
161
276
  except Exception as e:
162
277
  print(f"Error: {e}", file=sys.stderr)
163
278
  sys.exit(1)
164
- side_by_side_diff(lines1, lines2, file1, file2, only_diff=only_diff)
279
+ side_by_side_diff(lines1, lines2, path1, path2, only_diff=only_diff)
165
280
 
166
281
 
167
282
  if __name__ == "__main__":
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: peter-diff
3
+ Version: 0.1.3
4
+ Summary: A simple CLI tool to diff two text files.
5
+ License: MIT
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+
9
+ # peter-diff
10
+
11
+ A Python CLI tool for side-by-side diffing of files and folders.
12
+
13
+ ![](image/2026-05-23T01_18_19.png)
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install -e .
19
+ ```
20
+ OR
21
+
22
+ ```sh
23
+ pip install peter-diff
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Compare two files
29
+
30
+ ```sh
31
+ peter-diff file1.txt file2.txt
32
+ ```
33
+
34
+ Displays a side-by-side diff with line numbers. Changed characters within a line are highlighted. Identical files print `same`.
35
+
36
+ ![](image/file.png)
37
+
38
+ ### Compare two folders
39
+
40
+ ```sh
41
+ peter-diff dir1/ dir2/
42
+ ```
43
+
44
+ Displays a side-by-side tree view of both directories:
45
+
46
+ ![](image/folder.png)
47
+
48
+ Status indicators:
49
+ | Symbol | Meaning |
50
+ |--------|---------|
51
+ | *(none)* | identical |
52
+ | `diff` | file differs |
53
+ | `←` | only in left folder |
54
+ | `→` | only in right folder |
55
+ | `bin` | binary / unreadable, skipped |
56
+
57
+
58
+ ### Flags
59
+
60
+ | Flag | Description |
61
+ |------|-------------|
62
+ | `-o` | Only show differences — hide identical files/lines |
63
+
64
+ ## Publishing to PyPI
65
+
66
+ ```
67
+ pip install build twine
68
+ python -m build
69
+ twine upload dist/*
70
+ ```
71
+
72
+ When prompted, use `__token__` as the username and your PyPI API token as the password.
73
+
74
+ To skip the prompt, create a `~/.pypirc` file:
75
+
76
+ ```ini
77
+ [pypi]
78
+ username = __token__
79
+ password = pypi-YOUR-TOKEN-HERE
80
+ ```
81
+
82
+ > **Note:** Bump the `version` in `pyproject.toml` before each upload — PyPI rejects duplicate version numbers.
83
+
84
+ ## Requirements
85
+
86
+ - Python >= 3.9
87
+ - No external dependencies
88
+
89
+ ## License
90
+
91
+ MIT
@@ -1,8 +1,8 @@
1
1
  MANIFEST.in
2
2
  README.md
3
3
  pyproject.toml
4
- src/peter-diff/__init__.py
5
- src/peter-diff/cli.py
4
+ src/peter_diff/__init__.py
5
+ src/peter_diff/cli.py
6
6
  src/peter_diff.egg-info/PKG-INFO
7
7
  src/peter_diff.egg-info/SOURCES.txt
8
8
  src/peter_diff.egg-info/dependency_links.txt
@@ -0,0 +1 @@
1
+ peter_diff
peter_diff-0.1.1/PKG-INFO DELETED
@@ -1,58 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: peter-diff
3
- Version: 0.1.1
4
- Summary: A simple CLI tool to diff two text files.
5
- License: MIT
6
- Requires-Python: >=3.7
7
- Description-Content-Type: text/markdown
8
-
9
- # peter-diff
10
-
11
- A simple Python CLI tool to compare two text files and output their differences, similar to the Unix `diff` command.
12
-
13
- ## Installation
14
-
15
- ```bash
16
- pip install -e .
17
- pip uninstall peter-diff
18
- ```
19
- OR
20
-
21
- ```sh
22
- pip install peter-diff
23
- ```
24
-
25
- ## Usage
26
-
27
- ```sh
28
- pydiff file1.txt file2.txt
29
- ```
30
-
31
- ## Publishing to PyPI
32
-
33
- ```
34
- pip install build twine
35
- python -m build
36
- twine upload dist/*
37
- ```
38
-
39
- When prompted, use `__token__` as the username and your PyPI API token as the password.
40
-
41
- To skip the prompt, create a `~/.pypirc` file:
42
-
43
- ```ini
44
- [pypi]
45
- username = __token__
46
- password = pypi-YOUR-TOKEN-HERE
47
- ```
48
-
49
- > **Note:** Bump the `version` in `pyproject.toml` before each upload — PyPI rejects duplicate version numbers.
50
-
51
- ## Requirements
52
-
53
- - Python >= 3.9
54
- - No external dependencies
55
-
56
- ## License
57
-
58
- MIT
@@ -1,50 +0,0 @@
1
- # peter-diff
2
-
3
- A simple Python CLI tool to compare two text files and output their differences, similar to the Unix `diff` command.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pip install -e .
9
- pip uninstall peter-diff
10
- ```
11
- OR
12
-
13
- ```sh
14
- pip install peter-diff
15
- ```
16
-
17
- ## Usage
18
-
19
- ```sh
20
- pydiff file1.txt file2.txt
21
- ```
22
-
23
- ## Publishing to PyPI
24
-
25
- ```
26
- pip install build twine
27
- python -m build
28
- twine upload dist/*
29
- ```
30
-
31
- When prompted, use `__token__` as the username and your PyPI API token as the password.
32
-
33
- To skip the prompt, create a `~/.pypirc` file:
34
-
35
- ```ini
36
- [pypi]
37
- username = __token__
38
- password = pypi-YOUR-TOKEN-HERE
39
- ```
40
-
41
- > **Note:** Bump the `version` in `pyproject.toml` before each upload — PyPI rejects duplicate version numbers.
42
-
43
- ## Requirements
44
-
45
- - Python >= 3.9
46
- - No external dependencies
47
-
48
- ## License
49
-
50
- MIT
@@ -1,58 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: peter-diff
3
- Version: 0.1.1
4
- Summary: A simple CLI tool to diff two text files.
5
- License: MIT
6
- Requires-Python: >=3.7
7
- Description-Content-Type: text/markdown
8
-
9
- # peter-diff
10
-
11
- A simple Python CLI tool to compare two text files and output their differences, similar to the Unix `diff` command.
12
-
13
- ## Installation
14
-
15
- ```bash
16
- pip install -e .
17
- pip uninstall peter-diff
18
- ```
19
- OR
20
-
21
- ```sh
22
- pip install peter-diff
23
- ```
24
-
25
- ## Usage
26
-
27
- ```sh
28
- pydiff file1.txt file2.txt
29
- ```
30
-
31
- ## Publishing to PyPI
32
-
33
- ```
34
- pip install build twine
35
- python -m build
36
- twine upload dist/*
37
- ```
38
-
39
- When prompted, use `__token__` as the username and your PyPI API token as the password.
40
-
41
- To skip the prompt, create a `~/.pypirc` file:
42
-
43
- ```ini
44
- [pypi]
45
- username = __token__
46
- password = pypi-YOUR-TOKEN-HERE
47
- ```
48
-
49
- > **Note:** Bump the `version` in `pyproject.toml` before each upload — PyPI rejects duplicate version numbers.
50
-
51
- ## Requirements
52
-
53
- - Python >= 3.9
54
- - No external dependencies
55
-
56
- ## License
57
-
58
- MIT
@@ -1 +0,0 @@
1
- peter-diff
File without changes
File without changes