treex-cli 0.2.2__tar.gz → 0.2.4__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.
@@ -352,9 +352,9 @@ dependencies = [
352
352
 
353
353
  [[package]]
354
354
  name = "gix-config"
355
- version = "0.62.1"
355
+ version = "0.62.2"
356
356
  source = "registry+https://github.com/rust-lang/crates.io-index"
357
- checksum = "2d8de82251a43c9f845458cd64e565f7350ce446bb786348f79d80dc5089806e"
357
+ checksum = "3e11ccf82e9630c4e7befbae07cbe90e79de61dc73a34448c2dec70a13da9ebf"
358
358
  dependencies = [
359
359
  "bstr",
360
360
  "gix-config-value",
@@ -496,9 +496,9 @@ dependencies = [
496
496
 
497
497
  [[package]]
498
498
  name = "gix-hashtable"
499
- version = "0.18.0"
499
+ version = "0.18.1"
500
500
  source = "registry+https://github.com/rust-lang/crates.io-index"
501
- checksum = "916cb6b2ec42ffaca624e7867bb382a4673015cb4ffcd7330b48085d02e4c615"
501
+ checksum = "2f66822aee11ccb28d6a2e63b587254e578fb52a138bd5806b58d8560125592b"
502
502
  dependencies = [
503
503
  "gix-hash",
504
504
  "hashbrown 0.17.1",
@@ -507,9 +507,9 @@ dependencies = [
507
507
 
508
508
  [[package]]
509
509
  name = "gix-ignore"
510
- version = "0.24.0"
510
+ version = "0.24.1"
511
511
  source = "registry+https://github.com/rust-lang/crates.io-index"
512
- checksum = "cb1c93f60845d493a72fde38ae237498bc4e4464f45afa4af23e53f8295a3ba1"
512
+ checksum = "968f12fe42896873504f46127cd5bf0f9a34fc1479a781f43cf94b5d8d760a15"
513
513
  dependencies = [
514
514
  "bstr",
515
515
  "gix-glob",
@@ -660,9 +660,9 @@ dependencies = [
660
660
 
661
661
  [[package]]
662
662
  name = "gix-parallel"
663
- version = "0.50.0"
663
+ version = "0.50.1"
664
664
  source = "registry+https://github.com/rust-lang/crates.io-index"
665
- checksum = "e994cf1eec247ba4be7f9e7c0d58cc73f83eecb5fcb9feb595df5ba3d2078546"
665
+ checksum = "58e7e10f8343e05f6ad8651f97dffa1caca1531bb274bfc8e3050e80e3ed5946"
666
666
  dependencies = [
667
667
  "crossbeam-channel",
668
668
  "gix-trace",
@@ -777,9 +777,9 @@ dependencies = [
777
777
 
778
778
  [[package]]
779
779
  name = "gix-sec"
780
- version = "0.16.0"
780
+ version = "0.16.1"
781
781
  source = "registry+https://github.com/rust-lang/crates.io-index"
782
- checksum = "5640f642a1b471616d48230b1def7ca743cb30903441ff84800cafbab3c0fddf"
782
+ checksum = "9598d1f35baec7038c2d278bfcc18f1d8951f41dcc3f51ab6175661dfe5a6137"
783
783
  dependencies = [
784
784
  "bitflags 2.13.2",
785
785
  "gix-path",
@@ -869,9 +869,9 @@ dependencies = [
869
869
 
870
870
  [[package]]
871
871
  name = "gix-utils"
872
- version = "0.5.1"
872
+ version = "0.5.2"
873
873
  source = "registry+https://github.com/rust-lang/crates.io-index"
874
- checksum = "f8e770b8c34311044d1addc7b18957db0ac27183707e1140dc90e6f674a6354e"
874
+ checksum = "11a9446060447e9ec58f5d333b98bed53389dfa10de5d8dd46fc76d01dfc2954"
875
875
  dependencies = [
876
876
  "bstr",
877
877
  "bytes",
@@ -1257,9 +1257,9 @@ checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
1257
1257
 
1258
1258
  [[package]]
1259
1259
  name = "rustix"
1260
- version = "1.1.5"
1260
+ version = "1.1.6"
1261
1261
  source = "registry+https://github.com/rust-lang/crates.io-index"
1262
- checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"
1262
+ checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa"
1263
1263
  dependencies = [
1264
1264
  "bitflags 2.13.2",
1265
1265
  "errno",
@@ -1332,9 +1332,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
1332
1332
 
1333
1333
  [[package]]
1334
1334
  name = "smallvec"
1335
- version = "1.16.3"
1335
+ version = "1.16.4"
1336
1336
  source = "registry+https://github.com/rust-lang/crates.io-index"
1337
- checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b"
1337
+ checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8"
1338
1338
 
1339
1339
  [[package]]
1340
1340
  name = "stable_deref_trait"
@@ -1411,7 +1411,7 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee"
1411
1411
 
1412
1412
  [[package]]
1413
1413
  name = "treex-native"
1414
- version = "0.2.2"
1414
+ version = "0.2.4"
1415
1415
  dependencies = [
1416
1416
  "gix",
1417
1417
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "treex-native"
3
- version = "0.2.2"
3
+ version = "0.2.4"
4
4
  edition = "2024"
5
5
 
6
6
  [lib]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: treex-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Command-line directory tree utility with metadata and Git ignore support
5
5
  Author: Corey Goldberg
6
6
  Maintainer: Corey Goldberg
@@ -110,8 +110,8 @@ Prebuilt wheels are available for common platforms. See
110
110
  ## Git ignore support
111
111
 
112
112
  `treex` uses [Gitoxide][gitoxide-home] (`gix`), a native Rust implementation of
113
- Git functionality, to evaluate `.gitignore` rules without invoking the Git
114
- executable. Git does not need to be installed for ignore matching to work.
113
+ Git, to evaluate `.gitignore` rules without invoking the Git executable. Git
114
+ does not need to be installed for ignore matching to work.
115
115
 
116
116
  When the specified directory is inside a Git working tree, `treex` respects
117
117
  applicable `.gitignore` rules by default. Outside a Git working tree, Git ignore
@@ -87,8 +87,8 @@ Prebuilt wheels are available for common platforms. See
87
87
  ## Git ignore support
88
88
 
89
89
  `treex` uses [Gitoxide][gitoxide-home] (`gix`), a native Rust implementation of
90
- Git functionality, to evaluate `.gitignore` rules without invoking the Git
91
- executable. Git does not need to be installed for ignore matching to work.
90
+ Git, to evaluate `.gitignore` rules without invoking the Git executable. Git
91
+ does not need to be installed for ignore matching to work.
92
92
 
93
93
  When the specified directory is inside a Git working tree, `treex` respects
94
94
  applicable `.gitignore` rules by default. Outside a Git working tree, Git ignore
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "treex-cli"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description="Command-line directory tree utility with metadata and Git ignore support"
9
9
  requires-python = ">= 3.13"
10
10
  license = "MIT"
@@ -37,13 +37,14 @@ repository = "https://github.com/cgoldberg/treex"
37
37
 
38
38
  [dependency-groups]
39
39
  build = [
40
- "cibuildwheel==4.3.4",
40
+ "cibuildwheel==4.3.5",
41
+ "twine==7.0.0",
41
42
  ]
42
43
  lint = [
43
- "ruff==0.17.2",
44
+ "ruff==0.17.3",
44
45
  ]
45
46
  test = [
46
- "pytest==9.1.1",
47
+ "pytest==9.2.0",
47
48
  ]
48
49
  validate = [
49
50
  "validate-pyproject[all]==0.27",
@@ -64,12 +65,10 @@ py-limited-api = true
64
65
 
65
66
  [tool.cibuildwheel]
66
67
  build = "cp313-*"
67
- test-requires = ["pytest==9.1.1"]
68
- test-command = "pytest {project}/tests"
69
-
70
- [tool.cibuildwheel.linux]
71
68
  before-all = "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal"
72
69
  environment = { PATH = "$HOME/.cargo/bin:$PATH" }
70
+ test-requires = ["pytest==9.2.0"]
71
+ test-command = "pytest {project}"
73
72
 
74
73
  [tool.pytest]
75
74
  addopts = ["-v"]
@@ -15,6 +15,7 @@ the filesystem is scanned normally without Git filtering.
15
15
 
16
16
  import argparse
17
17
  import fnmatch
18
+ import os
18
19
  import re
19
20
  import sys
20
21
  from datetime import datetime
@@ -29,6 +30,9 @@ except PackageNotFoundError:
29
30
  __version__ = "unknown"
30
31
 
31
32
 
33
+ PROG = "treex"
34
+
35
+
32
36
  class GitIgnore:
33
37
  """Apply Git ignore rules using Gitoxide when inside a repository."""
34
38
 
@@ -53,26 +57,29 @@ class GitIgnore:
53
57
  """Check if Git considers a path ignored."""
54
58
  return bool(self.ignored_many([path]))
55
59
 
56
- def ignored_many(self, paths):
57
- """Check which paths Git considers ignored."""
60
+ def ignored_many(self, paths, directory_flags=None):
61
+ """Return the paths that Git considers ignored."""
58
62
  if not self.enabled or self.repo_root is None or self.matcher is None:
59
63
  return set()
60
- relative_paths = []
64
+ candidates = []
61
65
  matcher_paths = []
62
66
  for path in paths:
63
67
  try:
64
- relative = path.resolve().relative_to(self.repo_root)
68
+ relative = path.relative_to(self.repo_root)
65
69
  except (ValueError, OSError):
66
70
  continue
67
- relative_paths.append(relative.as_posix())
68
- matcher_paths.append((relative.as_posix(), path.is_dir()))
71
+ candidates.append(path)
72
+ is_dir = (
73
+ directory_flags[path]
74
+ if directory_flags is not None and path in directory_flags
75
+ else path.is_dir()
76
+ )
77
+ matcher_paths.append((relative.as_posix(), is_dir))
69
78
  if not matcher_paths:
70
79
  return set()
71
80
  results = self.matcher.ignored_many(matcher_paths)
72
81
  return {
73
- relative
74
- for relative, ignored in zip(relative_paths, results, strict=True)
75
- if ignored
82
+ path for path, ignored in zip(candidates, results, strict=True) if ignored
76
83
  }
77
84
 
78
85
 
@@ -125,7 +132,14 @@ def _count_lines(path):
125
132
 
126
133
 
127
134
  def _file_metadata(path):
128
- """Get file metadata."""
135
+ """Collect file metadata, identifying binary files and counting text lines.
136
+
137
+ Reads files in chunks to avoid loading large files into memory. Checks
138
+ the initial bytes for NUL characters and invalid UTF-8 to identify binary
139
+ files, and counts lines for text files in a single pass. Empty files are
140
+ handled without opening them. Returns formatted size, line count or file
141
+ status, byte size, and modification time.
142
+ """
129
143
  try:
130
144
  stat = path.stat()
131
145
  size = stat.st_size
@@ -133,37 +147,71 @@ def _file_metadata(path):
133
147
  except OSError:
134
148
  return "?", "[unreadable]", 0, None
135
149
  size_text = _format_size(size)
136
- if _is_binary_file(path):
150
+ if size == 0:
151
+ return size_text, "0 lines", size, modified
152
+ try:
153
+ f = path.open("rb")
154
+ except OSError:
137
155
  return size_text, "[binary]", size, modified
138
- lines = _count_lines(path)
139
- if lines is None:
140
- return size_text, "[unreadable]", size, modified
156
+ with f:
157
+ try:
158
+ chunk = f.read(8192)
159
+ except OSError:
160
+ return size_text, "[binary]", size, modified
161
+ if chunk:
162
+ if b"\x00" in chunk:
163
+ return size_text, "[binary]", size, modified
164
+ try:
165
+ chunk.decode("utf-8")
166
+ except UnicodeDecodeError:
167
+ return size_text, "[binary]", size, modified
168
+ lines = chunk.count(b"\n")
169
+ last_byte = chunk[-1:] if chunk else b""
170
+ has_data = bool(chunk)
171
+ # A short read from a regular file normally means EOF.
172
+ if len(chunk) == 8192:
173
+ try:
174
+ while True:
175
+ chunk = f.read(65536)
176
+ if not chunk:
177
+ break
178
+ has_data = True
179
+ lines += chunk.count(b"\n")
180
+ last_byte = chunk[-1:]
181
+ except OSError:
182
+ return size_text, "[unreadable]", size, modified
183
+ if has_data and last_byte != b"\n":
184
+ lines += 1
141
185
  return size_text, f"{lines:,} lines", size, modified
142
186
 
143
187
 
144
188
  def _get_tree_entries(path, gitignore):
145
- """Get entries to include in the tree."""
189
+ """Get entries to include in the tree with cached type information."""
190
+ typed_entries = []
146
191
  try:
147
- entries = list(path.iterdir())
192
+ # Use os.scandir() instead of Path.iterdir(): DirEntry caches
193
+ # filesystem metadata, reducing extra system calls for type checks.
194
+ entries = os.scandir(path)
148
195
  except PermissionError:
149
196
  return None
150
- entries = [entry for entry in entries if not entry.is_symlink()]
197
+ with entries:
198
+ for dir_entry in entries:
199
+ if dir_entry.is_symlink():
200
+ continue
201
+ is_dir = dir_entry.is_dir(follow_symlinks=False)
202
+ is_file = dir_entry.is_file(follow_symlinks=False)
203
+ typed_entries.append((Path(dir_entry.path), is_dir, is_file))
151
204
  if gitignore is not None:
152
- entries = [entry for entry in entries if entry.name != ".git"]
205
+ typed_entries = [item for item in typed_entries if item[0].name != ".git"]
153
206
  if gitignore.enabled and gitignore.repo_root is not None:
154
- ignored = gitignore.ignored_many(entries)
155
- filtered_entries = []
156
- for entry in entries:
157
- try:
158
- relative = entry.resolve().relative_to(gitignore.repo_root)
159
- except (ValueError, OSError):
160
- filtered_entries.append(entry)
161
- continue
162
- if relative.as_posix() not in ignored:
163
- filtered_entries.append(entry)
164
- entries = filtered_entries
165
- entries.sort(key=lambda p: (p.is_file(), p.name.casefold()))
166
- return entries
207
+ directory_flags = {entry: is_dir for entry, is_dir, _ in typed_entries}
208
+ ignored = gitignore.ignored_many(
209
+ [entry for entry, _, _ in typed_entries],
210
+ directory_flags,
211
+ )
212
+ typed_entries = [item for item in typed_entries if item[0] not in ignored]
213
+ typed_entries.sort(key=lambda item: (item[2], item[0].name.casefold()))
214
+ return typed_entries
167
215
 
168
216
 
169
217
  def _matches_pattern(path, pattern):
@@ -183,6 +231,7 @@ def _scan_tree(
183
231
  max_depth=None,
184
232
  depth=0,
185
233
  root=None,
234
+ relative_prefix="",
186
235
  ):
187
236
  """Scan and filter the tree, returning nodes for rendering.
188
237
 
@@ -194,8 +243,8 @@ def _scan_tree(
194
243
  Directory sizes include only files within the permitted depth
195
244
  that pass the include/exclude filters.
196
245
  """
197
- path = path.resolve()
198
246
  if root is None:
247
+ path = path.resolve()
199
248
  root = path
200
249
  if max_depth is not None and depth >= max_depth:
201
250
  return []
@@ -214,14 +263,14 @@ def _scan_tree(
214
263
  ]
215
264
  nodes = []
216
265
  has_include = include_pattern is not None or include_regex is not None
217
- for entry in entries:
218
- relative_path = entry.relative_to(root).as_posix()
219
- # Exclusions take precedence over inclusions.
266
+ for entry, is_dir, is_file in entries:
267
+ name = entry.name
268
+ relative_path = f"{relative_prefix}/{name}" if relative_prefix else name
220
269
  if _matches_pattern(relative_path, exclude_pattern):
221
270
  continue
222
271
  if exclude_regex is not None and exclude_regex.search(relative_path):
223
272
  continue
224
- if entry.is_dir():
273
+ if is_dir:
225
274
  entry_depth = depth + 1
226
275
  if max_depth is not None and entry_depth >= max_depth:
227
276
  children = []
@@ -238,6 +287,7 @@ def _scan_tree(
238
287
  max_depth,
239
288
  entry_depth,
240
289
  root,
290
+ relative_path,
241
291
  )
242
292
  contains_match = any(child["contains_match"] for child in children)
243
293
  if has_include and not contains_match:
@@ -247,7 +297,7 @@ def _scan_tree(
247
297
  )
248
298
  nodes.append(
249
299
  {
250
- "name": entry.name,
300
+ "name": name,
251
301
  "kind": "directory",
252
302
  "children": children,
253
303
  "size": size,
@@ -257,7 +307,7 @@ def _scan_tree(
257
307
  }
258
308
  )
259
309
  continue
260
- if not entry.is_file():
310
+ if not is_file:
261
311
  continue
262
312
  if include_pattern is not None and not _matches_pattern(
263
313
  relative_path,
@@ -272,7 +322,7 @@ def _scan_tree(
272
322
  size_text, info, size, modified = None, None, None, None
273
323
  nodes.append(
274
324
  {
275
- "name": entry.name,
325
+ "name": name,
276
326
  "kind": "file",
277
327
  "children": [],
278
328
  "size": size,
@@ -455,7 +505,7 @@ def parse_args(argv=None):
455
505
  parser = argparse.ArgumentParser(
456
506
  description="List directory contents as a tree "
457
507
  "with file and directory metadata.",
458
- usage="treex [OPTIONS] [directory]",
508
+ usage=f"{PROG} [OPTIONS] [directory]",
459
509
  formatter_class=formatter,
460
510
  )
461
511
  parser.add_argument(
@@ -469,7 +519,7 @@ def parse_args(argv=None):
469
519
  "-V",
470
520
  "--version",
471
521
  action="version",
472
- version=f"treex {__version__}",
522
+ version=f"{PROG} {__version__}",
473
523
  help="show version information and exit",
474
524
  )
475
525
  parser.add_argument(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: treex-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Command-line directory tree utility with metadata and Git ignore support
5
5
  Author: Corey Goldberg
6
6
  Maintainer: Corey Goldberg
@@ -110,8 +110,8 @@ Prebuilt wheels are available for common platforms. See
110
110
  ## Git ignore support
111
111
 
112
112
  `treex` uses [Gitoxide][gitoxide-home] (`gix`), a native Rust implementation of
113
- Git functionality, to evaluate `.gitignore` rules without invoking the Git
114
- executable. Git does not need to be installed for ignore matching to work.
113
+ Git, to evaluate `.gitignore` rules without invoking the Git executable. Git
114
+ does not need to be installed for ignore matching to work.
115
115
 
116
116
  When the specified directory is inside a Git working tree, `treex` respects
117
117
  applicable `.gitignore` rules by default. Outside a Git working tree, Git ignore
@@ -223,7 +223,7 @@ class TestGitIgnore:
223
223
  ignored_dir.mkdir()
224
224
  paths = [ignored_file, normal_file, ignored_dir]
225
225
  gitignore = treex.GitIgnore(tmp_path)
226
- assert gitignore.ignored_many(paths) == {"debug.log", "build"}
226
+ assert gitignore.ignored_many(paths) == {ignored_file, ignored_dir}
227
227
 
228
228
  def test_ignored_many_handles_spaces(self, tmp_path):
229
229
  init_git_repo(tmp_path)
@@ -234,7 +234,7 @@ class TestGitIgnore:
234
234
  ignored_dir.mkdir()
235
235
  paths = [ignored_file, ignored_dir]
236
236
  gitignore = treex.GitIgnore(tmp_path)
237
- assert gitignore.ignored_many(paths) == {"debug output.log", "build output"}
237
+ assert gitignore.ignored_many(paths) == {ignored_file, ignored_dir}
238
238
 
239
239
  def test_missing_git_index(self, tmp_path):
240
240
  init_git_repo(tmp_path)
@@ -260,7 +260,7 @@ class TestTreeEntries:
260
260
  (tmp_path / "Docs").mkdir()
261
261
  (tmp_path / ".git").mkdir()
262
262
  entries = treex._get_tree_entries(tmp_path, gitignore=None)
263
- assert [entry.name for entry in entries] == [
263
+ assert [entry[0].name for entry in entries] == [
264
264
  ".git",
265
265
  "Docs",
266
266
  "src",
@@ -278,7 +278,7 @@ class TestTreeEntries:
278
278
  link = tmp_path / "link.txt"
279
279
  link.symlink_to(target)
280
280
  entries = treex._get_tree_entries(tmp_path, gitignore=None)
281
- assert [entry.name for entry in entries] == ["target.txt"]
281
+ assert [entry[0].name for entry in entries] == ["target.txt"]
282
282
 
283
283
 
284
284
  class TestCollectTree:
@@ -380,7 +380,7 @@ class TestCollectTree:
380
380
  def test_collect_tree_permission_denied(self, tmp_path):
381
381
  rows = []
382
382
  stats = {"directories": 0, "files": 0, "total_size": 0}
383
- with patch.object(Path, "iterdir", side_effect=PermissionError):
383
+ with patch("os.scandir", side_effect=PermissionError):
384
384
  treex._collect_tree(tmp_path, "", stats, None, rows, True, False, True)
385
385
  assert rows == [("└── [permission denied]", None)]
386
386
  assert stats["directories"] == 0
@@ -562,6 +562,22 @@ class TestScanTree:
562
562
  )
563
563
  assert nodes == []
564
564
 
565
+ def test_relative_prefix_matches_nested_paths(self, tmp_path):
566
+ nested = tmp_path / "nested"
567
+ nested.mkdir()
568
+ (nested / "app.py").touch()
569
+ (nested / "notes.txt").touch()
570
+ nodes = treex._scan_tree(
571
+ nested,
572
+ None,
573
+ False,
574
+ False,
575
+ include_pattern="src/*.py",
576
+ relative_prefix="src",
577
+ )
578
+ assert [node["name"] for node in nodes] == ["app.py"]
579
+ assert nodes[0]["contains_match"] is True
580
+
565
581
 
566
582
  class TestPrintTreeStatistics:
567
583
  def test_counts_files_and_directories(self, tmp_path):
@@ -913,7 +929,7 @@ class TestPrintTree:
913
929
  def test_reports_permission_denied(self, tmp_path, capsys):
914
930
  directory = tmp_path / "restricted"
915
931
  directory.mkdir()
916
- with patch.object(Path, "iterdir", side_effect=PermissionError):
932
+ with patch("os.scandir", side_effect=PermissionError):
917
933
  treex.print_tree(directory)
918
934
  assert "[permission denied]" in capsys.readouterr().out
919
935
 
@@ -1019,7 +1035,7 @@ class TestArgumentParsing:
1019
1035
  treex.parse_args([option])
1020
1036
  assert exc_info.value.code == 0
1021
1037
  output = capsys.readouterr().out
1022
- assert output.splitlines()[0] == "usage: treex [OPTIONS] [directory]"
1038
+ assert output.splitlines()[0] == f"usage: {treex.PROG} [OPTIONS] [directory]"
1023
1039
 
1024
1040
  @pytest.mark.parametrize("option", ["-V", "--version"])
1025
1041
  def test_version(self, option, capsys):
@@ -1027,7 +1043,7 @@ class TestArgumentParsing:
1027
1043
  treex.parse_args([option])
1028
1044
  assert exc_info.value.code == 0
1029
1045
  output = capsys.readouterr().out
1030
- assert output.strip() == f"treex {treex.__version__}"
1046
+ assert output.strip() == f"{treex.PROG} {treex.__version__}"
1031
1047
 
1032
1048
  @pytest.mark.parametrize("option", ["-a", "--all"])
1033
1049
  def test_all(self, option):
@@ -1164,7 +1180,7 @@ class TestArgumentParsing:
1164
1180
  class TestMain:
1165
1181
  def test_success(self, tmp_path, capsys):
1166
1182
  (tmp_path / "file.txt").write_text("hello", encoding="utf-8")
1167
- with patch.object(sys, "argv", ["treex", str(tmp_path)]):
1183
+ with patch.object(sys, "argv", [treex.PROG, str(tmp_path)]):
1168
1184
  assert treex.main() == 0
1169
1185
  output = capsys.readouterr().out
1170
1186
  assert str(tmp_path) in output
@@ -1176,7 +1192,7 @@ class TestMain:
1176
1192
  directory = tmp_path / "dir"
1177
1193
  directory.mkdir()
1178
1194
  (tmp_path / "file.txt").write_text("hello", encoding="utf-8")
1179
- with patch.object(sys, "argv", ["treex", "--dirs", str(tmp_path)]):
1195
+ with patch.object(sys, "argv", [treex.PROG, "--dirs", str(tmp_path)]):
1180
1196
  assert treex.main() == 0
1181
1197
  output = capsys.readouterr().out
1182
1198
  assert str(tmp_path) in output
@@ -1188,13 +1204,13 @@ class TestMain:
1188
1204
  def test_not_a_directory(self, tmp_path, capsys):
1189
1205
  path = tmp_path / "file.txt"
1190
1206
  path.write_text("hello", encoding="utf-8")
1191
- with patch.object(sys, "argv", ["treex", str(path)]):
1207
+ with patch.object(sys, "argv", [treex.PROG, str(path)]):
1192
1208
  assert treex.main() == 1
1193
1209
  assert f"Not a directory: {path}" in capsys.readouterr().err
1194
1210
 
1195
1211
  def test_keyboard_interrupt(self, tmp_path, capsys):
1196
1212
  with (
1197
- patch.object(sys, "argv", ["treex", str(tmp_path)]),
1213
+ patch.object(sys, "argv", [treex.PROG, str(tmp_path)]),
1198
1214
  patch.object(treex, "print_tree", side_effect=KeyboardInterrupt),
1199
1215
  ):
1200
1216
  assert treex.main() == 130
File without changes
File without changes
File without changes
File without changes