pyright-to-gitlab 1.0.2__py3-none-any.whl → 1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyright-to-gitlab
3
- Version: 1.0.2
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,6 @@
1
+ pyright_to_gitlab.py,sha256=6b1sRM7aYWQShpWpAxmz5ANYOZla6-wEOxaZba20f6o,5794
2
+ pyright_to_gitlab-1.2.0.dist-info/METADATA,sha256=R__O7qdhzXfc6Vos3LTzr2q-BNceCWCL1LFpBCEdEoQ,2632
3
+ pyright_to_gitlab-1.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
4
+ pyright_to_gitlab-1.2.0.dist-info/entry_points.txt,sha256=0RnHrvYlZf3zD9BRTVQL564gF_fSuwF5IP13OB18540,61
5
+ pyright_to_gitlab-1.2.0.dist-info/licenses/LICENSE,sha256=tY29-MdtyUNoC3XN4IBzu_jxmb8U82z9UvLkQJuBiJM,1070
6
+ pyright_to_gitlab-1.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pyright-to-gitlab = pyright_to_gitlab:main
pyright_to_gitlab.py CHANGED
@@ -1,65 +1,188 @@
1
1
  """Convert pyright.json output to GitLab Code Quality report format."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import argparse
4
6
  import hashlib
5
7
  import json
6
8
  import sys
7
- from typing import cast
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
8
62
 
9
63
 
10
- def _pyright_to_gitlab(prefix: str = "") -> str:
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:
11
76
  """Convert pyright.json output to GitLab Code Quality report format.
12
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
+
13
81
  :arg prefix: A string to prepend to each file path in the output.
14
82
  This is useful if the application is in a subdirectory of the repository.
15
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.
16
86
 
17
87
  Pyright format at https://github.com/microsoft/pyright/blob/main/docs/command-line.md
18
88
  Gitlab format at https://docs.gitlab.com/ci/testing/code_quality/#code-quality-report-format
19
89
  """
20
- data = cast("dict", json.load(sys.stdin))
21
-
22
- issues = []
23
- for issue in data.get("generalDiagnostics", []):
24
- file = issue["file"]
25
- start, end = issue["range"]["start"], issue["range"]["end"]
26
- rule = "pyright: " + issue.get("rule", "")
27
- severity = "major" if issue["severity"] == "error" else "minor"
28
- # unique fingerprint
29
- fp_str = "--".join([str(start), str(end), rule])
30
-
31
- issues.append(
32
- {
33
- "description": issue["message"],
34
- "severity": severity,
35
- "fingerprint": hashlib.sha3_224(fp_str.encode()).hexdigest(),
36
- "check_name": rule,
37
- "location": {
38
- "path": f"{prefix}{file}",
39
- "positions": {
40
- "begin": {"line": start["line"], "column": start["character"]},
41
- "end": {"line": end["line"], "column": end["character"]},
42
- },
43
- },
44
- }
45
- )
46
- return json.dumps(issues, indent=2)
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
+ )
47
145
 
48
146
 
49
147
  def main() -> None:
50
148
  """Parse arguments and call the conversion function."""
51
149
  parser = argparse.ArgumentParser(
52
- description="Convert pyright.json to GitLab Code Quality report."
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)",
53
176
  )
54
177
  parser.add_argument(
55
178
  "--prefix",
56
179
  type=str,
57
180
  default="",
58
- help="Prefix to add to each file (default: empty string)",
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)",
59
183
  )
60
184
  args = parser.parse_args()
61
-
62
- print(_pyright_to_gitlab(prefix=args.prefix)) # noqa: T201
185
+ args.output.write(_pyright_to_gitlab(input_=args.input, prefix=args.prefix))
63
186
 
64
187
 
65
188
  if __name__ == "__main__": # pragma: no cover
@@ -1,6 +0,0 @@
1
- pyright_to_gitlab.py,sha256=LbPxle60_JFBGMtPZszoNaFmnXEzfWBhc67PuZ1669g,2251
2
- pyright_to_gitlab-1.0.2.dist-info/METADATA,sha256=zxP5XyJcirFWi-yPy09oPOgHM0fkF1eVeg-eHxMhxnM,2034
3
- pyright_to_gitlab-1.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
4
- pyright_to_gitlab-1.0.2.dist-info/entry_points.txt,sha256=MbNff91bPuO2XhW0P03N9O1Z7xtlr3x9duU5BftRbZo,60
5
- pyright_to_gitlab-1.0.2.dist-info/licenses/LICENSE,sha256=tY29-MdtyUNoC3XN4IBzu_jxmb8U82z9UvLkQJuBiJM,1070
6
- pyright_to_gitlab-1.0.2.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- pyrigh-to-gitlab = pyright_to_gitlab:main