cli2 5.2.2__tar.gz → 5.2.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.
- {cli2-5.2.2/cli2.egg-info → cli2-5.2.3}/PKG-INFO +1 -1
- {cli2-5.2.2 → cli2-5.2.3}/cli2/__init__.py +1 -0
- cli2-5.2.3/cli2/find.py +189 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/proc.py +12 -13
- {cli2-5.2.2 → cli2-5.2.3/cli2.egg-info}/PKG-INFO +1 -1
- {cli2-5.2.2 → cli2-5.2.3}/cli2.egg-info/SOURCES.txt +2 -0
- {cli2-5.2.2 → cli2-5.2.3}/setup.py +1 -1
- cli2-5.2.3/tests/test_find.py +131 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_proc.py +23 -23
- {cli2-5.2.2 → cli2-5.2.3}/MANIFEST.in +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/README.rst +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/classifiers.txt +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/asyncio.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/cli.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/cli2.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/colors.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/configuration.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/decorators.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/display.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/__init__.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/conf.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/example.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/example_obj.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/nesting.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/obj.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/obj2.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/examples/test.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/interactive.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/lock.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/log.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/mask.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/node.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/notlevenshtein.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/sphinx.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/table.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/test.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2/theme.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2.egg-info/dependency_links.txt +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2.egg-info/entry_points.txt +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2.egg-info/requires.txt +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/cli2.egg-info/top_level.txt +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/setup.cfg +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_ansible.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_ansible_variables.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_asyncio.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_cli.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_client.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_client_test.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_command.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_configuration.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_decorators.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_display.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_entry_point.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_group.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_inject.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_interactive.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_lock.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_log.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_mask.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_node.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_notlevenshtein.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_prompt2.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_restful.py +0 -0
- {cli2-5.2.2 → cli2-5.2.3}/tests/test_table.py +0 -0
cli2-5.2.3/cli2/find.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Effecient git-aware file finder with filtering capabilities.
|
|
3
|
+
|
|
4
|
+
Uses Linux commands: find, comm and git-ignore for an efficient path walker.
|
|
5
|
+
|
|
6
|
+
Example usage:
|
|
7
|
+
|
|
8
|
+
.. code-block:: python
|
|
9
|
+
|
|
10
|
+
# Simple usage with default root
|
|
11
|
+
finder = cli2.Find()
|
|
12
|
+
files = finder.files()
|
|
13
|
+
dirs = finder.dirs()
|
|
14
|
+
|
|
15
|
+
# Usage with filters and callback
|
|
16
|
+
def callback(filepath):
|
|
17
|
+
print(f"Found: {filepath}")
|
|
18
|
+
|
|
19
|
+
finder = cli2.Find(
|
|
20
|
+
root="/path/to/repo",
|
|
21
|
+
glob_include=['*.py'],
|
|
22
|
+
glob_exclude=['*test*'],
|
|
23
|
+
file_callback=callback
|
|
24
|
+
)
|
|
25
|
+
files = finder.files()
|
|
26
|
+
dirs = finder.dirs()
|
|
27
|
+
"""
|
|
28
|
+
import cli2
|
|
29
|
+
from fnmatch import fnmatch
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
import os
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Find:
|
|
35
|
+
"""
|
|
36
|
+
A class to walk through files and directories not ignored by git with
|
|
37
|
+
optional filtering.
|
|
38
|
+
|
|
39
|
+
.. py:attribute:: root
|
|
40
|
+
|
|
41
|
+
Root directory for file operations
|
|
42
|
+
|
|
43
|
+
.. py:attribute:: glob_include
|
|
44
|
+
|
|
45
|
+
Optional list of glob patterns to include
|
|
46
|
+
|
|
47
|
+
.. py:attribute:: glob_exclude
|
|
48
|
+
|
|
49
|
+
Optional list of glob patterns to exclude
|
|
50
|
+
|
|
51
|
+
.. py:attribute:: file_callback
|
|
52
|
+
|
|
53
|
+
Optional callback function called for each file or directory
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
root=None,
|
|
59
|
+
glob_include=None,
|
|
60
|
+
glob_exclude=None,
|
|
61
|
+
file_callback=None,
|
|
62
|
+
):
|
|
63
|
+
"""
|
|
64
|
+
Initialize Find with optional root directory, filters, and callback.
|
|
65
|
+
|
|
66
|
+
:param root: Root directory (defaults to current working directory if
|
|
67
|
+
not specified)
|
|
68
|
+
:type root: str or pathlib.Path or None
|
|
69
|
+
:param glob_include: List of glob patterns to include
|
|
70
|
+
:type glob_include: list or None
|
|
71
|
+
:param glob_exclude: List of glob patterns to exclude
|
|
72
|
+
:type glob_exclude: list or None
|
|
73
|
+
:param file_callback: Function to call for each file or directory
|
|
74
|
+
:type file_callback: callable or None
|
|
75
|
+
"""
|
|
76
|
+
self.root = Path(root if root is not None else os.getcwd()).resolve()
|
|
77
|
+
self.glob_include = glob_include if glob_include is not None else []
|
|
78
|
+
self.glob_exclude = glob_exclude if glob_exclude is not None else []
|
|
79
|
+
self.file_callback = file_callback
|
|
80
|
+
|
|
81
|
+
def _matches_filters(self, filepath):
|
|
82
|
+
"""
|
|
83
|
+
Check if a file or directory matches the include/exclude filters.
|
|
84
|
+
|
|
85
|
+
:param filepath: Path to check against filters
|
|
86
|
+
:type filepath: pathlib.Path
|
|
87
|
+
:return: True if path should be included, False otherwise
|
|
88
|
+
:rtype: bool
|
|
89
|
+
"""
|
|
90
|
+
filepath_str = str(filepath.relative_to(self.root))
|
|
91
|
+
|
|
92
|
+
if self.glob_include:
|
|
93
|
+
if not any(
|
|
94
|
+
fnmatch(filepath_str, pattern) for pattern in self.glob_include
|
|
95
|
+
):
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
if self.glob_exclude:
|
|
99
|
+
if any(
|
|
100
|
+
fnmatch(filepath_str, pattern) for pattern in self.glob_exclude
|
|
101
|
+
):
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
def files(self, directory=None):
|
|
107
|
+
"""
|
|
108
|
+
List files not ignored by git, applying filters and callback.
|
|
109
|
+
|
|
110
|
+
:param directory: Directory to start search from (defaults to root if
|
|
111
|
+
not specified)
|
|
112
|
+
:type directory: str or pathlib.Path or None
|
|
113
|
+
:return: List of Path objects for files not ignored by git that match
|
|
114
|
+
filters
|
|
115
|
+
:rtype: list
|
|
116
|
+
:raises RuntimeError: If the git command fails
|
|
117
|
+
"""
|
|
118
|
+
base_path = Path(directory).resolve() if directory else self.root
|
|
119
|
+
|
|
120
|
+
cmd = ' '.join([
|
|
121
|
+
f'comm -23 <(find {base_path} -type f | sort)',
|
|
122
|
+
f'<(find {base_path} -type f | git check-ignore --stdin | sort)'
|
|
123
|
+
])
|
|
124
|
+
proc = cli2.Proc("bash", "-c", cmd).wait()
|
|
125
|
+
|
|
126
|
+
if proc.rc != 0:
|
|
127
|
+
raise RuntimeError(
|
|
128
|
+
f"Command failed with return code {proc.rc}: {proc.stderr}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
files = []
|
|
132
|
+
for line in proc.stdout.splitlines():
|
|
133
|
+
if not line.strip():
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
filepath = (base_path / line.strip()).resolve()
|
|
137
|
+
|
|
138
|
+
if (
|
|
139
|
+
not self.glob_include and not self.glob_exclude
|
|
140
|
+
) or self._matches_filters(filepath):
|
|
141
|
+
files.append(filepath)
|
|
142
|
+
if self.file_callback:
|
|
143
|
+
self.file_callback(filepath)
|
|
144
|
+
|
|
145
|
+
return files
|
|
146
|
+
|
|
147
|
+
def dirs(self, directory=None):
|
|
148
|
+
"""
|
|
149
|
+
List directories not ignored by git, applying filters and callback.
|
|
150
|
+
|
|
151
|
+
:param directory: Directory to start search from (defaults to root if
|
|
152
|
+
not specified)
|
|
153
|
+
:type directory: str or pathlib.Path or None
|
|
154
|
+
:return: List of Path objects for directories not ignored by git that
|
|
155
|
+
match filters
|
|
156
|
+
:rtype: list
|
|
157
|
+
:raises RuntimeError: If the git command fails
|
|
158
|
+
"""
|
|
159
|
+
base_path = Path(directory).resolve() if directory else self.root
|
|
160
|
+
|
|
161
|
+
cmd = ' '.join([
|
|
162
|
+
f'comm -23 <(find {base_path} -type d | sort)',
|
|
163
|
+
f'<(find {base_path} -type d | git check-ignore --stdin | sort)'
|
|
164
|
+
])
|
|
165
|
+
proc = cli2.Proc("bash", "-c", cmd).wait()
|
|
166
|
+
|
|
167
|
+
if proc.rc != 0:
|
|
168
|
+
raise RuntimeError(
|
|
169
|
+
f"Command failed with return code {proc.rc}: {proc.stderr}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
dirs = []
|
|
173
|
+
for line in proc.stdout.splitlines():
|
|
174
|
+
if not line.strip():
|
|
175
|
+
continue
|
|
176
|
+
|
|
177
|
+
dirpath = (base_path / line.strip()).resolve()
|
|
178
|
+
|
|
179
|
+
if dirpath == base_path:
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
not self.glob_include and not self.glob_exclude
|
|
184
|
+
) or self._matches_filters(dirpath):
|
|
185
|
+
dirs.append(dirpath)
|
|
186
|
+
if self.file_callback:
|
|
187
|
+
self.file_callback(dirpath)
|
|
188
|
+
|
|
189
|
+
return dirs
|
|
@@ -14,22 +14,21 @@ Example usage:
|
|
|
14
14
|
# pass shell command in a string for convenience
|
|
15
15
|
proc = cli2.Proc('foo bar')
|
|
16
16
|
|
|
17
|
-
# or as list,
|
|
17
|
+
# or as list, better when building commands
|
|
18
18
|
proc = cli2.Proc('foo', 'bar')
|
|
19
19
|
|
|
20
20
|
# run in sync mode (ie. for jinja2)
|
|
21
|
-
proc.
|
|
21
|
+
proc.wait()
|
|
22
22
|
|
|
23
23
|
# OR run in async loop
|
|
24
|
-
await proc.
|
|
24
|
+
await proc.waita()
|
|
25
25
|
|
|
26
26
|
# You can chain
|
|
27
|
-
proc = cli2.Proc('hi').
|
|
28
|
-
proc = await cli2.Proc('hi').
|
|
27
|
+
proc = cli2.Proc('hi').wait()
|
|
28
|
+
proc = await cli2.Proc('hi').waita()
|
|
29
29
|
|
|
30
30
|
.. note:: There are also start functions, sync and async, in case you want to
|
|
31
31
|
start the proc and wait later.
|
|
32
|
-
|
|
33
32
|
"""
|
|
34
33
|
import asyncio
|
|
35
34
|
import os
|
|
@@ -137,7 +136,7 @@ class Proc:
|
|
|
137
136
|
def cmd(self, value):
|
|
138
137
|
self.args = shlex.split(value)
|
|
139
138
|
|
|
140
|
-
async def
|
|
139
|
+
async def starta(self):
|
|
141
140
|
"""
|
|
142
141
|
Launch the subprocess asynchronously.
|
|
143
142
|
|
|
@@ -167,7 +166,7 @@ class Proc:
|
|
|
167
166
|
)
|
|
168
167
|
return self
|
|
169
168
|
|
|
170
|
-
async def
|
|
169
|
+
async def waita(self):
|
|
171
170
|
"""
|
|
172
171
|
Wait for process completion with timeout handling.
|
|
173
172
|
|
|
@@ -176,7 +175,7 @@ class Proc:
|
|
|
176
175
|
:return: Self reference for method chaining
|
|
177
176
|
"""
|
|
178
177
|
if not self.started:
|
|
179
|
-
await self.
|
|
178
|
+
await self.starta()
|
|
180
179
|
|
|
181
180
|
try:
|
|
182
181
|
if self.timeout:
|
|
@@ -217,7 +216,7 @@ class Proc:
|
|
|
217
216
|
if not self.quiet:
|
|
218
217
|
print(decoded_line)
|
|
219
218
|
|
|
220
|
-
def
|
|
219
|
+
def start(self):
|
|
221
220
|
"""
|
|
222
221
|
Start the subprocess synchronously without waiting for output.
|
|
223
222
|
"""
|
|
@@ -236,16 +235,16 @@ class Proc:
|
|
|
236
235
|
universal_newlines=True
|
|
237
236
|
)
|
|
238
237
|
self.started = True
|
|
239
|
-
# Do NOT start output handling here; defer to
|
|
238
|
+
# Do NOT start output handling here; defer to wait
|
|
240
239
|
return self
|
|
241
240
|
|
|
242
|
-
def
|
|
241
|
+
def wait(self):
|
|
243
242
|
"""
|
|
244
243
|
Wait for process completion synchronously with timeout handling.
|
|
245
244
|
Collects output streams after waiting.
|
|
246
245
|
"""
|
|
247
246
|
if not self.started:
|
|
248
|
-
self.
|
|
247
|
+
self.start()
|
|
249
248
|
|
|
250
249
|
try:
|
|
251
250
|
if self.timeout:
|
|
@@ -10,6 +10,7 @@ cli2/colors.py
|
|
|
10
10
|
cli2/configuration.py
|
|
11
11
|
cli2/decorators.py
|
|
12
12
|
cli2/display.py
|
|
13
|
+
cli2/find.py
|
|
13
14
|
cli2/interactive.py
|
|
14
15
|
cli2/lock.py
|
|
15
16
|
cli2/log.py
|
|
@@ -46,6 +47,7 @@ tests/test_configuration.py
|
|
|
46
47
|
tests/test_decorators.py
|
|
47
48
|
tests/test_display.py
|
|
48
49
|
tests/test_entry_point.py
|
|
50
|
+
tests/test_find.py
|
|
49
51
|
tests/test_group.py
|
|
50
52
|
tests/test_inject.py
|
|
51
53
|
tests/test_interactive.py
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import os
|
|
4
|
+
from cli2 import Find
|
|
5
|
+
|
|
6
|
+
@pytest.fixture
|
|
7
|
+
def temp_git_repo(tmp_path):
|
|
8
|
+
"""Create a temporary git repository with test files."""
|
|
9
|
+
# Initialize git repo
|
|
10
|
+
os.system(f"git init {tmp_path}")
|
|
11
|
+
|
|
12
|
+
# Create some test files and directories
|
|
13
|
+
(tmp_path / "src").mkdir()
|
|
14
|
+
(tmp_path / "src" / "main.py").write_text("main content")
|
|
15
|
+
(tmp_path / "src" / "utils.py").write_text("utils content")
|
|
16
|
+
(tmp_path / "tests").mkdir()
|
|
17
|
+
(tmp_path / "tests" / "test_main.py").write_text("test content")
|
|
18
|
+
(tmp_path / ".gitignore").write_text("*.pyc\n__pycache__")
|
|
19
|
+
|
|
20
|
+
# Add files to git
|
|
21
|
+
os.system(f"cd {tmp_path} && git add . && git commit -m 'Initial commit'")
|
|
22
|
+
|
|
23
|
+
return tmp_path
|
|
24
|
+
|
|
25
|
+
def test_basic_initialization():
|
|
26
|
+
"""Test basic Find initialization."""
|
|
27
|
+
finder = Find()
|
|
28
|
+
assert isinstance(finder.root, Path)
|
|
29
|
+
assert finder.root.is_dir()
|
|
30
|
+
assert finder.glob_include == []
|
|
31
|
+
assert finder.glob_exclude == []
|
|
32
|
+
assert finder.file_callback is None
|
|
33
|
+
|
|
34
|
+
def test_custom_root_initialization(temp_git_repo):
|
|
35
|
+
"""Test initialization with custom root."""
|
|
36
|
+
finder = Find(root=temp_git_repo)
|
|
37
|
+
assert finder.root == temp_git_repo.resolve()
|
|
38
|
+
|
|
39
|
+
def test_files_listing(temp_git_repo):
|
|
40
|
+
"""Test basic file listing."""
|
|
41
|
+
finder = Find(root=temp_git_repo)
|
|
42
|
+
files = finder.files()
|
|
43
|
+
|
|
44
|
+
file_names = {f.name for f in files}
|
|
45
|
+
assert "main.py" in file_names
|
|
46
|
+
assert "utils.py" in file_names
|
|
47
|
+
assert "test_main.py" in file_names
|
|
48
|
+
assert ".gitignore" in file_names
|
|
49
|
+
|
|
50
|
+
def test_dirs_listing(temp_git_repo):
|
|
51
|
+
"""Test basic directory listing."""
|
|
52
|
+
finder = Find(root=temp_git_repo)
|
|
53
|
+
dirs = finder.dirs()
|
|
54
|
+
|
|
55
|
+
dir_names = {d.name for d in dirs}
|
|
56
|
+
assert "src" in dir_names
|
|
57
|
+
assert "tests" in dir_names
|
|
58
|
+
|
|
59
|
+
def test_glob_include_filter(temp_git_repo):
|
|
60
|
+
"""Test glob include filtering."""
|
|
61
|
+
finder = Find(root=temp_git_repo, glob_include=["*.py"])
|
|
62
|
+
files = finder.files()
|
|
63
|
+
|
|
64
|
+
file_names = {f.name for f in files}
|
|
65
|
+
assert "main.py" in file_names
|
|
66
|
+
assert "utils.py" in file_names
|
|
67
|
+
assert "test_main.py" in file_names
|
|
68
|
+
assert ".gitignore" not in file_names
|
|
69
|
+
|
|
70
|
+
def test_glob_exclude_filter(temp_git_repo):
|
|
71
|
+
"""Test glob exclude filtering."""
|
|
72
|
+
finder = Find(root=temp_git_repo, glob_exclude=["*test*"])
|
|
73
|
+
files = finder.files()
|
|
74
|
+
|
|
75
|
+
file_names = {f.name for f in files}
|
|
76
|
+
assert "main.py" in file_names
|
|
77
|
+
assert "utils.py" in file_names
|
|
78
|
+
assert "test_main.py" not in file_names
|
|
79
|
+
|
|
80
|
+
def test_file_callback(temp_git_repo):
|
|
81
|
+
"""Test file callback functionality."""
|
|
82
|
+
called_paths = []
|
|
83
|
+
def callback(filepath):
|
|
84
|
+
called_paths.append(filepath)
|
|
85
|
+
|
|
86
|
+
finder = Find(root=temp_git_repo, file_callback=callback)
|
|
87
|
+
files = finder.files()
|
|
88
|
+
|
|
89
|
+
assert len(called_paths) == len(files)
|
|
90
|
+
assert set(called_paths) == set(files)
|
|
91
|
+
|
|
92
|
+
def test_custom_directory_search(temp_git_repo):
|
|
93
|
+
"""Test searching from a specific directory."""
|
|
94
|
+
finder = Find(root=temp_git_repo)
|
|
95
|
+
files = finder.files(directory=temp_git_repo / "src")
|
|
96
|
+
|
|
97
|
+
file_names = {f.name for f in files}
|
|
98
|
+
assert "main.py" in file_names
|
|
99
|
+
assert "utils.py" in file_names
|
|
100
|
+
assert "test_main.py" not in file_names
|
|
101
|
+
|
|
102
|
+
def test_error_handling(temp_git_repo, monkeypatch):
|
|
103
|
+
"""Test error handling when git command fails."""
|
|
104
|
+
# Mock cli2.Proc to simulate git command failure
|
|
105
|
+
class MockProc:
|
|
106
|
+
def __init__(self, *args, **kwargs):
|
|
107
|
+
pass
|
|
108
|
+
def wait(self):
|
|
109
|
+
return self
|
|
110
|
+
rc = 1
|
|
111
|
+
stderr = "Git error"
|
|
112
|
+
stdout = ""
|
|
113
|
+
|
|
114
|
+
monkeypatch.setattr("cli2.Proc", MockProc)
|
|
115
|
+
|
|
116
|
+
finder = Find(root=temp_git_repo)
|
|
117
|
+
with pytest.raises(RuntimeError, match="Command failed with return code 1: Git error"):
|
|
118
|
+
finder.files()
|
|
119
|
+
|
|
120
|
+
def test_matches_filters(temp_git_repo):
|
|
121
|
+
"""Test the _matches_filters method."""
|
|
122
|
+
finder = Find(
|
|
123
|
+
root=temp_git_repo,
|
|
124
|
+
glob_include=["*.py"],
|
|
125
|
+
glob_exclude=["*test*"]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
assert finder._matches_filters(temp_git_repo / "src" / "main.py")
|
|
129
|
+
assert finder._matches_filters(temp_git_repo / "src" / "utils.py")
|
|
130
|
+
assert not finder._matches_filters(temp_git_repo / "tests" / "test_main.py")
|
|
131
|
+
assert not finder._matches_filters(temp_git_repo / ".gitignore")
|
|
@@ -55,10 +55,10 @@ async def test_proc_cmd_property():
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
@pytest.mark.asyncio
|
|
58
|
-
async def
|
|
58
|
+
async def test_proc_start_and_waita():
|
|
59
59
|
proc = cli2.Proc("echo hello")
|
|
60
|
-
await proc.
|
|
61
|
-
await proc.
|
|
60
|
+
await proc.starta()
|
|
61
|
+
await proc.waita()
|
|
62
62
|
assert proc.started is True
|
|
63
63
|
assert proc.waited is True
|
|
64
64
|
assert proc.rc == 0
|
|
@@ -68,16 +68,16 @@ async def test_proc_start_and_wait():
|
|
|
68
68
|
@pytest.mark.asyncio
|
|
69
69
|
async def test_proc_start_and_wait_with_timeout():
|
|
70
70
|
proc = cli2.Proc("sleep 2", timeout=1)
|
|
71
|
-
await proc.
|
|
72
|
-
await proc.
|
|
71
|
+
await proc.starta()
|
|
72
|
+
await proc.waita()
|
|
73
73
|
assert proc.rc != 0 # Should be terminated due to timeout
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
@pytest.mark.asyncio
|
|
77
77
|
async def test_proc_output_properties():
|
|
78
78
|
proc = cli2.Proc("echo hello")
|
|
79
|
-
await proc.
|
|
80
|
-
await proc.
|
|
79
|
+
await proc.starta()
|
|
80
|
+
await proc.waita()
|
|
81
81
|
assert proc.stdout == "hello"
|
|
82
82
|
assert proc.stderr == ""
|
|
83
83
|
assert proc.out == "hello"
|
|
@@ -89,8 +89,8 @@ async def test_proc_output_properties():
|
|
|
89
89
|
@pytest.mark.asyncio
|
|
90
90
|
async def test_proc_with_stderr():
|
|
91
91
|
proc = cli2.Proc("bash", "-c", "echo error >&2 ")
|
|
92
|
-
await proc.
|
|
93
|
-
await proc.
|
|
92
|
+
await proc.starta()
|
|
93
|
+
await proc.waita()
|
|
94
94
|
assert proc.stdout == ""
|
|
95
95
|
assert proc.stderr == "error"
|
|
96
96
|
assert proc.out == "error"
|
|
@@ -102,8 +102,8 @@ async def test_proc_with_stderr():
|
|
|
102
102
|
@pytest.mark.asyncio
|
|
103
103
|
async def test_proc_with_ansi_codes():
|
|
104
104
|
proc = cli2.Proc("echo -e '\033[31mred\033[0m'")
|
|
105
|
-
await proc.
|
|
106
|
-
await proc.
|
|
105
|
+
await proc.starta()
|
|
106
|
+
await proc.waita()
|
|
107
107
|
assert proc.stdout == "red"
|
|
108
108
|
assert proc.stdout_ansi == "\x1b[31mred\x1b[0m"
|
|
109
109
|
|
|
@@ -111,26 +111,26 @@ async def test_proc_with_ansi_codes():
|
|
|
111
111
|
@pytest.mark.asyncio
|
|
112
112
|
async def test_proc_quiet_mode():
|
|
113
113
|
proc = cli2.Proc("echo hello", quiet=True)
|
|
114
|
-
await proc.
|
|
115
|
-
await proc.
|
|
114
|
+
await proc.starta()
|
|
115
|
+
await proc.waita()
|
|
116
116
|
assert proc.out == "hello"
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
def
|
|
120
|
-
proc = cli2.Proc("echo hello").
|
|
119
|
+
def test_proc_start_and_wait():
|
|
120
|
+
proc = cli2.Proc("echo hello").wait()
|
|
121
121
|
assert proc.started is True
|
|
122
122
|
assert proc.waited is True
|
|
123
123
|
assert proc.rc == 0
|
|
124
124
|
assert proc.out == "hello"
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
def
|
|
128
|
-
proc = cli2.Proc("sleep 2", timeout=1).
|
|
127
|
+
def test_proc_start_and_wait_with_timeout():
|
|
128
|
+
proc = cli2.Proc("sleep 2", timeout=1).wait()
|
|
129
129
|
assert proc.rc != 0 # Should be terminated due to timeout
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
def
|
|
133
|
-
proc = cli2.Proc("bash", "-c", "echo error >&2").
|
|
132
|
+
def test_proc_start_and_wait_with_stderr():
|
|
133
|
+
proc = cli2.Proc("bash", "-c", "echo error >&2").wait()
|
|
134
134
|
assert proc.stdout == ""
|
|
135
135
|
assert proc.stderr == "error"
|
|
136
136
|
assert proc.out == "error"
|
|
@@ -139,12 +139,12 @@ def test_proc_start_sync_and_wait_sync_with_stderr():
|
|
|
139
139
|
assert proc.out_ansi == "error"
|
|
140
140
|
|
|
141
141
|
|
|
142
|
-
def
|
|
143
|
-
proc = cli2.Proc("echo -e '\033[31mred\033[0m'").
|
|
142
|
+
def test_proc_start_and_wait_with_ansi_codes():
|
|
143
|
+
proc = cli2.Proc("echo -e '\033[31mred\033[0m'").wait()
|
|
144
144
|
assert proc.stdout == "red"
|
|
145
145
|
assert proc.stdout_ansi == "\x1b[31mred\x1b[0m"
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
def
|
|
149
|
-
proc = cli2.Proc("echo hello", quiet=True).
|
|
148
|
+
def test_proc_start_and_wait_quiet_mode():
|
|
149
|
+
proc = cli2.Proc("echo hello", quiet=True).wait()
|
|
150
150
|
assert proc.out == "hello"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|