git-commit-guard 0.5.0__tar.gz → 0.6.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.
- git_commit_guard-0.6.0/.github/workflows/lint-commits.yml +44 -0
- git_commit_guard-0.6.0/.github/workflows/test.yml +27 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/.gitignore +1 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/PKG-INFO +1 -1
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/pyproject.toml +6 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/src/git_commit_guard/__init__.py +16 -110
- git_commit_guard-0.6.0/tests/__init__.py +0 -0
- git_commit_guard-0.6.0/tests/test_git_commit_guard.py +464 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/uv.lock +162 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/.pre-commit-hooks.yaml +0 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/.python-version +0 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/LICENSE +0 -0
- {git_commit_guard-0.5.0 → git_commit_guard-0.6.0}/README.md +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Lint commits
|
|
3
|
+
on: # yamllint disable-line rule:truthy
|
|
4
|
+
pull_request:
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
jobs:
|
|
8
|
+
lint-commits:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
# yamllint disable-line rule:line-length
|
|
15
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
16
|
+
with:
|
|
17
|
+
persist-credentials: false
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
- name: Install Python
|
|
20
|
+
# yamllint disable-line rule:line-length
|
|
21
|
+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
- name: Install uv
|
|
25
|
+
# yamllint disable-line rule:line-length
|
|
26
|
+
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
27
|
+
- name: Install commit-guard
|
|
28
|
+
run: uv pip install --system .
|
|
29
|
+
- name: Cache NLTK data
|
|
30
|
+
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
31
|
+
with:
|
|
32
|
+
path: ~/nltk_data
|
|
33
|
+
key: nltk-averaged-perceptron-tagger-punkt
|
|
34
|
+
- name: Lint commits
|
|
35
|
+
env:
|
|
36
|
+
BASE_REF: ${{ github.base_ref }}
|
|
37
|
+
run: |-
|
|
38
|
+
commits=$(git log --no-merges --format="%H" "origin/$BASE_REF"..HEAD)
|
|
39
|
+
failed=0
|
|
40
|
+
for sha in $commits; do
|
|
41
|
+
echo "--- checking $sha ---"
|
|
42
|
+
commit-guard --disable signature "$sha" || failed=1
|
|
43
|
+
done
|
|
44
|
+
exit $failed
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Test
|
|
3
|
+
on: # yamllint disable-line rule:truthy
|
|
4
|
+
pull_request:
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout code
|
|
12
|
+
# yamllint disable-line rule:line-length
|
|
13
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
14
|
+
with:
|
|
15
|
+
persist-credentials: false
|
|
16
|
+
- name: Install uv
|
|
17
|
+
# yamllint disable-line rule:line-length
|
|
18
|
+
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
19
|
+
- name: Cache NLTK data
|
|
20
|
+
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
|
21
|
+
with:
|
|
22
|
+
path: ~/nltk_data
|
|
23
|
+
key: nltk-averaged-perceptron-tagger-punkt
|
|
24
|
+
- name: Run tests with coverage
|
|
25
|
+
run: |-
|
|
26
|
+
uv run --dev pytest \
|
|
27
|
+
tests/ --cov=git_commit_guard --cov-report=term-missing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-commit-guard
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Opinionated conventional commit message linter with imperative mood detection
|
|
5
5
|
Project-URL: Homepage, https://github.com/benner/commit-guard
|
|
6
6
|
Project-URL: Repository, https://github.com/benner/commit-guard
|
|
@@ -7,6 +7,7 @@ from enum import StrEnum
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
import nltk
|
|
10
|
+
from nltk.corpus import wordnet
|
|
10
11
|
|
|
11
12
|
TYPES = frozenset(
|
|
12
13
|
{
|
|
@@ -24,112 +25,7 @@ TYPES = frozenset(
|
|
|
24
25
|
}
|
|
25
26
|
)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
"add",
|
|
30
|
-
"allow",
|
|
31
|
-
"apply",
|
|
32
|
-
"avoid",
|
|
33
|
-
"bump",
|
|
34
|
-
"change",
|
|
35
|
-
"check",
|
|
36
|
-
"clean",
|
|
37
|
-
"clear",
|
|
38
|
-
"configure",
|
|
39
|
-
"correct",
|
|
40
|
-
"create",
|
|
41
|
-
"catch",
|
|
42
|
-
"define",
|
|
43
|
-
"delete",
|
|
44
|
-
"deprecate",
|
|
45
|
-
"disable",
|
|
46
|
-
"document",
|
|
47
|
-
"drop",
|
|
48
|
-
"enable",
|
|
49
|
-
"enforce",
|
|
50
|
-
"ensure",
|
|
51
|
-
"exclude",
|
|
52
|
-
"export",
|
|
53
|
-
"extend",
|
|
54
|
-
"extract",
|
|
55
|
-
"fix",
|
|
56
|
-
"format",
|
|
57
|
-
"guard",
|
|
58
|
-
"handle",
|
|
59
|
-
"ignore",
|
|
60
|
-
"implement",
|
|
61
|
-
"import",
|
|
62
|
-
"improve",
|
|
63
|
-
"include",
|
|
64
|
-
"increase",
|
|
65
|
-
"initialize",
|
|
66
|
-
"inline",
|
|
67
|
-
"install",
|
|
68
|
-
"introduce",
|
|
69
|
-
"invalidate",
|
|
70
|
-
"limit",
|
|
71
|
-
"log",
|
|
72
|
-
"make",
|
|
73
|
-
"mark",
|
|
74
|
-
"merge",
|
|
75
|
-
"migrate",
|
|
76
|
-
"move",
|
|
77
|
-
"normalize",
|
|
78
|
-
"open",
|
|
79
|
-
"optimize",
|
|
80
|
-
"override",
|
|
81
|
-
"parse",
|
|
82
|
-
"pass",
|
|
83
|
-
"patch",
|
|
84
|
-
"pin",
|
|
85
|
-
"port",
|
|
86
|
-
"prevent",
|
|
87
|
-
"print",
|
|
88
|
-
"provide",
|
|
89
|
-
"publish",
|
|
90
|
-
"reduce",
|
|
91
|
-
"refactor",
|
|
92
|
-
"release",
|
|
93
|
-
"remove",
|
|
94
|
-
"rename",
|
|
95
|
-
"reorganize",
|
|
96
|
-
"replace",
|
|
97
|
-
"require",
|
|
98
|
-
"reset",
|
|
99
|
-
"resolve",
|
|
100
|
-
"restore",
|
|
101
|
-
"restrict",
|
|
102
|
-
"return",
|
|
103
|
-
"revert",
|
|
104
|
-
"report",
|
|
105
|
-
"run",
|
|
106
|
-
"separate",
|
|
107
|
-
"set",
|
|
108
|
-
"show",
|
|
109
|
-
"simplify",
|
|
110
|
-
"skip",
|
|
111
|
-
"sort",
|
|
112
|
-
"split",
|
|
113
|
-
"start",
|
|
114
|
-
"store",
|
|
115
|
-
"stop",
|
|
116
|
-
"support",
|
|
117
|
-
"suppress",
|
|
118
|
-
"switch",
|
|
119
|
-
"sync",
|
|
120
|
-
"track",
|
|
121
|
-
"trim",
|
|
122
|
-
"unify",
|
|
123
|
-
"update",
|
|
124
|
-
"upgrade",
|
|
125
|
-
"use",
|
|
126
|
-
"wait",
|
|
127
|
-
"validate",
|
|
128
|
-
"vendor",
|
|
129
|
-
"verify",
|
|
130
|
-
"wrap",
|
|
131
|
-
}
|
|
132
|
-
)
|
|
28
|
+
_NON_IMPERATIVE_SUFFIX_RE = re.compile(r"(?:ing|ed)$")
|
|
133
29
|
|
|
134
30
|
SUBJECT_RE = re.compile(
|
|
135
31
|
r"^(?P<type>\w+)(?:\((?P<scope>[^)]+)\))?!?:\s+(?P<desc>.+)$",
|
|
@@ -189,6 +85,7 @@ class Result:
|
|
|
189
85
|
def _ensure_nltk_data():
|
|
190
86
|
_download_if_missing("taggers/averaged_perceptron_tagger_eng")
|
|
191
87
|
_download_if_missing("tokenizers/punkt_tab")
|
|
88
|
+
_download_if_missing("corpora/wordnet")
|
|
192
89
|
|
|
193
90
|
|
|
194
91
|
def _download_if_missing(resource):
|
|
@@ -228,12 +125,21 @@ def check_imperative(desc, result):
|
|
|
228
125
|
if not tokens:
|
|
229
126
|
return
|
|
230
127
|
first = tokens[0]
|
|
231
|
-
if first
|
|
128
|
+
if _NON_IMPERATIVE_SUFFIX_RE.search(first):
|
|
129
|
+
result.error(f"expected imperative verb, got '{first}' (non-imperative suffix)")
|
|
130
|
+
return
|
|
131
|
+
base = wordnet.morphy(first, wordnet.VERB)
|
|
132
|
+
if base is not None and base != first:
|
|
133
|
+
result.error(
|
|
134
|
+
f"expected imperative verb, got '{first}' (inflected form of '{base}')"
|
|
135
|
+
)
|
|
232
136
|
return
|
|
233
|
-
tagged = nltk.pos_tag(tokens)
|
|
234
|
-
if tagged[
|
|
137
|
+
tagged = nltk.pos_tag(["to", *tokens])
|
|
138
|
+
if tagged[1][1] != "VB":
|
|
139
|
+
if wordnet.morphy(first, wordnet.VERB) == first:
|
|
140
|
+
return
|
|
235
141
|
result.error(
|
|
236
|
-
f"expected imperative verb, got '{tagged[
|
|
142
|
+
f"expected imperative verb, got '{tagged[1][0]}' (POS={tagged[1][1]})",
|
|
237
143
|
)
|
|
238
144
|
|
|
239
145
|
|
|
File without changes
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from argparse import ArgumentParser
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from git_commit_guard import (
|
|
7
|
+
Result,
|
|
8
|
+
_download_if_missing,
|
|
9
|
+
_ensure_nltk_data,
|
|
10
|
+
_get_message,
|
|
11
|
+
_parse_checks,
|
|
12
|
+
_report,
|
|
13
|
+
_strip_comments,
|
|
14
|
+
check_body,
|
|
15
|
+
check_imperative,
|
|
16
|
+
check_signature,
|
|
17
|
+
check_signed_off,
|
|
18
|
+
check_subject,
|
|
19
|
+
main,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# ruff: noqa: S101 # Use of `assert` detected
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture(scope="session", autouse=True)
|
|
26
|
+
def nltk_data():
|
|
27
|
+
_ensure_nltk_data()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestResult:
|
|
31
|
+
def test_ok_when_empty(self):
|
|
32
|
+
assert Result().ok
|
|
33
|
+
|
|
34
|
+
def test_not_ok_with_error(self):
|
|
35
|
+
r = Result()
|
|
36
|
+
r.error("bad")
|
|
37
|
+
assert not r.ok
|
|
38
|
+
|
|
39
|
+
def test_ok_with_only_warn(self):
|
|
40
|
+
r = Result()
|
|
41
|
+
r.warn("hmm")
|
|
42
|
+
assert r.ok
|
|
43
|
+
|
|
44
|
+
def test_ok_with_only_info(self):
|
|
45
|
+
r = Result()
|
|
46
|
+
r.info("fyi")
|
|
47
|
+
assert r.ok
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TestStripComments:
|
|
51
|
+
def test_removes_comment_lines(self):
|
|
52
|
+
assert _strip_comments("# comment\nfoo") == "foo"
|
|
53
|
+
|
|
54
|
+
def test_keeps_non_comment_lines(self):
|
|
55
|
+
assert _strip_comments("foo\nbar") == "foo\nbar"
|
|
56
|
+
|
|
57
|
+
def test_removes_indented_comments(self):
|
|
58
|
+
assert _strip_comments(" # indented\nfoo") == "foo"
|
|
59
|
+
|
|
60
|
+
def test_empty_string(self):
|
|
61
|
+
assert _strip_comments("") == ""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TestCheckSubject:
|
|
65
|
+
def test_valid_simple(self):
|
|
66
|
+
r = Result()
|
|
67
|
+
desc = check_subject("fix: add token refresh", r)
|
|
68
|
+
assert desc == "add token refresh"
|
|
69
|
+
assert r.ok
|
|
70
|
+
|
|
71
|
+
def test_valid_with_scope(self):
|
|
72
|
+
r = Result()
|
|
73
|
+
desc = check_subject("feat(auth): add login", r)
|
|
74
|
+
assert desc == "add login"
|
|
75
|
+
assert r.ok
|
|
76
|
+
|
|
77
|
+
def test_valid_breaking_change(self):
|
|
78
|
+
r = Result()
|
|
79
|
+
check_subject("feat!: drop v1 support", r)
|
|
80
|
+
assert r.ok
|
|
81
|
+
|
|
82
|
+
def test_valid_breaking_change_with_scope(self):
|
|
83
|
+
r = Result()
|
|
84
|
+
check_subject("feat(api)!: drop v1", r)
|
|
85
|
+
assert r.ok
|
|
86
|
+
|
|
87
|
+
def test_invalid_format(self):
|
|
88
|
+
r = Result()
|
|
89
|
+
desc = check_subject("not a commit", r)
|
|
90
|
+
assert desc is None
|
|
91
|
+
assert not r.ok
|
|
92
|
+
|
|
93
|
+
def test_unknown_type(self):
|
|
94
|
+
r = Result()
|
|
95
|
+
check_subject("unknown: add thing", r)
|
|
96
|
+
assert not r.ok
|
|
97
|
+
|
|
98
|
+
def test_uppercase_description(self):
|
|
99
|
+
r = Result()
|
|
100
|
+
check_subject("fix: Add token", r)
|
|
101
|
+
assert not r.ok
|
|
102
|
+
|
|
103
|
+
def test_trailing_period(self):
|
|
104
|
+
r = Result()
|
|
105
|
+
check_subject("fix: add token.", r)
|
|
106
|
+
assert not r.ok
|
|
107
|
+
|
|
108
|
+
def test_subject_too_long(self):
|
|
109
|
+
r = Result()
|
|
110
|
+
check_subject("fix: " + "a" * 68, r) # 73 chars total
|
|
111
|
+
assert not r.ok
|
|
112
|
+
|
|
113
|
+
def test_subject_at_max_length(self):
|
|
114
|
+
r = Result()
|
|
115
|
+
check_subject("fix: " + "a" * 67, r) # exactly 72 chars
|
|
116
|
+
assert r.ok
|
|
117
|
+
|
|
118
|
+
@pytest.mark.parametrize(
|
|
119
|
+
"type_",
|
|
120
|
+
[
|
|
121
|
+
"feat",
|
|
122
|
+
"fix",
|
|
123
|
+
"docs",
|
|
124
|
+
"style",
|
|
125
|
+
"refactor",
|
|
126
|
+
"perf",
|
|
127
|
+
"test",
|
|
128
|
+
"build",
|
|
129
|
+
"ci",
|
|
130
|
+
"chore",
|
|
131
|
+
"revert",
|
|
132
|
+
],
|
|
133
|
+
)
|
|
134
|
+
def test_all_valid_types(self, type_):
|
|
135
|
+
r = Result()
|
|
136
|
+
check_subject(f"{type_}: add thing", r)
|
|
137
|
+
assert r.ok
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestCheckBody:
|
|
141
|
+
def test_subject_only(self):
|
|
142
|
+
r = Result()
|
|
143
|
+
check_body(["fix: add thing"], r)
|
|
144
|
+
assert not r.ok
|
|
145
|
+
|
|
146
|
+
def test_subject_and_blank_only(self):
|
|
147
|
+
r = Result()
|
|
148
|
+
check_body(["fix: add thing", ""], r)
|
|
149
|
+
assert not r.ok
|
|
150
|
+
|
|
151
|
+
def test_valid_body(self):
|
|
152
|
+
r = Result()
|
|
153
|
+
check_body(["fix: add thing", "", "body text here"], r)
|
|
154
|
+
assert r.ok
|
|
155
|
+
|
|
156
|
+
def test_missing_blank_line(self):
|
|
157
|
+
r = Result()
|
|
158
|
+
check_body(["fix: add thing", "body text", "more"], r)
|
|
159
|
+
assert not r.ok
|
|
160
|
+
|
|
161
|
+
def test_blank_body_content(self):
|
|
162
|
+
r = Result()
|
|
163
|
+
check_body(["fix: add thing", "", " "], r)
|
|
164
|
+
assert not r.ok
|
|
165
|
+
|
|
166
|
+
def test_multiline_body(self):
|
|
167
|
+
r = Result()
|
|
168
|
+
check_body(["fix: add thing", "", "line one", "line two"], r)
|
|
169
|
+
assert r.ok
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class TestCheckSignedOff:
|
|
173
|
+
def test_valid(self):
|
|
174
|
+
r = Result()
|
|
175
|
+
check_signed_off(
|
|
176
|
+
"fix: add thing\n\nbody\n\nSigned-off-by: John Doe <john@example.com>",
|
|
177
|
+
r,
|
|
178
|
+
)
|
|
179
|
+
assert r.ok
|
|
180
|
+
|
|
181
|
+
def test_missing(self):
|
|
182
|
+
r = Result()
|
|
183
|
+
check_signed_off("fix: add thing\n\nbody", r)
|
|
184
|
+
assert not r.ok
|
|
185
|
+
|
|
186
|
+
def test_malformed_no_email(self):
|
|
187
|
+
r = Result()
|
|
188
|
+
check_signed_off("fix: add thing\n\nSigned-off-by: John Doe", r)
|
|
189
|
+
assert not r.ok
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class TestCheckImperative:
|
|
193
|
+
def test_imperative_verb_passes(self):
|
|
194
|
+
r = Result()
|
|
195
|
+
check_imperative("add token refresh", r)
|
|
196
|
+
assert r.ok
|
|
197
|
+
|
|
198
|
+
def test_ed_suffix_fails(self):
|
|
199
|
+
r = Result()
|
|
200
|
+
check_imperative("added token refresh", r)
|
|
201
|
+
assert not r.ok
|
|
202
|
+
|
|
203
|
+
def test_ing_suffix_fails(self):
|
|
204
|
+
r = Result()
|
|
205
|
+
check_imperative("adding token refresh", r)
|
|
206
|
+
assert not r.ok
|
|
207
|
+
|
|
208
|
+
def test_third_person_fails(self):
|
|
209
|
+
r = Result()
|
|
210
|
+
check_imperative("adds token refresh", r)
|
|
211
|
+
assert not r.ok
|
|
212
|
+
|
|
213
|
+
def test_third_person_es_suffix_fails(self):
|
|
214
|
+
r = Result()
|
|
215
|
+
check_imperative("fixes the bug", r)
|
|
216
|
+
assert not r.ok
|
|
217
|
+
|
|
218
|
+
def test_non_whitelist_imperative_passes(self):
|
|
219
|
+
r = Result()
|
|
220
|
+
check_imperative("refactor authentication module", r)
|
|
221
|
+
assert r.ok
|
|
222
|
+
|
|
223
|
+
def test_write_imperative_passes(self):
|
|
224
|
+
r = Result()
|
|
225
|
+
check_imperative("write unit tests", r)
|
|
226
|
+
assert r.ok
|
|
227
|
+
|
|
228
|
+
def test_tagger_misclassified_verb_passes(self):
|
|
229
|
+
# 'disable' is tagged non-VB by the tagger but wordnet confirms it as a verb
|
|
230
|
+
r = Result()
|
|
231
|
+
check_imperative("disable feature flag", r)
|
|
232
|
+
assert r.ok
|
|
233
|
+
|
|
234
|
+
def test_refactor_passes(self):
|
|
235
|
+
r = Result()
|
|
236
|
+
check_imperative("refactor authentication module", r)
|
|
237
|
+
assert r.ok
|
|
238
|
+
|
|
239
|
+
def test_vendor_passes(self):
|
|
240
|
+
r = Result()
|
|
241
|
+
check_imperative("vendor third-party libs", r)
|
|
242
|
+
assert r.ok
|
|
243
|
+
|
|
244
|
+
def test_configure_passes(self):
|
|
245
|
+
r = Result()
|
|
246
|
+
check_imperative("configure logging pipeline", r)
|
|
247
|
+
assert r.ok
|
|
248
|
+
|
|
249
|
+
def test_empty_desc_passes(self):
|
|
250
|
+
r = Result()
|
|
251
|
+
check_imperative("", r)
|
|
252
|
+
assert r.ok
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class TestDownloadIfMissing:
|
|
256
|
+
def test_skips_download_when_present(self):
|
|
257
|
+
with (
|
|
258
|
+
patch("git_commit_guard.nltk.data.find"),
|
|
259
|
+
patch("git_commit_guard.nltk.download") as mock_dl,
|
|
260
|
+
):
|
|
261
|
+
_download_if_missing("tokenizers/punkt_tab")
|
|
262
|
+
mock_dl.assert_not_called()
|
|
263
|
+
|
|
264
|
+
def test_downloads_when_missing(self):
|
|
265
|
+
with (
|
|
266
|
+
patch("git_commit_guard.nltk.data.find", side_effect=LookupError),
|
|
267
|
+
patch("git_commit_guard.nltk.download") as mock_dl,
|
|
268
|
+
):
|
|
269
|
+
_download_if_missing("tokenizers/punkt_tab")
|
|
270
|
+
mock_dl.assert_called_once_with("punkt_tab", quiet=True)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class TestCheckSignature:
|
|
274
|
+
def test_unsigned_commit(self):
|
|
275
|
+
r = Result()
|
|
276
|
+
proc = MagicMock(returncode=1)
|
|
277
|
+
with patch("git_commit_guard.subprocess.run", return_value=proc):
|
|
278
|
+
check_signature("abc123", r)
|
|
279
|
+
assert not r.ok
|
|
280
|
+
|
|
281
|
+
def test_gpg_signed_commit(self):
|
|
282
|
+
r = Result()
|
|
283
|
+
proc = MagicMock(returncode=0, stderr="gpg signature verified")
|
|
284
|
+
with patch("git_commit_guard.subprocess.run", return_value=proc):
|
|
285
|
+
check_signature("abc123", r)
|
|
286
|
+
assert r.ok
|
|
287
|
+
assert any("GPG" in msg for _, msg in r.errors)
|
|
288
|
+
|
|
289
|
+
def test_ssh_signed_commit(self):
|
|
290
|
+
r = Result()
|
|
291
|
+
proc = MagicMock(returncode=0, stderr="Good ssh signature")
|
|
292
|
+
with patch("git_commit_guard.subprocess.run", return_value=proc):
|
|
293
|
+
check_signature("abc123", r)
|
|
294
|
+
assert r.ok
|
|
295
|
+
assert any("SSH" in msg for _, msg in r.errors)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TestGetMessage:
|
|
299
|
+
def test_success(self):
|
|
300
|
+
with patch(
|
|
301
|
+
"git_commit_guard.subprocess.check_output",
|
|
302
|
+
return_value="fix: add thing\n\n",
|
|
303
|
+
):
|
|
304
|
+
assert _get_message("abc123") == "fix: add thing"
|
|
305
|
+
|
|
306
|
+
def test_unknown_revision(self):
|
|
307
|
+
err = subprocess.CalledProcessError(128, "git")
|
|
308
|
+
err.stderr = "fatal: unknown revision 'abc'"
|
|
309
|
+
with (
|
|
310
|
+
patch("git_commit_guard.subprocess.check_output", side_effect=err),
|
|
311
|
+
pytest.raises(SystemExit, match="no commits yet"),
|
|
312
|
+
):
|
|
313
|
+
_get_message("abc")
|
|
314
|
+
|
|
315
|
+
def test_ambiguous_argument(self):
|
|
316
|
+
err = subprocess.CalledProcessError(128, "git")
|
|
317
|
+
err.stderr = "fatal: ambiguous argument 'HEAD'"
|
|
318
|
+
with (
|
|
319
|
+
patch("git_commit_guard.subprocess.check_output", side_effect=err),
|
|
320
|
+
pytest.raises(SystemExit, match="no commits yet"),
|
|
321
|
+
):
|
|
322
|
+
_get_message("HEAD")
|
|
323
|
+
|
|
324
|
+
def test_other_git_error(self):
|
|
325
|
+
err = subprocess.CalledProcessError(128, "git")
|
|
326
|
+
err.stderr = "fatal: not a git repository"
|
|
327
|
+
with (
|
|
328
|
+
patch("git_commit_guard.subprocess.check_output", side_effect=err),
|
|
329
|
+
pytest.raises(SystemExit, match="git error"),
|
|
330
|
+
):
|
|
331
|
+
_get_message("abc")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class TestParseChecks:
|
|
335
|
+
def test_invalid_check_name(self):
|
|
336
|
+
parser = ArgumentParser()
|
|
337
|
+
with pytest.raises(SystemExit):
|
|
338
|
+
_parse_checks(parser, "invalid")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class TestReport:
|
|
342
|
+
def test_all_passed(self, capsys):
|
|
343
|
+
r = Result()
|
|
344
|
+
ret = _report(r)
|
|
345
|
+
assert ret == 0
|
|
346
|
+
assert "all checks passed" in capsys.readouterr().err
|
|
347
|
+
|
|
348
|
+
def test_with_error(self, capsys):
|
|
349
|
+
r = Result()
|
|
350
|
+
r.error("something broke")
|
|
351
|
+
ret = _report(r)
|
|
352
|
+
assert ret == 1
|
|
353
|
+
assert "something broke" in capsys.readouterr().err
|
|
354
|
+
|
|
355
|
+
def test_with_warning_returns_zero(self, capsys):
|
|
356
|
+
r = Result()
|
|
357
|
+
r.warn("heads up")
|
|
358
|
+
ret = _report(r)
|
|
359
|
+
assert ret == 0
|
|
360
|
+
captured = capsys.readouterr().err
|
|
361
|
+
assert "heads up" in captured
|
|
362
|
+
assert "all checks passed" in captured
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
_VALID_MSG = "fix: add thing\n\nbody text\n\nSigned-off-by: A User <a@b.com>"
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class TestMain:
|
|
369
|
+
def test_from_message_file(self, tmp_path, capsys):
|
|
370
|
+
f = tmp_path / "msg"
|
|
371
|
+
f.write_text(_VALID_MSG)
|
|
372
|
+
with patch(
|
|
373
|
+
"sys.argv",
|
|
374
|
+
["cg", "--message-file", str(f), "--disable", "signature"],
|
|
375
|
+
):
|
|
376
|
+
assert main() == 0
|
|
377
|
+
assert "all checks passed" in capsys.readouterr().err
|
|
378
|
+
|
|
379
|
+
def test_from_stdin(self):
|
|
380
|
+
stdin = MagicMock()
|
|
381
|
+
stdin.isatty.return_value = False
|
|
382
|
+
stdin.read.return_value = _VALID_MSG
|
|
383
|
+
with (
|
|
384
|
+
patch("sys.argv", ["cg", "--disable", "signature"]),
|
|
385
|
+
patch("sys.stdin", stdin),
|
|
386
|
+
):
|
|
387
|
+
assert main() == 0
|
|
388
|
+
|
|
389
|
+
def test_from_rev(self):
|
|
390
|
+
with (
|
|
391
|
+
patch("sys.argv", ["cg", "abc123", "--disable", "signature"]),
|
|
392
|
+
patch("git_commit_guard._get_message", return_value=_VALID_MSG),
|
|
393
|
+
):
|
|
394
|
+
assert main() == 0
|
|
395
|
+
|
|
396
|
+
def test_from_head(self):
|
|
397
|
+
stdin = MagicMock()
|
|
398
|
+
stdin.isatty.return_value = True
|
|
399
|
+
with (
|
|
400
|
+
patch("sys.argv", ["cg", "--disable", "signature"]),
|
|
401
|
+
patch("sys.stdin", stdin),
|
|
402
|
+
patch("git_commit_guard._get_message", return_value=_VALID_MSG),
|
|
403
|
+
):
|
|
404
|
+
assert main() == 0
|
|
405
|
+
|
|
406
|
+
def test_enable_flag(self, tmp_path):
|
|
407
|
+
f = tmp_path / "msg"
|
|
408
|
+
f.write_text(_VALID_MSG)
|
|
409
|
+
with patch("sys.argv", ["cg", "--message-file", str(f), "--enable", "subject"]):
|
|
410
|
+
assert main() == 0
|
|
411
|
+
|
|
412
|
+
def test_invalid_message_returns_one(self, tmp_path):
|
|
413
|
+
f = tmp_path / "msg"
|
|
414
|
+
f.write_text("not a valid commit")
|
|
415
|
+
with patch(
|
|
416
|
+
"sys.argv",
|
|
417
|
+
[
|
|
418
|
+
"cg",
|
|
419
|
+
"--message-file",
|
|
420
|
+
str(f),
|
|
421
|
+
"--disable",
|
|
422
|
+
"signature,body,signed-off,imperative",
|
|
423
|
+
],
|
|
424
|
+
):
|
|
425
|
+
assert main() == 1
|
|
426
|
+
|
|
427
|
+
def test_signature_skipped_without_rev(self, tmp_path, capsys):
|
|
428
|
+
f = tmp_path / "msg"
|
|
429
|
+
f.write_text(_VALID_MSG)
|
|
430
|
+
with patch(
|
|
431
|
+
"sys.argv",
|
|
432
|
+
["cg", "--message-file", str(f), "--enable", "signature"],
|
|
433
|
+
):
|
|
434
|
+
ret = main()
|
|
435
|
+
assert ret == 0
|
|
436
|
+
assert "skipped" in capsys.readouterr().err
|
|
437
|
+
|
|
438
|
+
def test_imperative_only_no_subject_check(self, tmp_path):
|
|
439
|
+
# imperative enabled, subject not — desc starts as None, parsed from line
|
|
440
|
+
f = tmp_path / "msg"
|
|
441
|
+
f.write_text(_VALID_MSG)
|
|
442
|
+
with patch(
|
|
443
|
+
"sys.argv",
|
|
444
|
+
["cg", "--message-file", str(f), "--enable", "imperative"],
|
|
445
|
+
):
|
|
446
|
+
assert main() == 0
|
|
447
|
+
|
|
448
|
+
def test_signature_with_rev(self):
|
|
449
|
+
proc = MagicMock(returncode=0, stderr="gpg signature verified")
|
|
450
|
+
with (
|
|
451
|
+
patch("sys.argv", ["cg", "abc123", "--enable", "signature"]),
|
|
452
|
+
patch("git_commit_guard._get_message", return_value=_VALID_MSG),
|
|
453
|
+
patch("git_commit_guard.subprocess.run", return_value=proc),
|
|
454
|
+
):
|
|
455
|
+
assert main() == 0
|
|
456
|
+
|
|
457
|
+
def test_invalid_check_name_exits(self, tmp_path):
|
|
458
|
+
f = tmp_path / "msg"
|
|
459
|
+
f.write_text(_VALID_MSG)
|
|
460
|
+
with (
|
|
461
|
+
patch("sys.argv", ["cg", "--message-file", str(f), "--enable", "bogus"]),
|
|
462
|
+
pytest.raises(SystemExit),
|
|
463
|
+
):
|
|
464
|
+
main()
|
|
@@ -23,6 +23,90 @@ wheels = [
|
|
|
23
23
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "coverage"
|
|
28
|
+
version = "7.13.5"
|
|
29
|
+
source = { registry = "https://pypi.org/simple" }
|
|
30
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" }
|
|
31
|
+
wheels = [
|
|
32
|
+
{ url = "https://files.pythonhosted.org/packages/a0/c3/a396306ba7db865bf96fc1fb3b7fd29bcbf3d829df642e77b13555163cd6/coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01", size = 219554, upload-time = "2026-03-17T10:30:42.208Z" },
|
|
33
|
+
{ url = "https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422", size = 219908, upload-time = "2026-03-17T10:30:43.906Z" },
|
|
34
|
+
{ url = "https://files.pythonhosted.org/packages/29/72/20b917c6793af3a5ceb7fb9c50033f3ec7865f2911a1416b34a7cfa0813b/coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f", size = 251419, upload-time = "2026-03-17T10:30:45.545Z" },
|
|
35
|
+
{ url = "https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5", size = 254159, upload-time = "2026-03-17T10:30:47.204Z" },
|
|
36
|
+
{ url = "https://files.pythonhosted.org/packages/9d/00/7b0edcfe64e2ed4c0340dac14a52ad0f4c9bd0b8b5e531af7d55b703db7c/coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376", size = 255270, upload-time = "2026-03-17T10:30:48.812Z" },
|
|
37
|
+
{ url = "https://files.pythonhosted.org/packages/93/89/7ffc4ba0f5d0a55c1e84ea7cee39c9fc06af7b170513d83fbf3bbefce280/coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256", size = 257538, upload-time = "2026-03-17T10:30:50.77Z" },
|
|
38
|
+
{ url = "https://files.pythonhosted.org/packages/81/bd/73ddf85f93f7e6fa83e77ccecb6162d9415c79007b4bc124008a4995e4a7/coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c", size = 251821, upload-time = "2026-03-17T10:30:52.5Z" },
|
|
39
|
+
{ url = "https://files.pythonhosted.org/packages/a0/81/278aff4e8dec4926a0bcb9486320752811f543a3ce5b602cc7a29978d073/coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5", size = 253191, upload-time = "2026-03-17T10:30:54.543Z" },
|
|
40
|
+
{ url = "https://files.pythonhosted.org/packages/70/ee/fe1621488e2e0a58d7e94c4800f0d96f79671553488d401a612bebae324b/coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09", size = 251337, upload-time = "2026-03-17T10:30:56.663Z" },
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/37/a6/f79fb37aa104b562207cc23cb5711ab6793608e246cae1e93f26b2236ed9/coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9", size = 255404, upload-time = "2026-03-17T10:30:58.427Z" },
|
|
42
|
+
{ url = "https://files.pythonhosted.org/packages/75/f0/ed15262a58ec81ce457ceb717b7f78752a1713556b19081b76e90896e8d4/coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf", size = 250903, upload-time = "2026-03-17T10:31:00.093Z" },
|
|
43
|
+
{ url = "https://files.pythonhosted.org/packages/0f/e9/9129958f20e7e9d4d56d51d42ccf708d15cac355ff4ac6e736e97a9393d2/coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c", size = 252780, upload-time = "2026-03-17T10:31:01.916Z" },
|
|
44
|
+
{ url = "https://files.pythonhosted.org/packages/a4/d7/0ad9b15812d81272db94379fe4c6df8fd17781cc7671fdfa30c76ba5ff7b/coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf", size = 222093, upload-time = "2026-03-17T10:31:03.642Z" },
|
|
45
|
+
{ url = "https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810", size = 222900, upload-time = "2026-03-17T10:31:05.651Z" },
|
|
46
|
+
{ url = "https://files.pythonhosted.org/packages/d4/fa/2238c2ad08e35cf4f020ea721f717e09ec3152aea75d191a7faf3ef009a8/coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de", size = 221515, upload-time = "2026-03-17T10:31:07.293Z" },
|
|
47
|
+
{ url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" },
|
|
48
|
+
{ url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" },
|
|
49
|
+
{ url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26", size = 250935, upload-time = "2026-03-17T10:31:12.392Z" },
|
|
50
|
+
{ url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" },
|
|
51
|
+
{ url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" },
|
|
52
|
+
{ url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a", size = 256912, upload-time = "2026-03-17T10:31:17.89Z" },
|
|
53
|
+
{ url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969", size = 251165, upload-time = "2026-03-17T10:31:19.605Z" },
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" },
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15", size = 250873, upload-time = "2026-03-17T10:31:23.565Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1", size = 255030, upload-time = "2026-03-17T10:31:25.58Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6", size = 250694, upload-time = "2026-03-17T10:31:27.316Z" },
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" },
|
|
59
|
+
{ url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85", size = 222112, upload-time = "2026-03-17T10:31:31.526Z" },
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b", size = 222923, upload-time = "2026-03-17T10:31:33.633Z" },
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664", size = 221540, upload-time = "2026-03-17T10:31:35.445Z" },
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" },
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" },
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806", size = 261912, upload-time = "2026-03-17T10:31:41.324Z" },
|
|
65
|
+
{ url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" },
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" },
|
|
67
|
+
{ url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd", size = 267558, upload-time = "2026-03-17T10:31:48.293Z" },
|
|
68
|
+
{ url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606", size = 261163, upload-time = "2026-03-17T10:31:50.125Z" },
|
|
69
|
+
{ url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" },
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0", size = 261604, upload-time = "2026-03-17T10:31:53.872Z" },
|
|
71
|
+
{ url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87", size = 265321, upload-time = "2026-03-17T10:31:55.997Z" },
|
|
72
|
+
{ url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479", size = 260502, upload-time = "2026-03-17T10:31:58.308Z" },
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a", size = 222788, upload-time = "2026-03-17T10:32:02.246Z" },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819", size = 223851, upload-time = "2026-03-17T10:32:04.416Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911", size = 222104, upload-time = "2026-03-17T10:32:06.65Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" },
|
|
90
|
+
{ url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" },
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" },
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" },
|
|
94
|
+
{ url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" },
|
|
95
|
+
{ url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" },
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" },
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" },
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" },
|
|
102
|
+
{ url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" },
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" },
|
|
104
|
+
{ url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" },
|
|
105
|
+
{ url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" },
|
|
106
|
+
{ url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" },
|
|
107
|
+
{ url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" },
|
|
108
|
+
]
|
|
109
|
+
|
|
26
110
|
[[package]]
|
|
27
111
|
name = "git-commit-guard"
|
|
28
112
|
source = { editable = "." }
|
|
@@ -30,9 +114,30 @@ dependencies = [
|
|
|
30
114
|
{ name = "nltk" },
|
|
31
115
|
]
|
|
32
116
|
|
|
117
|
+
[package.dev-dependencies]
|
|
118
|
+
dev = [
|
|
119
|
+
{ name = "pytest" },
|
|
120
|
+
{ name = "pytest-cov" },
|
|
121
|
+
]
|
|
122
|
+
|
|
33
123
|
[package.metadata]
|
|
34
124
|
requires-dist = [{ name = "nltk", specifier = ">=3.9.3" }]
|
|
35
125
|
|
|
126
|
+
[package.metadata.requires-dev]
|
|
127
|
+
dev = [
|
|
128
|
+
{ name = "pytest", specifier = ">=9.0.2" },
|
|
129
|
+
{ name = "pytest-cov", specifier = ">=7.1.0" },
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
[[package]]
|
|
133
|
+
name = "iniconfig"
|
|
134
|
+
version = "2.3.0"
|
|
135
|
+
source = { registry = "https://pypi.org/simple" }
|
|
136
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
137
|
+
wheels = [
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
139
|
+
]
|
|
140
|
+
|
|
36
141
|
[[package]]
|
|
37
142
|
name = "joblib"
|
|
38
143
|
version = "1.5.3"
|
|
@@ -57,6 +162,63 @@ wheels = [
|
|
|
57
162
|
{ url = "https://files.pythonhosted.org/packages/c2/7e/9af5a710a1236e4772de8dfcc6af942a561327bb9f42b5b4a24d0cf100fd/nltk-3.9.3-py3-none-any.whl", hash = "sha256:60b3db6e9995b3dd976b1f0fa7dec22069b2677e759c28eb69b62ddd44870522", size = 1525385, upload-time = "2026-02-24T12:05:46.54Z" },
|
|
58
163
|
]
|
|
59
164
|
|
|
165
|
+
[[package]]
|
|
166
|
+
name = "packaging"
|
|
167
|
+
version = "26.0"
|
|
168
|
+
source = { registry = "https://pypi.org/simple" }
|
|
169
|
+
sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" }
|
|
170
|
+
wheels = [
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" },
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
[[package]]
|
|
175
|
+
name = "pluggy"
|
|
176
|
+
version = "1.6.0"
|
|
177
|
+
source = { registry = "https://pypi.org/simple" }
|
|
178
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
179
|
+
wheels = [
|
|
180
|
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
[[package]]
|
|
184
|
+
name = "pygments"
|
|
185
|
+
version = "2.19.2"
|
|
186
|
+
source = { registry = "https://pypi.org/simple" }
|
|
187
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
|
188
|
+
wheels = [
|
|
189
|
+
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
[[package]]
|
|
193
|
+
name = "pytest"
|
|
194
|
+
version = "9.0.2"
|
|
195
|
+
source = { registry = "https://pypi.org/simple" }
|
|
196
|
+
dependencies = [
|
|
197
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
198
|
+
{ name = "iniconfig" },
|
|
199
|
+
{ name = "packaging" },
|
|
200
|
+
{ name = "pluggy" },
|
|
201
|
+
{ name = "pygments" },
|
|
202
|
+
]
|
|
203
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
|
204
|
+
wheels = [
|
|
205
|
+
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
[[package]]
|
|
209
|
+
name = "pytest-cov"
|
|
210
|
+
version = "7.1.0"
|
|
211
|
+
source = { registry = "https://pypi.org/simple" }
|
|
212
|
+
dependencies = [
|
|
213
|
+
{ name = "coverage" },
|
|
214
|
+
{ name = "pluggy" },
|
|
215
|
+
{ name = "pytest" },
|
|
216
|
+
]
|
|
217
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" }
|
|
218
|
+
wheels = [
|
|
219
|
+
{ url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" },
|
|
220
|
+
]
|
|
221
|
+
|
|
60
222
|
[[package]]
|
|
61
223
|
name = "regex"
|
|
62
224
|
version = "2026.2.28"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|