buganize 0.2.2__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.
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: buganize
3
+ Version: 0.2.2
4
+ Summary: Python client for the Chromium Issue Tracker
5
+ Author: Ritchie Mwewa
6
+ Author-email: Ritchie Mwewa <hi@rly0nheart.com>
7
+ License-Expression: MIT
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Classifier: Topic :: Software Development :: Bug Tracking
17
+ Classifier: Framework :: AsyncIO
18
+ Requires-Dist: httpx>=0.28.1
19
+ Requires-Dist: pandas>=3.0.1
20
+ Requires-Dist: rich>=14.3.3
21
+ Requires-Dist: update-checker>=0.18.0
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Buganize
26
+
27
+ Python client for the Chromium Issue Tracker.
28
+
29
+ ## Table of Contents
30
+
31
+ - [Installation](#installation)
32
+ - [Usage as a library](#usage-as-a-library)
33
+ - [Search issues](#search-issues)
34
+ - [Get a single issue](#get-a-single-issue)
35
+ - [Batch get issues](#batch-get-issues)
36
+ - [Get comments](#get-comments)
37
+ - [Get full updates](#get-full-updates)
38
+ - [CLI usage](#cli-usage)
39
+ - [Search](#search)
40
+ - [Issue](#issue)
41
+ - [Issues (batch)](#issues-batch)
42
+ - [Comments](#comments)
43
+ - [Extra fields](#extra-fields)
44
+ - [Export](#export)
45
+ - [Debug logging](#debug-logging)
46
+ - [Timeout](#timeout)
47
+ - [How it works](#how-it-works)
48
+ - [Limitations](#limitations)
49
+
50
+ ## Installation
51
+
52
+ ```shell
53
+ pip install buganize
54
+ ```
55
+
56
+ > [!Tip]
57
+ > Running `pip install buganise` will also install the package
58
+
59
+ ## Usage as a library
60
+
61
+ ### Search issues
62
+
63
+ ```python
64
+ from buganize import Buganize
65
+
66
+
67
+ async def search():
68
+ async with Buganize() as client:
69
+ result = await client.search("status:open component:Blink", page_size=10)
70
+
71
+ print(f"{result.total_count} total matches")
72
+ for issue in result.issues:
73
+ print(f"#{issue.id} [{issue.status.name}] {issue.title}")
74
+
75
+ # Pagination
76
+ if result.has_more:
77
+ page2 = await client.next_page(result)
78
+ for issue in page2.issues:
79
+ print(f"#{issue.id} [{issue.status.name}] {issue.title}")
80
+ ```
81
+
82
+ ### Get a single issue
83
+
84
+ ```python
85
+ async def get_issue():
86
+ async with Buganize() as client:
87
+ issue = await client.issue(40060244)
88
+
89
+ print(issue.title)
90
+ print(issue.url) # https://issues.chromium.org/issues/40060244
91
+ print(issue.status.name) # e.g. "FIXED"
92
+ print(issue.priority.name) # e.g. "P2"
93
+ print(issue.os) # e.g. ["Linux", "Mac", "Windows"]
94
+ print(issue.cve) # e.g. ["CVE-2024-1234"]
95
+ ```
96
+
97
+ ### Batch get issues
98
+
99
+ ```python
100
+ async def batch_get():
101
+ async with Buganize() as client:
102
+ issues = await client.issues([40060244, 485912774, 486077869])
103
+
104
+ for issue in issues:
105
+ print(f"#{issue.id} - {issue.title}")
106
+ ```
107
+
108
+ ### Get comments
109
+
110
+ ```python
111
+ async def get_comments():
112
+ async with Buganize() as client:
113
+ comments = await client.comments(486077869)
114
+
115
+ for comment in comments:
116
+ print(f"#{comment.comment_number} by {comment.author}")
117
+ print(comment.body)
118
+ ```
119
+
120
+ ### Get full updates
121
+
122
+ Updates include both comments and field changes (status changes, priority changes, etc.):
123
+
124
+ ```python
125
+ async def get_updates():
126
+ async with Buganize() as client:
127
+ result = await client.issue_updates(486077869)
128
+
129
+ print(f"{result.total_count} total updates")
130
+
131
+ # Just the comments, in chronological order
132
+ for comment in result.comments:
133
+ print(f"#{comment.comment_number}: {comment.body[:80]}")
134
+
135
+ # All updates (newest first), including field changes
136
+ for update in result.updates:
137
+ if update.field_changes:
138
+ changed = ", ".join(fc.field for fc in update.field_changes)
139
+ print(f" Fields changed: {changed}")
140
+ if update.comment:
141
+ print(f" Comment: {update.comment.body[:80]}")
142
+ ```
143
+
144
+ ## CLI usage
145
+
146
+ Run with `python -m buganize <command>` or just `buganize <command>`.
147
+
148
+ ### Search
149
+
150
+ ```bash
151
+ # Search for open issues
152
+ buganize search "status:open"
153
+
154
+ # Combined filters
155
+ buganize search "status:open component:Blink"
156
+
157
+ # Results per page (choices: 25, 50, 100, 250)
158
+ buganize search "type:bug" -n 100
159
+
160
+ # Fetch a total of 200 results, paginating as needed
161
+ buganize search "status:open" -l 200
162
+ ```
163
+
164
+ ### Issue
165
+
166
+ ```bash
167
+ buganize issue 486077869
168
+ ```
169
+
170
+ ### Issues (batch)
171
+
172
+ ```bash
173
+ buganize issues 40060244 485912774 486077869
174
+ ```
175
+
176
+ ### Comments
177
+
178
+ ```bash
179
+ buganize comments 486077869
180
+ ```
181
+
182
+ ### Extra fields
183
+
184
+ By default, the table output only shows ID, Status, Priority, and Title. You can show additional columns:
185
+
186
+ ```bash
187
+ # Show specific extra fields
188
+ buganize search "status:open" -f owner os milestone
189
+
190
+ # Show all available fields
191
+ buganize search "status:open" -F
192
+
193
+ # Works with issue and issues too
194
+ buganize issue 486077869 --fields cve tags labels
195
+ buganize issue 486077869 --all-fields
196
+ ```
197
+
198
+ Available extra field names: `owner`, `reporter`, `verifier`, `type`, `component`, `tags`, `ancestor_tags`, `labels`,
199
+ `os`, `milestone`, `ccs`, `hotlists`, `blocking`, `cve`, `cwe`, `build`, `introduced_in`, `merge`, `merge_request`,
200
+ `release_block`, `notice`, `flaky_test`, `est_days`, `next_action`, `vrp_reward`, `irm_link`, `sec_release`, `fixed_by`,
201
+ `created`, `modified`, `verified`, `comments`, `stars`, `last_modifier`.
202
+
203
+ ### Export
204
+
205
+ All commands support `-e/--export` for exporting to CSV, JSON, or HTML. You can specify multiple formats at once:
206
+
207
+ ```bash
208
+ buganize -e csv search "status:open" -n 50
209
+ buganize -e json issue 486077869
210
+ buganize -e html comments 486077869
211
+
212
+ # Multiple formats in one command
213
+ buganize -e csv json search "status:open"
214
+ ```
215
+
216
+ ### Debug logging
217
+
218
+ Use `-d/--debug` to see HTTP request/response details:
219
+
220
+ ```bash
221
+ buganize --debug search "status:open"
222
+ ```
223
+
224
+ ### Timeout
225
+
226
+ Use `-t/--timeout` to set the HTTP request timeout in seconds (default: 30):
227
+
228
+ ```bash
229
+ buganize -t 60 search "status:open"
230
+ ```
231
+
232
+ > [!Tip]
233
+ > British English spelling `buganise` will also work.
234
+
235
+ ## How it works
236
+
237
+ The Chromium issue tracker at `issues.chromium.org` doesn't have a documented public API. But the web frontend talks to
238
+ a set of POST endpoints under `https://issues.chromium.org/action/` using JSON arrays as request/response bodies. This
239
+ library speaks that same protocol.
240
+
241
+ Every response from the API starts with `)]}'\n` (an anti-XSSI prefix) followed by a JSON array. Issue data comes back
242
+ as 48-element positional arrays, no keys, just indexes. The parser maps those indexes to fields on the `Issue`
243
+ dataclass. Custom fields (things like OS, milestone, CVE, component tags) are embedded inside the issue array at a
244
+ specific offset and have their own internal structure.
245
+
246
+ No cookies or tokens are needed for reading public issues. The only headers required are `Content-Type`, `Origin`,
247
+ `Referer`, and a browser-like `User-Agent`.
248
+
249
+ ## Limitations
250
+
251
+ - This uses an undocumented API. It could break if Google changes the response format.
252
+ - Only works with public issues. Private/restricted issues need authentication cookies that this client doesn't handle.
253
+ - The parser is entirely index-based. If the API adds or removes fields from the arrays, the parsing will silently
254
+ return wrong data.
255
+ - Pagination for updates (comments) is not fully wired up, currently fetches the first page only.
256
+ - The batch endpoint may not return issues in the same order as the input IDs.
@@ -0,0 +1,232 @@
1
+ # Buganize
2
+
3
+ Python client for the Chromium Issue Tracker.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Usage as a library](#usage-as-a-library)
9
+ - [Search issues](#search-issues)
10
+ - [Get a single issue](#get-a-single-issue)
11
+ - [Batch get issues](#batch-get-issues)
12
+ - [Get comments](#get-comments)
13
+ - [Get full updates](#get-full-updates)
14
+ - [CLI usage](#cli-usage)
15
+ - [Search](#search)
16
+ - [Issue](#issue)
17
+ - [Issues (batch)](#issues-batch)
18
+ - [Comments](#comments)
19
+ - [Extra fields](#extra-fields)
20
+ - [Export](#export)
21
+ - [Debug logging](#debug-logging)
22
+ - [Timeout](#timeout)
23
+ - [How it works](#how-it-works)
24
+ - [Limitations](#limitations)
25
+
26
+ ## Installation
27
+
28
+ ```shell
29
+ pip install buganize
30
+ ```
31
+
32
+ > [!Tip]
33
+ > Running `pip install buganise` will also install the package
34
+
35
+ ## Usage as a library
36
+
37
+ ### Search issues
38
+
39
+ ```python
40
+ from buganize import Buganize
41
+
42
+
43
+ async def search():
44
+ async with Buganize() as client:
45
+ result = await client.search("status:open component:Blink", page_size=10)
46
+
47
+ print(f"{result.total_count} total matches")
48
+ for issue in result.issues:
49
+ print(f"#{issue.id} [{issue.status.name}] {issue.title}")
50
+
51
+ # Pagination
52
+ if result.has_more:
53
+ page2 = await client.next_page(result)
54
+ for issue in page2.issues:
55
+ print(f"#{issue.id} [{issue.status.name}] {issue.title}")
56
+ ```
57
+
58
+ ### Get a single issue
59
+
60
+ ```python
61
+ async def get_issue():
62
+ async with Buganize() as client:
63
+ issue = await client.issue(40060244)
64
+
65
+ print(issue.title)
66
+ print(issue.url) # https://issues.chromium.org/issues/40060244
67
+ print(issue.status.name) # e.g. "FIXED"
68
+ print(issue.priority.name) # e.g. "P2"
69
+ print(issue.os) # e.g. ["Linux", "Mac", "Windows"]
70
+ print(issue.cve) # e.g. ["CVE-2024-1234"]
71
+ ```
72
+
73
+ ### Batch get issues
74
+
75
+ ```python
76
+ async def batch_get():
77
+ async with Buganize() as client:
78
+ issues = await client.issues([40060244, 485912774, 486077869])
79
+
80
+ for issue in issues:
81
+ print(f"#{issue.id} - {issue.title}")
82
+ ```
83
+
84
+ ### Get comments
85
+
86
+ ```python
87
+ async def get_comments():
88
+ async with Buganize() as client:
89
+ comments = await client.comments(486077869)
90
+
91
+ for comment in comments:
92
+ print(f"#{comment.comment_number} by {comment.author}")
93
+ print(comment.body)
94
+ ```
95
+
96
+ ### Get full updates
97
+
98
+ Updates include both comments and field changes (status changes, priority changes, etc.):
99
+
100
+ ```python
101
+ async def get_updates():
102
+ async with Buganize() as client:
103
+ result = await client.issue_updates(486077869)
104
+
105
+ print(f"{result.total_count} total updates")
106
+
107
+ # Just the comments, in chronological order
108
+ for comment in result.comments:
109
+ print(f"#{comment.comment_number}: {comment.body[:80]}")
110
+
111
+ # All updates (newest first), including field changes
112
+ for update in result.updates:
113
+ if update.field_changes:
114
+ changed = ", ".join(fc.field for fc in update.field_changes)
115
+ print(f" Fields changed: {changed}")
116
+ if update.comment:
117
+ print(f" Comment: {update.comment.body[:80]}")
118
+ ```
119
+
120
+ ## CLI usage
121
+
122
+ Run with `python -m buganize <command>` or just `buganize <command>`.
123
+
124
+ ### Search
125
+
126
+ ```bash
127
+ # Search for open issues
128
+ buganize search "status:open"
129
+
130
+ # Combined filters
131
+ buganize search "status:open component:Blink"
132
+
133
+ # Results per page (choices: 25, 50, 100, 250)
134
+ buganize search "type:bug" -n 100
135
+
136
+ # Fetch a total of 200 results, paginating as needed
137
+ buganize search "status:open" -l 200
138
+ ```
139
+
140
+ ### Issue
141
+
142
+ ```bash
143
+ buganize issue 486077869
144
+ ```
145
+
146
+ ### Issues (batch)
147
+
148
+ ```bash
149
+ buganize issues 40060244 485912774 486077869
150
+ ```
151
+
152
+ ### Comments
153
+
154
+ ```bash
155
+ buganize comments 486077869
156
+ ```
157
+
158
+ ### Extra fields
159
+
160
+ By default, the table output only shows ID, Status, Priority, and Title. You can show additional columns:
161
+
162
+ ```bash
163
+ # Show specific extra fields
164
+ buganize search "status:open" -f owner os milestone
165
+
166
+ # Show all available fields
167
+ buganize search "status:open" -F
168
+
169
+ # Works with issue and issues too
170
+ buganize issue 486077869 --fields cve tags labels
171
+ buganize issue 486077869 --all-fields
172
+ ```
173
+
174
+ Available extra field names: `owner`, `reporter`, `verifier`, `type`, `component`, `tags`, `ancestor_tags`, `labels`,
175
+ `os`, `milestone`, `ccs`, `hotlists`, `blocking`, `cve`, `cwe`, `build`, `introduced_in`, `merge`, `merge_request`,
176
+ `release_block`, `notice`, `flaky_test`, `est_days`, `next_action`, `vrp_reward`, `irm_link`, `sec_release`, `fixed_by`,
177
+ `created`, `modified`, `verified`, `comments`, `stars`, `last_modifier`.
178
+
179
+ ### Export
180
+
181
+ All commands support `-e/--export` for exporting to CSV, JSON, or HTML. You can specify multiple formats at once:
182
+
183
+ ```bash
184
+ buganize -e csv search "status:open" -n 50
185
+ buganize -e json issue 486077869
186
+ buganize -e html comments 486077869
187
+
188
+ # Multiple formats in one command
189
+ buganize -e csv json search "status:open"
190
+ ```
191
+
192
+ ### Debug logging
193
+
194
+ Use `-d/--debug` to see HTTP request/response details:
195
+
196
+ ```bash
197
+ buganize --debug search "status:open"
198
+ ```
199
+
200
+ ### Timeout
201
+
202
+ Use `-t/--timeout` to set the HTTP request timeout in seconds (default: 30):
203
+
204
+ ```bash
205
+ buganize -t 60 search "status:open"
206
+ ```
207
+
208
+ > [!Tip]
209
+ > British English spelling `buganise` will also work.
210
+
211
+ ## How it works
212
+
213
+ The Chromium issue tracker at `issues.chromium.org` doesn't have a documented public API. But the web frontend talks to
214
+ a set of POST endpoints under `https://issues.chromium.org/action/` using JSON arrays as request/response bodies. This
215
+ library speaks that same protocol.
216
+
217
+ Every response from the API starts with `)]}'\n` (an anti-XSSI prefix) followed by a JSON array. Issue data comes back
218
+ as 48-element positional arrays, no keys, just indexes. The parser maps those indexes to fields on the `Issue`
219
+ dataclass. Custom fields (things like OS, milestone, CVE, component tags) are embedded inside the issue array at a
220
+ specific offset and have their own internal structure.
221
+
222
+ No cookies or tokens are needed for reading public issues. The only headers required are `Content-Type`, `Origin`,
223
+ `Referer`, and a browser-like `User-Agent`.
224
+
225
+ ## Limitations
226
+
227
+ - This uses an undocumented API. It could break if Google changes the response format.
228
+ - Only works with public issues. Private/restricted issues need authentication cookies that this client doesn't handle.
229
+ - The parser is entirely index-based. If the API adds or removes fields from the arrays, the parsing will silently
230
+ return wrong data.
231
+ - Pagination for updates (comments) is not fully wired up, currently fetches the first page only.
232
+ - The batch endpoint may not return issues in the same order as the input IDs.
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "buganize"
3
+ version = "0.2.2"
4
+ description = "Python client for the Chromium Issue Tracker"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.11"
8
+ authors = [
9
+ { name = "Ritchie Mwewa", email = "hi@rly0nheart.com" },
10
+ ]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Software Development :: Libraries :: Python Modules",
20
+ "Topic :: Software Development :: Bug Tracking",
21
+ "Framework :: AsyncIO",
22
+ ]
23
+ dependencies = [
24
+ "httpx>=0.28.1",
25
+ "pandas>=3.0.1",
26
+ "rich>=14.3.3",
27
+ "update-checker>=0.18.0",
28
+ ]
29
+
30
+ [project.scripts]
31
+ buganize = "buganize.cli.main:start"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "pytest>=9.0.2",
36
+ "pytest-asyncio>=1.3.0",
37
+ "black>=26.1.0"
38
+ ]
39
+
40
+ [tool.pytest.ini_options]
41
+ asyncio_mode = "auto"
42
+
43
+ [build-system]
44
+ requires = ["uv_build>=0.8.3,<0.9.0"]
45
+ build-backend = "uv_build"
@@ -0,0 +1,33 @@
1
+ from buganize.api.client import Buganize
2
+ from buganize.api.models import (
3
+ CUSTOM_FIELD_IDS,
4
+ Comment,
5
+ CustomFieldValue,
6
+ FieldChange,
7
+ Issue,
8
+ IssueType,
9
+ IssueUpdate,
10
+ IssueUpdatesResult,
11
+ Priority,
12
+ SearchResult,
13
+ Status,
14
+ )
15
+ from buganize.cli.output import EXTRA_FIELDS, export, to_dataframe
16
+
17
+ __all__ = [
18
+ "CUSTOM_FIELD_IDS",
19
+ "Buganize",
20
+ "Comment",
21
+ "CustomFieldValue",
22
+ "EXTRA_FIELDS",
23
+ "FieldChange",
24
+ "Issue",
25
+ "IssueType",
26
+ "IssueUpdate",
27
+ "IssueUpdatesResult",
28
+ "Priority",
29
+ "SearchResult",
30
+ "Status",
31
+ "export",
32
+ "to_dataframe",
33
+ ]
@@ -0,0 +1,3 @@
1
+ from buganize.cli.main import start
2
+
3
+ start()
@@ -0,0 +1,3 @@
1
+ from . import client, models, parser
2
+
3
+ __all__ = ["client", "models", "parser"]