pyright-to-gitlab 1.1.0__tar.gz → 1.2.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.
@@ -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.1.0
3
+ Version: 1.2.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.6
26
+ Requires-Python: >=3.8
28
27
  Description-Content-Type: text/markdown
29
28
 
30
29
  # pyright to gitlab
31
- Simple zero-dependency Python script to convert a pyright --outputjson to a gitlab
32
- code-quality report.
30
+
31
+ [![PyPI version](https://img.shields.io/pypi/v/pyright-to-gitlab.svg)](https://pypi.org/project/pyright-to-gitlab)
32
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyright-to-gitlab.svg)](https://pypi.org/project/pyright-to-gitlab)
33
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
34
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
35
+ [![Typing: typed](https://img.shields.io/badge/typing-typed-blue.svg)](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:
@@ -0,0 +1,39 @@
1
+ # pyright to gitlab
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/pyright-to-gitlab.svg)](https://pypi.org/project/pyright-to-gitlab)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyright-to-gitlab.svg)](https://pypi.org/project/pyright-to-gitlab)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
7
+ [![Typing: typed](https://img.shields.io/badge/typing-typed-blue.svg)](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 to the file paths in the output. This is
28
+ useful if the paths in the pyright output are not relative to the root of the repository.
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,9 +1,9 @@
1
1
  [project]
2
2
  name = "pyright-to-gitlab"
3
- version = "1.1.0"
3
+ version = "1.2.0"
4
4
  description = "Convert Pyright JSON output to GitLab CI/CD format"
5
5
  readme = "README.md"
6
- requires-python = ">=3.6"
6
+ requires-python = ">=3.8"
7
7
  authors = [{ name = "Micha Schöll", email = "" }]
8
8
  license = "MIT"
9
9
  license-files = ["LICENSE"]
@@ -12,7 +12,6 @@ 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
- pyrigh-to-gitlab = 'pyright_to_gitlab:main'
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 = [
@@ -0,0 +1,2 @@
1
+ # Marker file for PEP 561
2
+ # This package provides inline type annotations
@@ -0,0 +1,189 @@
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):
15
+ """Pyright Range Element (part of Range)."""
16
+
17
+ line: int
18
+ character: int
19
+
20
+
21
+ class PyrightRange(TypedDict):
22
+ """Pyright Range (Part of Issue)."""
23
+
24
+ start: PyrightRangeElement
25
+ end: PyrightRangeElement
26
+
27
+
28
+ class PyrightIssue(TypedDict):
29
+ """Single Pyright Issue.
30
+
31
+ Note: 'rule' field is optional in practice but marked as required in type hints.
32
+ Runtime code handles this with 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 string 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
+ try:
91
+ data = json.load(input_)
92
+ except json.JSONDecodeError as e:
93
+ err_msg = f"Invalid JSON input: {e}"
94
+ raise ValueError(err_msg) from e
95
+
96
+ if not isinstance(data, dict):
97
+ err_msg = "Input must be a JSON object"
98
+ raise TypeError(err_msg)
99
+
100
+ return json.dumps(
101
+ [
102
+ _pyright_issue_to_gitlab(issue, prefix)
103
+ for issue in data.get("generalDiagnostics", [])
104
+ ],
105
+ indent=2,
106
+ )
107
+
108
+
109
+ def _pyright_issue_to_gitlab(issue: PyrightIssue, prefix: str) -> GitlabIssue:
110
+ """Convert a single issue to gitlab.
111
+
112
+ Uses defensive .get() with defaults to handle missing optional fields.
113
+ File path defaults to '<anonymous>' and missing rule to 'unknown'.
114
+
115
+ :param issue: A pyright single issue.
116
+ :param prefix: The path prefix.
117
+ :returns: A gitlab single issue.
118
+ """
119
+ start, end = (
120
+ issue.get("range", {}).get("start", {}),
121
+ issue.get("range", {}).get("end", {}),
122
+ )
123
+ rule = "pyright: " + issue.get("rule", "unknown")
124
+ # Unique fingerprint including file path to prevent collisions across files
125
+ fp_str = "--".join([issue.get("file", "<anonymous>"), str(start), str(end), rule])
126
+
127
+ return GitlabIssue(
128
+ description=issue.get("message", ""),
129
+ severity="major" if issue.get("severity") == "error" else "minor",
130
+ # Any hash function really works, does not have to be cryptographic.
131
+ fingerprint=hashlib.sha3_224(fp_str.encode()).hexdigest(),
132
+ check_name=rule,
133
+ location=GitlabIssueLocation(
134
+ path=f"{prefix}{issue['file']}" if "file" in issue else "<anonymous>",
135
+ positions=GitlabIssuePositions(
136
+ begin=GitlabIssuePositionLocation(
137
+ line=start.get("line", 0), column=start.get("character", 0)
138
+ ),
139
+ end=GitlabIssuePositionLocation(
140
+ line=end.get("line", 0), column=end.get("character", 0)
141
+ ),
142
+ ),
143
+ ),
144
+ )
145
+
146
+
147
+ def main() -> None:
148
+ """Parse arguments and call the conversion function."""
149
+ parser = argparse.ArgumentParser(
150
+ description=textwrap.dedent("""
151
+ Convert pyright.json to GitLab Code Quality report.
152
+ By default, reads from stdin and writes to stdout."""),
153
+ epilog=textwrap.dedent(
154
+ """
155
+
156
+ Example usage:
157
+ > python pyright . --outputjson | pyright-to-gitlab > gitlab_code_quality.json
158
+ > pyright-to-gitlab -i pyright.json -o gitlab_code_quality.json
159
+ """
160
+ ),
161
+ formatter_class=argparse.RawDescriptionHelpFormatter,
162
+ )
163
+ parser.add_argument(
164
+ "-i",
165
+ "--input",
166
+ type=argparse.FileType("r"),
167
+ default=sys.stdin,
168
+ help="Input file (default: stdin)",
169
+ )
170
+ parser.add_argument(
171
+ "-o",
172
+ "--output",
173
+ type=argparse.FileType("w"),
174
+ default=sys.stdout,
175
+ help="Output file (default: stdout)",
176
+ )
177
+ parser.add_argument(
178
+ "--prefix",
179
+ type=str,
180
+ default="",
181
+ help="Prefix to add to each file entry. This can be used if pyright is run"
182
+ " from a subdirectory of the repository. (default: empty string)",
183
+ )
184
+ args = parser.parse_args()
185
+ args.output.write(_pyright_to_gitlab(input_=args.input, prefix=args.prefix))
186
+
187
+
188
+ if __name__ == "__main__": # pragma: no cover
189
+ main()