testpick 0.2.0__py3-none-any.whl
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.
- testpick/__init__.py +10 -0
- testpick/__main__.py +3 -0
- testpick/cli.py +228 -0
- testpick/git/__init__.py +21 -0
- testpick/git/commit.py +28 -0
- testpick/git/diff.py +59 -0
- testpick/git/interactive.py +106 -0
- testpick/graph/__init__.py +7 -0
- testpick/graph/builder.py +201 -0
- testpick/graph/edge.py +13 -0
- testpick/graph/node.py +16 -0
- testpick/parser/__init__.py +18 -0
- testpick/parser/base.py +46 -0
- testpick/parser/python.py +225 -0
- testpick/parser/registry.py +24 -0
- testpick/parser/typescript.py +207 -0
- testpick/reporter/__init__.py +7 -0
- testpick/reporter/github.py +58 -0
- testpick/reporter/json_reporter.py +49 -0
- testpick/reporter/terminal.py +108 -0
- testpick/selector/__init__.py +6 -0
- testpick/selector/matcher.py +58 -0
- testpick/selector/walker.py +42 -0
- testpick-0.2.0.dist-info/METADATA +211 -0
- testpick-0.2.0.dist-info/RECORD +28 -0
- testpick-0.2.0.dist-info/WHEEL +4 -0
- testpick-0.2.0.dist-info/entry_points.txt +2 -0
- testpick-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Terminal reporter that renders beautiful terminal output using rich."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TerminalReporter:
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
self.console = Console()
|
|
15
|
+
|
|
16
|
+
def render(
|
|
17
|
+
self,
|
|
18
|
+
changed_files: list[tuple[str, str]],
|
|
19
|
+
test_files: list[Path],
|
|
20
|
+
dependency_chains: dict[Path, list[Path]],
|
|
21
|
+
stats: dict,
|
|
22
|
+
) -> None:
|
|
23
|
+
"""Render the test selection results to the terminal.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
changed_files: List of (status, filepath) tuples from git diff.
|
|
27
|
+
test_files: List of selected test filepaths.
|
|
28
|
+
dependency_chains: Mapping from test file to its dependency chain.
|
|
29
|
+
stats: Statistics about the analysis.
|
|
30
|
+
"""
|
|
31
|
+
# Header
|
|
32
|
+
self.console.print()
|
|
33
|
+
self.console.print(
|
|
34
|
+
Panel(
|
|
35
|
+
"[bold]testpick - Test Selection Results[/bold]",
|
|
36
|
+
style="blue",
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
self.console.print()
|
|
40
|
+
|
|
41
|
+
# Changed files table
|
|
42
|
+
changed_table = Table(title="Changed Files", show_header=True)
|
|
43
|
+
changed_table.add_column("Status", style="bold", width=10)
|
|
44
|
+
changed_table.add_column("File", style="cyan")
|
|
45
|
+
|
|
46
|
+
status_colors = {
|
|
47
|
+
"A": "green",
|
|
48
|
+
"M": "yellow",
|
|
49
|
+
"D": "red",
|
|
50
|
+
"R": "blue",
|
|
51
|
+
}
|
|
52
|
+
status_names = {
|
|
53
|
+
"A": "Added",
|
|
54
|
+
"M": "Modified",
|
|
55
|
+
"D": "Deleted",
|
|
56
|
+
"R": "Renamed",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for status, filepath in changed_files:
|
|
60
|
+
color = status_colors.get(status, "white")
|
|
61
|
+
status_name = status_names.get(status, status)
|
|
62
|
+
changed_table.add_row(
|
|
63
|
+
f"[{color}]{status_name}[/{color}]",
|
|
64
|
+
filepath,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
self.console.print(changed_table)
|
|
68
|
+
self.console.print()
|
|
69
|
+
|
|
70
|
+
# Test files table
|
|
71
|
+
if test_files:
|
|
72
|
+
test_table = Table(title="Selected Tests", show_header=True)
|
|
73
|
+
test_table.add_column("#", style="dim", width=5)
|
|
74
|
+
test_table.add_column("Test File", style="cyan")
|
|
75
|
+
test_table.add_column("Dependency Path", style="dim")
|
|
76
|
+
|
|
77
|
+
for i, test_file in enumerate(test_files, 1):
|
|
78
|
+
chain = dependency_chains.get(test_file, [])
|
|
79
|
+
chain_str = " -> ".join(str(p) for p in chain) if chain else "direct"
|
|
80
|
+
test_table.add_row(
|
|
81
|
+
str(i),
|
|
82
|
+
str(test_file),
|
|
83
|
+
chain_str,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
self.console.print(test_table)
|
|
87
|
+
else:
|
|
88
|
+
self.console.print(
|
|
89
|
+
Panel(
|
|
90
|
+
"[yellow]No tests selected[/yellow]",
|
|
91
|
+
style="yellow",
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
self.console.print()
|
|
96
|
+
|
|
97
|
+
# Stats
|
|
98
|
+
stats_table = Table(title="Statistics", show_header=False)
|
|
99
|
+
stats_table.add_column("Metric", style="bold")
|
|
100
|
+
stats_table.add_column("Value", style="green")
|
|
101
|
+
|
|
102
|
+
stats_table.add_row("Files Changed", str(stats.get("files_changed", 0)))
|
|
103
|
+
stats_table.add_row("Tests Selected", str(len(test_files)))
|
|
104
|
+
stats_table.add_row("Insertions", str(stats.get("insertions", 0)))
|
|
105
|
+
stats_table.add_row("Deletions", str(stats.get("deletions", 0)))
|
|
106
|
+
|
|
107
|
+
self.console.print(stats_table)
|
|
108
|
+
self.console.print()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Test matcher that filters and sorts test files from affected files."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from ..graph import DependencyGraph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestMatcher:
|
|
11
|
+
def match(
|
|
12
|
+
self, affected_files: set[Path], graph: DependencyGraph
|
|
13
|
+
) -> list[Path]:
|
|
14
|
+
"""Filter affected files to only test files and sort by directness.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
affected_files: Set of all affected filepaths.
|
|
18
|
+
graph: The dependency graph.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
List of test filepaths, sorted with direct imports first.
|
|
22
|
+
"""
|
|
23
|
+
test_files: list[Path] = []
|
|
24
|
+
direct_tests: list[Path] = []
|
|
25
|
+
transitive_tests: list[Path] = []
|
|
26
|
+
|
|
27
|
+
for filepath in affected_files:
|
|
28
|
+
if graph.is_test_file(filepath):
|
|
29
|
+
# Check if this test directly imports from a changed file
|
|
30
|
+
if filepath in graph.nodes:
|
|
31
|
+
node = graph.nodes[filepath]
|
|
32
|
+
# Check if any of its imports are in the affected files
|
|
33
|
+
is_direct = False
|
|
34
|
+
for imp in node.imports:
|
|
35
|
+
targets = graph.resolve_import(imp, filepath)
|
|
36
|
+
for target in targets:
|
|
37
|
+
if target in affected_files and not graph.is_test_file(target):
|
|
38
|
+
is_direct = True
|
|
39
|
+
break
|
|
40
|
+
if is_direct:
|
|
41
|
+
break
|
|
42
|
+
|
|
43
|
+
if is_direct:
|
|
44
|
+
direct_tests.append(filepath)
|
|
45
|
+
else:
|
|
46
|
+
transitive_tests.append(filepath)
|
|
47
|
+
else:
|
|
48
|
+
# File not in graph, treat as transitive
|
|
49
|
+
transitive_tests.append(filepath)
|
|
50
|
+
|
|
51
|
+
# Sort each group alphabetically for consistency
|
|
52
|
+
direct_tests.sort()
|
|
53
|
+
transitive_tests.sort()
|
|
54
|
+
|
|
55
|
+
# Direct imports first, then transitive
|
|
56
|
+
test_files = direct_tests + transitive_tests
|
|
57
|
+
|
|
58
|
+
return test_files
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Dependency walker that finds all files affected by changes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from ..graph import DependencyGraph
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DependencyWalker:
|
|
11
|
+
def walk_from_files(
|
|
12
|
+
self, changed_files: list[tuple[str, str]], graph: DependencyGraph
|
|
13
|
+
) -> tuple[set[Path], dict[Path, Path]]:
|
|
14
|
+
"""Walk from changed files to every affected file, tracking how each was reached.
|
|
15
|
+
|
|
16
|
+
Returns the affected files and a parent map used to explain why a file
|
|
17
|
+
was selected. Deleted files are walked but never selected themselves.
|
|
18
|
+
"""
|
|
19
|
+
affected: set[Path] = set()
|
|
20
|
+
parents: dict[Path, Path] = {}
|
|
21
|
+
|
|
22
|
+
for status, filepath in changed_files:
|
|
23
|
+
path = Path(filepath).resolve()
|
|
24
|
+
if not status.startswith("D"):
|
|
25
|
+
affected.add(path)
|
|
26
|
+
|
|
27
|
+
if path not in graph.nodes:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
queue = [path]
|
|
31
|
+
visited = {path}
|
|
32
|
+
while queue:
|
|
33
|
+
current = queue.pop(0)
|
|
34
|
+
for dependent in graph.get_dependents(current):
|
|
35
|
+
if dependent in visited:
|
|
36
|
+
continue
|
|
37
|
+
visited.add(dependent)
|
|
38
|
+
affected.add(dependent)
|
|
39
|
+
parents.setdefault(dependent, current)
|
|
40
|
+
queue.append(dependent)
|
|
41
|
+
|
|
42
|
+
return affected, parents
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: testpick
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Pick the tests affected by your git diff before you run them
|
|
5
|
+
Project-URL: Homepage, https://github.com/krauzX/testpick
|
|
6
|
+
Project-URL: Repository, https://github.com/krauzX/testpick
|
|
7
|
+
Project-URL: Issues, https://github.com/krauzX/testpick/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ci,git,pytest,static-analysis,testing
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: click>=8.0
|
|
24
|
+
Requires-Dist: gitpython>=3.1
|
|
25
|
+
Requires-Dist: readchar>=4.0
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
32
|
+
Provides-Extra: tree-sitter
|
|
33
|
+
Requires-Dist: tree-sitter-javascript>=0.23; extra == 'tree-sitter'
|
|
34
|
+
Requires-Dist: tree-sitter-python>=0.23; extra == 'tree-sitter'
|
|
35
|
+
Requires-Dist: tree-sitter-typescript>=0.23; extra == 'tree-sitter'
|
|
36
|
+
Requires-Dist: tree-sitter>=0.25; extra == 'tree-sitter'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# testpick
|
|
40
|
+
|
|
41
|
+
[](https://github.com/krauzX/testpick/actions/workflows/ci.yml)
|
|
42
|
+
|
|
43
|
+
A Python CLI that looks at your git diff and tells you which tests are going to break. Before you run them.
|
|
44
|
+
|
|
45
|
+
## Why this exists
|
|
46
|
+
|
|
47
|
+
You change a function in `utils.py`. Now what? You could run the full test suite and wait, or you could guess
|
|
48
|
+
which tests matter and hope you're right. `pytest --lf` reruns failures only after they've already failed.
|
|
49
|
+
Jest's `--changedSince` follows direct file changes.
|
|
50
|
+
|
|
51
|
+
testpick parses your git diff, builds an import graph of your source files, and walks it backwards from what
|
|
52
|
+
you changed to find every test that depends on it — transitively. Each selected test comes with the chain that
|
|
53
|
+
explains why, so you can sanity-check the answer instead of trusting it.
|
|
54
|
+
|
|
55
|
+
## Getting started
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/krauzX/testpick.git
|
|
59
|
+
cd testpick
|
|
60
|
+
pip install -e .
|
|
61
|
+
|
|
62
|
+
# optional but recommended: real AST parsing instead of the regex fallback
|
|
63
|
+
pip install -e ".[tree-sitter]"
|
|
64
|
+
|
|
65
|
+
# for development (tests, linter, type checker)
|
|
66
|
+
pip install -e ".[dev]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Python 3.10+, runs on Linux, macOS and Windows.
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# what changed since your last commit, including uncommitted work?
|
|
75
|
+
testpick
|
|
76
|
+
|
|
77
|
+
# committed changes on your branch since it diverged from main
|
|
78
|
+
testpick --base main
|
|
79
|
+
|
|
80
|
+
# a single commit
|
|
81
|
+
testpick --commit abc123
|
|
82
|
+
|
|
83
|
+
# output for scripts and CI
|
|
84
|
+
testpick --format json
|
|
85
|
+
testpick --format github # GitHub Actions annotations
|
|
86
|
+
|
|
87
|
+
# only analyze Python and TypeScript files
|
|
88
|
+
testpick --languages python,typescript
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Everything resolves from the repository root, so it works from any directory inside the repo.
|
|
92
|
+
|
|
93
|
+
### Example output
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
97
|
+
│ testpick - Test Selection Results │
|
|
98
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
99
|
+
|
|
100
|
+
Changed Files
|
|
101
|
+
┌────────────┬────────────────────────────────────────┐
|
|
102
|
+
│ Status │ File │
|
|
103
|
+
├────────────┼────────────────────────────────────────┤
|
|
104
|
+
│ Modified │ src/testpick/graph/builder.py │
|
|
105
|
+
│ Modified │ src/testpick/reporter/github.py │
|
|
106
|
+
│ Modified │ src/testpick/reporter/json_reporter.py │
|
|
107
|
+
│ Added │ tests/test_selection_pipeline.py │
|
|
108
|
+
└────────────┴────────────────────────────────────────┘
|
|
109
|
+
|
|
110
|
+
Selected Tests
|
|
111
|
+
┌───────┬──────────────────────────────────┬──────────────────────────────────┐
|
|
112
|
+
│ # │ Test File │ Dependency Path │
|
|
113
|
+
├───────┼──────────────────────────────────┼──────────────────────────────────┤
|
|
114
|
+
│ 1 │ tests/test_cli.py │ tests/test_cli.py -> │
|
|
115
|
+
│ │ │ src/testpick/cli.py -> │
|
|
116
|
+
│ │ │ src/testpick/graph/__init__.py │
|
|
117
|
+
│ │ │ -> src/testpick/graph/builder.py │
|
|
118
|
+
│ 2 │ tests/test_graph/test_builder.py │ tests/test_graph/test_builder.py │
|
|
119
|
+
│ │ │ -> │
|
|
120
|
+
│ │ │ src/testpick/graph/__init__.py │
|
|
121
|
+
│ │ │ -> src/testpick/graph/builder.py │
|
|
122
|
+
│ 3 │ tests/test_selection_pipeline.py │ direct │
|
|
123
|
+
│ 4 │ tests/test_selector/test_matche… │ tests/test_selector/test_matche… │
|
|
124
|
+
│ │ │ -> │
|
|
125
|
+
│ │ │ src/testpick/graph/__init__.py │
|
|
126
|
+
│ │ │ -> src/testpick/graph/builder.py │
|
|
127
|
+
└───────┴──────────────────────────────────┴──────────────────────────────────┘
|
|
128
|
+
|
|
129
|
+
Statistics
|
|
130
|
+
┌────────────────┬─────┐
|
|
131
|
+
│ Files Changed │ 4 │
|
|
132
|
+
│ Tests Selected │ 4 │
|
|
133
|
+
│ Insertions │ 258 │
|
|
134
|
+
│ Deletions │ 25 │
|
|
135
|
+
└────────────────┴─────┘
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`direct` means the test file itself changed. The JSON format carries the same
|
|
139
|
+
information, with chains as arrays:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"changed_files": [
|
|
144
|
+
{ "status": "M", "path": "src/utils.ts" }
|
|
145
|
+
],
|
|
146
|
+
"test_files": [
|
|
147
|
+
{
|
|
148
|
+
"path": "tests/test_page.ts",
|
|
149
|
+
"dependency_chain": ["tests/test_page.ts", "src/page.ts", "src/utils.ts"]
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
"stats": {
|
|
153
|
+
"files_changed": 1,
|
|
154
|
+
"insertions": 6,
|
|
155
|
+
"deletions": 2,
|
|
156
|
+
"tests_selected": 1
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## How it works
|
|
162
|
+
|
|
163
|
+
1. Read the diff with `git diff --name-status` and resolve every path against the repository root.
|
|
164
|
+
2. Parse each source file for imports. tree-sitter grammars are used when installed, otherwise a regex pass
|
|
165
|
+
handles Python, TypeScript and JavaScript.
|
|
166
|
+
3. Resolve each import to a file: relative specifiers against the importing file's directory, absolute ones
|
|
167
|
+
against `src/`, the project root, then the file's own directory — preferring the importing file's language
|
|
168
|
+
when both a `.py` and a `.ts` file would match.
|
|
169
|
+
4. Walk reverse edges from the changed files with a breadth-first search, recording the parent of every hit so
|
|
170
|
+
the chain can be explained afterwards.
|
|
171
|
+
5. Report the files that are test files, direct changes first.
|
|
172
|
+
|
|
173
|
+
## How it compares
|
|
174
|
+
|
|
175
|
+
Other tools pick tests from a diff too; they differ in how far they can see:
|
|
176
|
+
|
|
177
|
+
- **pytest-testmon** records which code each test actually executed, so it catches runtime coupling a static
|
|
178
|
+
graph misses — but it needs a baseline run and is pytest-only.
|
|
179
|
+
- **Jest `--changedSince`** maps changed files to tests, but inside a Jest project and mostly direct
|
|
180
|
+
relationships.
|
|
181
|
+
- **Nx and Turborepo affected tasks** pick packages to rebuild, which is coarser than picking individual tests.
|
|
182
|
+
- **snob, pytest-impacted, pytest-git-selector** do static graph selection like this one, but for Python only.
|
|
183
|
+
|
|
184
|
+
testpick's corner: one CLI covering Python and TypeScript/JavaScript, no baseline run required, and every
|
|
185
|
+
selection arrives with the chain that produced it.
|
|
186
|
+
|
|
187
|
+
## Limitations
|
|
188
|
+
|
|
189
|
+
Static analysis only sees imports it can read:
|
|
190
|
+
|
|
191
|
+
- Dynamic imports, dependency injection, plugin registries and other runtime wiring are invisible to it.
|
|
192
|
+
- A file deleted from the working tree is gone from the parse tree, so testpick reports the deletion but
|
|
193
|
+
cannot trace what used to import it.
|
|
194
|
+
- `--languages` scopes which files are analyzed for dependencies; changed files are always reported.
|
|
195
|
+
|
|
196
|
+
Treat the output as a starting point. A full suite run still belongs in CI before a release.
|
|
197
|
+
|
|
198
|
+
## Running tests
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pytest tests/ -v
|
|
202
|
+
pytest tests/ --cov=testpick
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
77 tests covering diff parsing, import resolution, graph building, the selection pipeline and all three
|
|
206
|
+
reporters, with 82% branch coverage. CI runs ruff, mypy and the suite on Python 3.10-3.13 across Linux, macOS
|
|
207
|
+
and Windows.
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
testpick/__init__.py,sha256=CDFgRH2D_Tuw3V1GXu-CZ_1hcPHn06MbMwks2nMKIg8,254
|
|
2
|
+
testpick/__main__.py,sha256=FY7OCYd3IE4hQpDvI-p_lrfkDl7ZYAhLajJVF1YegPA,38
|
|
3
|
+
testpick/cli.py,sha256=VcWsc9Cyq5xK6IkZj_S8aroOdthOuYRMGaluJI8EXlY,6831
|
|
4
|
+
testpick/git/__init__.py,sha256=atIQtxlOih29JM_vnZcWTPXhC9VOs0Sraq8vNN_cn14,514
|
|
5
|
+
testpick/git/commit.py,sha256=6Vphw1j0-7gaCV3V1V25fthM9vaHh0xVeZvZ6tPb-Jc,1045
|
|
6
|
+
testpick/git/diff.py,sha256=WrA2XPRh3DxDKTjvG-BQNtDIhhG2mz89KBmS2tOnpUw,1702
|
|
7
|
+
testpick/git/interactive.py,sha256=PmIaWxOJwNbbIET9Qv7lkedGCUKepfdIO_MRoTjlCR0,3711
|
|
8
|
+
testpick/graph/__init__.py,sha256=xkV_gekG83ghOlPqSIaCfDbODDgenQQy7Vc0x5vntGU,199
|
|
9
|
+
testpick/graph/builder.py,sha256=HQ10NcxbMRfRn_cu7cVe3jhJfNjsQvQBUkkW3t5Vxtk,7878
|
|
10
|
+
testpick/graph/edge.py,sha256=UrEQEhRha25fAg52L4qsgTNixQ3IitAvj-Q8vfmcwss,301
|
|
11
|
+
testpick/graph/node.py,sha256=geha69c4i0uHv_iZYN7JdXNZNZ1-aV0s9vApNYe7DRY,375
|
|
12
|
+
testpick/parser/__init__.py,sha256=PdxY0L32mY_Rtp86Opbg82Mc5-fIcLZZOUiThVK67Nw,434
|
|
13
|
+
testpick/parser/base.py,sha256=iN03ee9_n_eAxZm73N2aghi0NYpnOAxxTSMNhmHHm2c,1010
|
|
14
|
+
testpick/parser/python.py,sha256=7xeIkcX9gkCRYOufVWmNT9N9FhV4U8MGcwcS6wRfpEA,8840
|
|
15
|
+
testpick/parser/registry.py,sha256=y2vJUQ9znhuu6IdQdGWx3dVqUuVPJ83Eksb8OMa-RVg,621
|
|
16
|
+
testpick/parser/typescript.py,sha256=qR5Tl80t-5LpP4QZSlj3_ZzyCe-tifW-phWrAgP-Cw0,8133
|
|
17
|
+
testpick/reporter/__init__.py,sha256=Lew5Dg3pzFw8RfXRiwav28Vf3Qg1EoK3sKeIHxTQo3M,225
|
|
18
|
+
testpick/reporter/github.py,sha256=KVMoyoZejfABOa_Wpw7oLspfjySnP6oDa9zCReAsk70,2052
|
|
19
|
+
testpick/reporter/json_reporter.py,sha256=fj3OG6xtr6LZfUp2nIfo5JZenm4M1ThIKgax66ShxMg,1565
|
|
20
|
+
testpick/reporter/terminal.py,sha256=9vZXY1yZzW0vRSyHFXmX88MHSByTFgDzthOyy_ZcEh0,3478
|
|
21
|
+
testpick/selector/__init__.py,sha256=H-q1Htokm30NsgcdcBqUdP8bysy65_v4CqSmWK1k8cI,160
|
|
22
|
+
testpick/selector/matcher.py,sha256=Kb923i9XcVZwUq4UymswRiCX5whB2HKLIs3d8Xwit6s,2038
|
|
23
|
+
testpick/selector/walker.py,sha256=9rals56AcZEIBWOHNSeKAjrJZMqHsmU8F0XuvYtmNAs,1394
|
|
24
|
+
testpick-0.2.0.dist-info/METADATA,sha256=E6I6Xq_l9FBdKKGSgw25J6MutIpsArcHW6NBUe5WeRY,9815
|
|
25
|
+
testpick-0.2.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
26
|
+
testpick-0.2.0.dist-info/entry_points.txt,sha256=2IbOPSnYjU9aUd5vlaZgvzeIUelZQcZ3TlgyFmWK3YU,47
|
|
27
|
+
testpick-0.2.0.dist-info/licenses/LICENSE,sha256=bUxTf7MNRUy_sQGHcUt0CLpDV301088prTjhVffHC1U,1063
|
|
28
|
+
testpick-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 krauzX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|