pyright-to-gitlab 1.1.0__tar.gz → 1.3.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.
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/CHANGELOG.md +5 -0
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/PKG-INFO +16 -8
- pyright_to_gitlab-1.3.0/README.md +39 -0
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/pyproject.toml +7 -7
- pyright_to_gitlab-1.3.0/src/py.typed +2 -0
- pyright_to_gitlab-1.3.0/src/pyright_to_gitlab.py +202 -0
- pyright_to_gitlab-1.3.0/tests/test_main.py +526 -0
- pyright_to_gitlab-1.3.0/uv.lock +637 -0
- pyright_to_gitlab-1.1.0/README.md +0 -30
- pyright_to_gitlab-1.1.0/src/pyright_to_gitlab.py +0 -92
- pyright_to_gitlab-1.1.0/tests/test_main.py +0 -160
- pyright_to_gitlab-1.1.0/uv.lock +0 -982
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/.github/workflows/python.yml +0 -0
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/.github/workflows/testing.yml +0 -0
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/.gitignore +0 -0
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/LICENSE +0 -0
- {pyright_to_gitlab-1.1.0 → pyright_to_gitlab-1.3.0}/tests/__init__.py +0 -0
|
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
## [Upcoming]
|
|
9
|
+
### Changes
|
|
10
|
+
- Minimum version is now 3.8.
|
|
11
|
+
- Use TypedDict for Pyright and Gitlab issue handling.
|
|
12
|
+
- Update test dependencies to latest.
|
|
13
|
+
|
|
9
14
|
## [1.1.0] - 2025-06-03
|
|
10
15
|
### Added
|
|
11
16
|
- Support for -i/--input and -o/--output options to specify input and output files.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyright-to-gitlab
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Convert Pyright JSON output to GitLab CI/CD format
|
|
5
5
|
Project-URL: homepage, https://github.com/schollm/pyright-to-gitlab/
|
|
6
6
|
Project-URL: repository, https://github.com/schollm/pyright-to-gitlab/
|
|
@@ -12,7 +12,6 @@ Keywords: ci/cd,gitlab,pyright
|
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -24,12 +23,21 @@ Classifier: Topic :: Software Development
|
|
|
24
23
|
Classifier: Topic :: Software Development :: Testing
|
|
25
24
|
Classifier: Topic :: Utilities
|
|
26
25
|
Classifier: Typing :: Typed
|
|
27
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.8
|
|
28
27
|
Description-Content-Type: text/markdown
|
|
29
28
|
|
|
30
29
|
# pyright to gitlab
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/pyright-to-gitlab)
|
|
32
|
+
[](https://pypi.org/project/pyright-to-gitlab)
|
|
33
|
+
[](https://opensource.org/licenses/MIT)
|
|
34
|
+
[](https://github.com/astral-sh/ruff)
|
|
35
|
+
[](https://github.com/python/mypy)
|
|
36
|
+
|
|
37
|
+
Simple Python tool to convert Pyright JSON output to GitLab Code Quality report format.
|
|
38
|
+
|
|
39
|
+
**Zero runtime dependencies** - Pure Python implementation using only the standard library.
|
|
40
|
+
|
|
33
41
|
|
|
34
42
|
## Usage
|
|
35
43
|
Run the script with the path to the pyright output file:
|
|
@@ -44,12 +52,12 @@ $ pip install pyright-to-gitlab
|
|
|
44
52
|
$ pyright . --outputjson | python -m pyright_to_gitlab > code-quality.json
|
|
45
53
|
```
|
|
46
54
|
### Custom path prefix
|
|
47
|
-
The `--prefix` option adds a custom prefix to the file paths in the output. This is
|
|
48
|
-
useful
|
|
55
|
+
The `--prefix` option adds a custom prefix path to the file paths in the output. This is
|
|
56
|
+
useful for mono-repos, where the paths in the pyright output is not the repository root.
|
|
49
57
|
|
|
50
58
|
|
|
51
59
|
```shell
|
|
52
|
-
$ pyright . --outputjson | pyright-to-gitlab --prefix my-app
|
|
60
|
+
$ pyright . --outputjson | pyright-to-gitlab --prefix my-app > code-quality.json
|
|
53
61
|
```
|
|
54
62
|
|
|
55
63
|
## Testing
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# pyright to gitlab
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/pyright-to-gitlab)
|
|
4
|
+
[](https://pypi.org/project/pyright-to-gitlab)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
[](https://github.com/python/mypy)
|
|
8
|
+
|
|
9
|
+
Simple Python tool to convert Pyright JSON output to GitLab Code Quality report format.
|
|
10
|
+
|
|
11
|
+
**Zero runtime dependencies** - Pure Python implementation using only the standard library.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
Run the script with the path to the pyright output file:
|
|
16
|
+
```shell
|
|
17
|
+
$ pip install pyright-to-gitlab
|
|
18
|
+
$ pyright . --outputjson | pyright-to-gitlab > code-quality.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Alternatively, the module can be called:
|
|
22
|
+
```shell
|
|
23
|
+
$ pip install pyright-to-gitlab
|
|
24
|
+
$ pyright . --outputjson | python -m pyright_to_gitlab > code-quality.json
|
|
25
|
+
```
|
|
26
|
+
### Custom path prefix
|
|
27
|
+
The `--prefix` option adds a custom prefix path to the file paths in the output. This is
|
|
28
|
+
useful for mono-repos, where the paths in the pyright output is not the repository root.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
```shell
|
|
32
|
+
$ pyright . --outputjson | pyright-to-gitlab --prefix my-app > code-quality.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Testing
|
|
36
|
+
To run the tests, execute
|
|
37
|
+
```shell
|
|
38
|
+
$ uv run pytest tests/
|
|
39
|
+
```
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyright-to-gitlab"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.3.0"
|
|
4
4
|
description = "Convert Pyright JSON output to GitLab CI/CD format"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
7
|
authors = [{ name = "Micha Schöll", email = "" }]
|
|
8
8
|
license = "MIT"
|
|
9
9
|
license-files = ["LICENSE"]
|
|
10
|
-
keywords = [ "ci/cd", "pyright",
|
|
10
|
+
keywords = [ "ci/cd", "pyright", "gitlab" ]
|
|
11
11
|
classifiers = [
|
|
12
12
|
"Development Status :: 5 - Production/Stable",
|
|
13
13
|
"Intended Audience :: Developers",
|
|
14
14
|
"License :: OSI Approved :: MIT License",
|
|
15
|
-
"Programming Language :: Python :: 3.7",
|
|
16
15
|
"Programming Language :: Python :: 3.8",
|
|
17
16
|
"Programming Language :: Python :: 3.9",
|
|
18
17
|
"Programming Language :: Python :: 3.10",
|
|
@@ -32,7 +31,7 @@ repository = "https://github.com/schollm/pyright-to-gitlab/"
|
|
|
32
31
|
pypi = "https://pypi.org/project/pyright-to-gitlab"
|
|
33
32
|
|
|
34
33
|
[project.scripts]
|
|
35
|
-
|
|
34
|
+
pyright-to-gitlab = 'pyright_to_gitlab:main'
|
|
36
35
|
|
|
37
36
|
[build-system]
|
|
38
37
|
requires = ["hatchling"]
|
|
@@ -40,6 +39,7 @@ build-backend = "hatchling.build"
|
|
|
40
39
|
|
|
41
40
|
[tool.hatch.build.targets.wheel]
|
|
42
41
|
packages = ["src/pyright_to_gitlab.py"]
|
|
42
|
+
artifacts = ["src/py.typed"]
|
|
43
43
|
|
|
44
44
|
[dependency-groups]
|
|
45
45
|
dev = [
|
|
@@ -59,7 +59,6 @@ ignore = [
|
|
|
59
59
|
"D203", # `incorrect-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible.
|
|
60
60
|
"COM812", # Missing trailing comma in a single-line list - already applied by ruff format.
|
|
61
61
|
"D205", # 1 blank line required between summary line and description: ruff format disagrees.
|
|
62
|
-
|
|
63
62
|
]
|
|
64
63
|
|
|
65
64
|
[tool.ruff.lint.per-file-ignores]
|
|
@@ -68,6 +67,7 @@ ignore = [
|
|
|
68
67
|
"S101", # asserts allowed in tests...
|
|
69
68
|
"ARG", # Unused function args -> fixtures nevertheless are functionally relevant...
|
|
70
69
|
"FBT", # Don't care about booleans as positional arguments in tests, e.g. via @pytest.mark.parametrize()
|
|
70
|
+
"PLR0913", # Too many arguments in test functions are sometimes necessary (e.g. via fixtures)
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
[tool.pytest.ini_options]
|
|
@@ -78,4 +78,4 @@ addopts = """
|
|
|
78
78
|
--cov-report=html:.out/coverage-html
|
|
79
79
|
--cov-report term-missing
|
|
80
80
|
--cov-branch
|
|
81
|
-
"""
|
|
81
|
+
"""
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Convert pyright.json output to GitLab Code Quality report format."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
import textwrap
|
|
10
|
+
from typing import Literal, TextIO, TypedDict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Typing for PyRight Issue
|
|
14
|
+
class PyrightRangeElement(TypedDict, total=False):
|
|
15
|
+
"""Pyright Range Element (part of Range)."""
|
|
16
|
+
|
|
17
|
+
line: int
|
|
18
|
+
character: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PyrightRange(TypedDict, total=False):
|
|
22
|
+
"""Pyright Range (Part of Issue)."""
|
|
23
|
+
|
|
24
|
+
start: PyrightRangeElement
|
|
25
|
+
end: PyrightRangeElement
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PyrightIssue(TypedDict, total=False):
|
|
29
|
+
"""Single Pyright Issue.
|
|
30
|
+
|
|
31
|
+
Note: total=False makes all fields optional. Runtime code handles this with
|
|
32
|
+
defensive .get() calls.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
file: str
|
|
36
|
+
severity: Literal["error", "warning", "information"]
|
|
37
|
+
message: str
|
|
38
|
+
rule: str # Optional in practice, handled via .get() at runtime
|
|
39
|
+
range: PyrightRange
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Typing for Gitlab Issue
|
|
43
|
+
class GitlabIssuePositionLocation(TypedDict):
|
|
44
|
+
"""Single Gitlab Position (Part of Position)."""
|
|
45
|
+
|
|
46
|
+
line: int
|
|
47
|
+
column: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class GitlabIssuePositions(TypedDict):
|
|
51
|
+
"""Gitlab ranged Position within a file (Part of Location)."""
|
|
52
|
+
|
|
53
|
+
begin: GitlabIssuePositionLocation
|
|
54
|
+
end: GitlabIssuePositionLocation
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class GitlabIssueLocation(TypedDict):
|
|
58
|
+
"""Gitlab location (Part of Issue)."""
|
|
59
|
+
|
|
60
|
+
path: str
|
|
61
|
+
positions: GitlabIssuePositions
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class GitlabIssue(TypedDict):
|
|
65
|
+
"""Single Gitlab Issue."""
|
|
66
|
+
|
|
67
|
+
description: str
|
|
68
|
+
severity: Literal["major", "minor"]
|
|
69
|
+
fingerprint: str
|
|
70
|
+
check_name: str
|
|
71
|
+
location: GitlabIssueLocation
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Functions
|
|
75
|
+
def _pyright_to_gitlab(input_: TextIO, prefix: str = "") -> str:
|
|
76
|
+
"""Convert pyright.json output to GitLab Code Quality report format.
|
|
77
|
+
|
|
78
|
+
Line numbers from Pyright are passed through unchanged (0-based per LSP spec).
|
|
79
|
+
GitLab expects the same format, so no conversion is needed.
|
|
80
|
+
|
|
81
|
+
:arg prefix: A path to prepend to each file path in the output.
|
|
82
|
+
This is useful if the application is in a subdirectory of the repository.
|
|
83
|
+
:return: JSON of issues in GitLab Code Quality report format.
|
|
84
|
+
:raises ValueError: If input is not a JSON object.
|
|
85
|
+
:raises TypeError: If input JSON is not an object.
|
|
86
|
+
|
|
87
|
+
Pyright format at https://github.com/microsoft/pyright/blob/main/docs/command-line.md
|
|
88
|
+
Gitlab format at https://docs.gitlab.com/ci/testing/code_quality/#code-quality-report-format
|
|
89
|
+
"""
|
|
90
|
+
if prefix and not prefix.endswith("/"):
|
|
91
|
+
prefix += "/"
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
data = json.load(input_)
|
|
95
|
+
except json.JSONDecodeError as e:
|
|
96
|
+
err_msg = f"Invalid JSON input: {e}"
|
|
97
|
+
raise ValueError(err_msg) from e
|
|
98
|
+
|
|
99
|
+
if not isinstance(data, dict):
|
|
100
|
+
err_msg = "Input must be a JSON object"
|
|
101
|
+
raise TypeError(err_msg)
|
|
102
|
+
|
|
103
|
+
return json.dumps(
|
|
104
|
+
[
|
|
105
|
+
_pyright_issue_to_gitlab(issue, prefix)
|
|
106
|
+
for issue in data.get("generalDiagnostics", [])
|
|
107
|
+
],
|
|
108
|
+
indent=2,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _pyright_issue_to_gitlab(issue: PyrightIssue, prefix: str) -> GitlabIssue:
|
|
113
|
+
"""Convert a single issue to gitlab.
|
|
114
|
+
|
|
115
|
+
Uses defensive .get() with defaults to handle missing optional fields.
|
|
116
|
+
File path defaults to '<anonymous>' and missing rule to 'unknown'.
|
|
117
|
+
|
|
118
|
+
:param issue: A pyright single issue.
|
|
119
|
+
:param prefix: The path prefix.
|
|
120
|
+
:returns: A gitlab single issue.
|
|
121
|
+
"""
|
|
122
|
+
range_ = issue.get("range", {})
|
|
123
|
+
start, end = (range_.get("start", {}), range_.get("end", {}))
|
|
124
|
+
rule = "pyright: " + issue.get("rule", "unknown")
|
|
125
|
+
# Hash input must contain file, location and rule to generate a unique fingerprint.
|
|
126
|
+
# (This takes advantage of stable dict order).
|
|
127
|
+
fingerprint = f"{issue.get('file', '<anonymous>')}--{range_}--{rule}"
|
|
128
|
+
|
|
129
|
+
return GitlabIssue(
|
|
130
|
+
description=issue.get("message", ""),
|
|
131
|
+
# Map 'error' to 'major', all others, including empty, to 'minor'
|
|
132
|
+
severity="major" if issue.get("severity") == "error" else "minor",
|
|
133
|
+
# Any hash function really works, does not have to be cryptographic.
|
|
134
|
+
fingerprint=_hash(fingerprint),
|
|
135
|
+
check_name=rule,
|
|
136
|
+
location=GitlabIssueLocation(
|
|
137
|
+
path=f"{prefix}{issue.get('file', '<anonymous>')}",
|
|
138
|
+
positions=GitlabIssuePositions(
|
|
139
|
+
begin=GitlabIssuePositionLocation(
|
|
140
|
+
line=start.get("line", 0), column=start.get("character", 0)
|
|
141
|
+
),
|
|
142
|
+
end=GitlabIssuePositionLocation(
|
|
143
|
+
line=end.get("line", 0), column=end.get("character", 0)
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _hash(data: str) -> str:
|
|
151
|
+
"""Generate an (non-secure) hash of the given data string.
|
|
152
|
+
|
|
153
|
+
:param data: The input string to hash.
|
|
154
|
+
:returns: The hexadecimal representation of the MD5 hash.
|
|
155
|
+
"""
|
|
156
|
+
return hashlib.new("md5", data.encode(), usedforsecurity=False).hexdigest()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def cli() -> None:
|
|
160
|
+
"""Parse arguments and call the conversion function."""
|
|
161
|
+
parser = argparse.ArgumentParser(
|
|
162
|
+
description=textwrap.dedent("""
|
|
163
|
+
Convert pyright.json to GitLab Code Quality report.
|
|
164
|
+
By default, reads from stdin and writes to stdout."""),
|
|
165
|
+
epilog=textwrap.dedent(
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
Example usage:
|
|
169
|
+
> python pyright . --outputjson | pyright-to-gitlab > gitlab_code_quality.json
|
|
170
|
+
> pyright-to-gitlab -i pyright.json -o gitlab_code_quality.json
|
|
171
|
+
"""
|
|
172
|
+
),
|
|
173
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
174
|
+
)
|
|
175
|
+
parser.add_argument(
|
|
176
|
+
"-i",
|
|
177
|
+
"--input",
|
|
178
|
+
type=argparse.FileType("r"),
|
|
179
|
+
default=sys.stdin,
|
|
180
|
+
help="Input file (default: stdin)",
|
|
181
|
+
)
|
|
182
|
+
parser.add_argument(
|
|
183
|
+
"-o",
|
|
184
|
+
"--output",
|
|
185
|
+
type=argparse.FileType("w"),
|
|
186
|
+
default=sys.stdout,
|
|
187
|
+
help="Output file (default: stdout)",
|
|
188
|
+
)
|
|
189
|
+
parser.add_argument(
|
|
190
|
+
"--prefix",
|
|
191
|
+
type=str,
|
|
192
|
+
default="",
|
|
193
|
+
help="Prefix path to add to each file entry. This can be used if pyright is run"
|
|
194
|
+
" from a subdirectory of the repository. (default: empty string)",
|
|
195
|
+
)
|
|
196
|
+
parser.add_argument("--version", action="version", version="%(prog)s 1.3.0")
|
|
197
|
+
args = parser.parse_args()
|
|
198
|
+
args.output.write(_pyright_to_gitlab(input_=args.input, prefix=args.prefix))
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__": # pragma: no cover
|
|
202
|
+
cli()
|