Import python-pygerrit2_2.0.4.orig.tar.gz
Filip Pytloun
6 years ago
0 | Alexander D. Kanevskiy <kad@kad.name> | |
1 | Andrey Devyatkin <andrey.a.devyatkin@gmail.com> | |
2 | Benjamin Foster <bfoster@phi.al> | |
3 | Chad Horohoe <chadh@wikimedia.org> | |
4 | Chris Packham <chris.packham@alliedtelesis.co.nz> | |
5 | Christopher Zee <christopher.xs.zee@gmail.com> | |
6 | David Pursehouse <david.pursehouse@gmail.com> | |
7 | David Pursehouse <david.pursehouse@sonyericsson.com> | |
8 | David Pursehouse <david.pursehouse@sonymobile.com> | |
9 | Ernst Sjostrand <ernst.sjostrand@sonymobile.com> | |
10 | Fabien Boucher <fabien.boucher@enovance.com> | |
11 | Gabriel Féron <feron.gabriel@gmail.com> | |
12 | George Peristerakis <gperiste@redhat.com> | |
13 | Jens Andersen <jens.andersen@gmail.com> | |
14 | Johannes Richter <johannes.richter@kernkonzept.com> | |
15 | Justin Simon <jls5177@gmail.com> | |
16 | Marcin Płonka <mplonka@gmail.com> | |
17 | Nikki Heald <nicky@notnowlewis.com> | |
18 | Peter Theckanath <peter.xa.theckanath@sonymobile.com> | |
19 | Vasiliy Kleschov <vkleschov@cloudlinux.com> | |
20 | Vineet Naik <vineet@helpshift.com> | |
21 | dependabot[bot] <support@dependabot.com> |
0 | CHANGES | |
1 | ======= | |
2 | ||
3 | 2.0.4 | |
4 | ----- | |
5 | ||
6 | * Add missing docstrings to silence pydocstyle errors | |
7 | * Replace pep257 with pydocstyle | |
8 | * Fix up various pydocstyle warnings, mostly about spacing | |
9 | * pylint: Remove useless subclassing/super() calls | |
10 | * pylint: Don't iterate over range(len()), use enumerate() | |
11 | * Minor pylint fixes, mostly spacing, indentation, capitalization, naming | |
12 | * Bump pyflakes from 1.5.0 to 1.6.0 | |
13 | * Bump flake8 from 3.3.0 to 3.5.0 | |
14 | * Exclude docs from analysis | |
15 | * Remove doc build; add generated docs folder | |
16 | ||
17 | 2.0.3 | |
18 | ----- | |
19 | ||
20 | * Allow requests up to version 2.18.4 | |
21 | * Fixes #11 Remove pypi downloads shields | |
22 | * Allow to use requests version 2.18.1 | |
23 | * Fixes #10: Fix a bug preventing file upload in PUT and POST | |
24 | * Fixes #7: Be less strict about requests version | |
25 | * Upgrade requests to 2.16.0 | |
26 | * Adapt to removal of Digest authentication in Gerrit 2.14 | |
27 | * Update links to Gerrit 2.14 | |
28 | * Upgrade requests to 2.14.2 | |
29 | * Add support to return response code | |
30 | * Fix docs folder exclusion in tox config | |
31 | * Upgrade pyflakes and flake8 to latest versions | |
32 | * Remove debug logs | |
33 | * rest: use encoding from response instead of UTF-8 | |
34 | * Upgrade requests to 2.13.0 | |
35 | * Add back the ReStructured Text version of the README | |
36 | ||
37 | 2.0.2 | |
38 | ----- | |
39 | ||
40 | * Fix MANIFEST.in with correct README file name | |
41 | * Upgrade flake8 to version 3.2.1 | |
42 | * Upgrade requests to 2.12.1 | |
43 | * Update Gerrit documentation link to latest major release | |
44 | * Upgrade flake8 and pyflakes to latest versions | |
45 | * Fix README reference in setup.cfg | |
46 | * Upgrade requests to 2.11.1 | |
47 | * Convert README to markdown | |
48 | ||
49 | 2.0.1 | |
50 | ----- | |
51 | ||
52 | * Upgrade requests to version 2.11.0 | |
53 | * Revert "Don't use requests.session()" | |
54 | * Upgrade requests to 2.10.0 | |
55 | * returning raw response for non-JSON Content-Type | |
56 | * making use of requests' json argument | |
57 | * merging request headers | |
58 | * Remove redundant pylint suppressions | |
59 | * Rewrite the README with better usage instructions | |
60 | * Update prerequisites | |
61 | * Add tox configuration | |
62 | * Use flake8 instead of pep8 | |
63 | * GerritReview: dump json content with sorted keys | |
64 | * Example: Don't use 'self' in query when not authenticated | |
65 | * Python3 compatibility fixes | |
66 | ||
67 | 2.0.0 | |
68 | ----- | |
69 | ||
70 | * Remove SSH support and rename to pygerrit2 | |
71 | * Update Gerrit documentation link to latest version | |
72 | * Update link to Gerrit home page | |
73 | ||
74 | 1.0.0 | |
75 | ----- | |
76 | ||
77 | * Use badges from shields.io | |
78 | * Upgrade pyflakes to 1.0.0 | |
79 | * Upgrade PEP-8 to 1.7.0 | |
80 | * Include event name in UnhandledEvent's \_\_repr\_\_ output | |
81 | * Upgrade Paramiko to 1.16.0 | |
82 | * Upgrade requests to 2.9.1 | |
83 | * Raise exception if the json content is invalid | |
84 | * Add missing docstring for Change#from\_json | |
85 | * Added from\_json method for Change model | |
86 | * Updated add\_comment() to support line ranges | |
87 | ||
88 | 0.2.11 | |
89 | ------ | |
90 | ||
91 | * Upgrade paramiko to version 1.15.2 | |
92 | * Add keepalive support to GerritSSHClient | |
93 | ||
94 | 0.2.10 | |
95 | ------ | |
96 | ||
97 | * Don't use requests.session() | |
98 | * Don't set Content-Type on PUT/POST if there is no body | |
99 | * Add debug log of response content | |
100 | ||
101 | 0.2.9 | |
102 | ----- | |
103 | ||
104 | * Upgrade requests to 2.7.0 | |
105 | * Allow unicode characters in gerrit commands | |
106 | * Update requests to 2.5.1 | |
107 | * Fix incorrect docstring | |
108 | * Upgrade pyflakes to 0.8.1 | |
109 | * Be less strict about the pbr version used | |
110 | * Restructure docstrings for better html output | |
111 | * Bump pep8 to 1.5.7 | |
112 | * Bump paramiko to 1.15.1 and pycrypto to 2.6.1 | |
113 | * Bump requests to 2.4.3 | |
114 | * Fix documentation build | |
115 | * Bump python requests version to 2.4.1 | |
116 | ||
117 | 0.2.8 | |
118 | ----- | |
119 | ||
120 | * Add inline review support | |
121 | * Replace deprecated assertEquals() with assertEqual() | |
122 | * Update PEP257 to 0.2.4 | |
123 | * Update pyflakes to 0.7.3 | |
124 | ||
125 | 0.2.7 | |
126 | ----- | |
127 | ||
128 | * Add message formatter class | |
129 | * Upgrade requests to 2.3.0 | |
130 | * Fix paramiko link in the README file | |
131 | ||
132 | 0.2.6 | |
133 | ----- | |
134 | ||
135 | * Add support for ProxyCommand in GerritSSHClient | |
136 | * Upgrade paramiko to 1.14.0 | |
137 | * Revert "Remove support for Gerrit over ssh" | |
138 | * Update the README | |
139 | * Upgdade requests to 2.2.1 | |
140 | * Include full description in the setup config | |
141 | * Set Content-Type header on PUT and POST requests | |
142 | * Set HTTP headers to reduce data transfer time | |
143 | * Migrate setup to pbr | |
144 | * Stop using pylint | |
145 | * Remove support for Gerrit over ssh | |
146 | ||
147 | 0.2.5 | |
148 | ----- | |
149 | ||
150 | * Version 0.2.5 | |
151 | * Fix up usage of argparse in examples | |
152 | * Update examples to use argparse instead of optparse | |
153 | * Fix for hanging connections | |
154 | ||
155 | 0.2.4 | |
156 | ----- | |
157 | ||
158 | * Version 0.2.4 | |
159 | * Accept kwargs for request functions | |
160 | * Correct Gerrit documentation link | |
161 | * Add badges to the README | |
162 | * Fix README markup to work properly on Gitlab | |
163 | ||
164 | 0.2.3 | |
165 | ----- | |
166 | ||
167 | * Version 0.2.3 | |
168 | * Use find\_packages() in setup | |
169 | ||
170 | 0.2.2 | |
171 | ----- | |
172 | ||
173 | * Bump version to 0.2.2 | |
174 | * Release notes for 0.2.2 | |
175 | * Add user in the Approval object | |
176 | * Add support for CurrentPatchSet | |
177 | * Include change status in Change object | |
178 | * Always set sortkey in Change object | |
179 | * Handle errors in REST API example | |
180 | * Simplify REST API error handling | |
181 | * Add username field in the account data | |
182 | * Add method to run a gerrit command from the client | |
183 | * Add method to get username and Gerrit version | |
184 | * Fix NameError in REST API example | |
185 | * Bump to requests 2.0.1 also in setup.py | |
186 | * Bump requests version up to 2.0.1 | |
187 | ||
188 | 0.2.1 | |
189 | ----- | |
190 | ||
191 | * Update change log for 0.2.1 | |
192 | * Document the HTTP password configuration for REST API usage | |
193 | * Reword the prerequisites in the readme | |
194 | * Separate prerequisites and configuration in the readme | |
195 | ||
196 | 0.2.0 | |
197 | ----- | |
198 | ||
199 | * Bump version to 0.2.0 | |
200 | * Update change log for 0.2.0 | |
201 | * Suppress pylint "Unable to import" error | |
202 | * Suppress pylint warning about catching too general exception | |
203 | * Fix pylint error in rest\_example.py | |
204 | * Change abandoned and restored events don't have a patchset field | |
205 | * Add myself to authors | |
206 | * Add an authors list that contributors can add themselves to | |
207 | * Fix indentation | |
208 | * Ensure errors in json parsing doesn't leave everything in a broken state | |
209 | * Reason is optional in abandon and restore changes | |
210 | * Return sortKey from query result, to allow resuming query | |
211 | * Update the README to mention the REST API | |
212 | * Avoid busy loop when receiving incoming stream data | |
213 | * Add more detailed examples of SSH interface usage in the README | |
214 | * Update the README notes about ssh configuration | |
215 | * Add support for Kerberos authentication in example script | |
216 | * Allow client to disable SSL certificate verification | |
217 | * Add REST API example | |
218 | * Refactor the authentication handling | |
219 | * Add MANIFEST to .gitignore | |
220 | * Add method to build url from endpoint | |
221 | ||
222 | 0.1.1 | |
223 | ----- | |
224 | ||
225 | * Bump version to 0.1.1 | |
226 | * Add changelog | |
227 | * Make get, put, post and delete REST methods public | |
228 | * Fix #10: Allow to manually specify ssh username and port | |
229 | * Completely refactor the stream event handling | |
230 | * Add missing \_\_repr\_\_ methods on ErrorEvent and UnhandledEvent | |
231 | * Fix initialisation of error event | |
232 | * Fix #11: correct handling of \`identityfile\` in the ssh config | |
233 | * Allow example script to continue if errors are received | |
234 | * Fix #9: Add a bit more detail in the documentation | |
235 | * Fix #8: Support the "topic-changed" stream event | |
236 | * Fix #7: Support the "reviewer-added" stream event | |
237 | * Fix #6: Support the "merge-failed" stream event | |
238 | * Fix #5: Move json parsing and error handling into the event factory | |
239 | * Improved logging in the example script | |
240 | * Fix #3: Don't treat unhandled event types as errors | |
241 | * Fix #4: Keep event's raw json data in the event object | |
242 | * Add \_\_repr\_\_ methods on event and model classes | |
243 | * Remove redundant \`exec\_command\` method | |
244 | * Fix #2: Establish SSH connection in a thread-safe way | |
245 | * Fix #1: Use select.select() instead of select.poll() | |
246 | * Fix authentication setup | |
247 | * Add handling of HTTP error status codes in responses | |
248 | * Add support for HTTP digest authentication | |
249 | * Initial implementation of Gerrit REST API interface | |
250 | * Disable W0142 'Used \* or \*\* magic' in the Pylint configuration | |
251 | ||
252 | 0.1.0 | |
253 | ----- | |
254 | ||
255 | * Bump version to 0.1.0 | |
256 | * Add Python trove classifiers to the setup | |
257 | * Include full license text in the license parameter of setup | |
258 | * Add long description in the setup | |
259 | * Add MANIFEST.in file | |
260 | * Improve formatting in the README file | |
261 | * Rename README to README.rst | |
262 | * Add make target and rules to ensure environment setup tools are OK | |
263 | * Add a make target to build the API documentation zip archive | |
264 | * Don't put copyright and license headers in Python module docstrings | |
265 | * Add a make target to build the source distribution | |
266 | * Add a make target to check for clean git status | |
267 | * Add a make target to check that the git is tagged properly | |
268 | * Add generation of package documentation | |
269 | * Separate test dependency installation into its own build target | |
270 | * Separate environment intialisation into its own build target | |
271 | * Make the shebangs consistent | |
272 | * Add pylint check in the Makefile | |
273 | * Fix pylint warnings | |
274 | * Add PEP-257 conformance check in the Makefile | |
275 | * Add a unit test to ensure dependency package version consistency | |
276 | * Separate package dependencies from verification/test dependencies | |
277 | * Bump paramiko dependency to version 1.11.0 | |
278 | * Fix relative imports | |
279 | * Fix one more PEP-257 violation | |
280 | * Add a \`clean\` target in the Makefile | |
281 | * Add PEP-8 conformance check in the Makefile | |
282 | * Add pyflakes check in the Makefile | |
283 | * Add a Makefile to install dependencies and run unit tests | |
284 | * Only allow strings as query terms and commands | |
285 | * Don't hard code version in setup.py | |
286 | * Include command in GerritCommandResult | |
287 | * Don't open ssh connection until needed | |
288 | * Fix UnboundLocalError when stream-event connection fails | |
289 | * Add missing docstrings to conform to PEP-257 | |
290 | * Explicitly depend on paramiko v1.7.6 | |
291 | * Move requirements.txt to the root | |
292 | * Add requirements file | |
293 | ||
294 | 0.0.7 | |
295 | ----- | |
296 | ||
297 | * Bump to version 0.0.7 | |
298 | * Better error message in example script | |
299 | * Rename the readme and license files | |
300 | ||
301 | 0.0.6 | |
302 | ----- | |
303 | ||
304 | * Bump to version 0.0.6 | |
305 | * Better error handling in the example script | |
306 | * Move license to its own file and add basic documentation | |
307 | * Use correct URL in setup information | |
308 | * Handle socket connection error in SSH client | |
309 | * Python 2.6 style exception handling | |
310 | * Add Eclipse and Pydev project files | |
311 | ||
312 | 0.0.5 | |
313 | ----- | |
314 | ||
315 | * Bump to version 0.0.5 | |
316 | * Revert "Basic thread-safeness in the SSH client" | |
317 | * Encapsulate the SSH command results in a class | |
318 | * Update pylint config to work with version 0.26 | |
319 | * Only add query result lines to returned data | |
320 | * Handle JSON errors in query results | |
321 | * Refactor getting the Gerrit version | |
322 | * Reduce nested \`if\` blocks in stream handling | |
323 | * Add pylint configuration file | |
324 | * More error handling improvements in stream | |
325 | * Handle exception when running ssh command | |
326 | * Fix pylint warnings in stream.py | |
327 | * Basic thread-safeness in the SSH client | |
328 | * More exception handling in stream | |
329 | ||
330 | 0.0.4 | |
331 | ----- | |
332 | ||
333 | * Bump version to 0.0.4 | |
334 | * Fix hostname in unit tests | |
335 | * Get Gerrit version during client initialisation | |
336 | * Add query functionality | |
337 | * Add \_\_str\_\_ on event base class | |
338 | * Move SSH client from stream class to main client class | |
339 | * Add license information | |
340 | * Simplify the unit test structure | |
341 | ||
342 | 0.0.3 | |
343 | ----- | |
344 | ||
345 | * Bump version to 0.0.3 | |
346 | * Add an example of how the Gerrit client class is used | |
347 | * Wait for thread to complete when stopping stream | |
348 | * Handle errors when reading event stream | |
349 | * Fix event registration from other module | |
350 | * Refactor event stream handling to use SSH client | |
351 | * Handle invalid port in ssh config | |
352 | * Add GerritSSHClient | |
353 | * Add initial stub of GerritClient class | |
354 | * Inject event name into event classes with decorator | |
355 | * Revert "GerritEventFactory should be a singleton" | |
356 | * GerritEventFactory should be a singleton | |
357 | * Pass input stream in the constructor of GerritStream | |
358 | * Add support for topic name in change attribute | |
359 | * Add event factory and refactor event dispatching | |
360 | * Events unit tests should also test event dispatching | |
361 | * Add helper methods for initialisation from json data | |
362 | * Add setup.py | |
363 | ||
364 | 0.0.2 | |
365 | ----- | |
366 | ||
367 | * Tidy up docstrings to follow the PEP-257 docstring convention | |
368 | * Refactor into submodules | |
369 | * Add .settings to .gitignore | |
370 | * Remove check for supported approval types | |
371 | ||
372 | 0.0.1 | |
373 | ----- | |
374 | ||
375 | * Add .gitignore | |
376 | * Add Makefile and script for unit tests | |
377 | * Add unit tests for event handling | |
378 | * Reason is missing in ChangeRestoredEvent | |
379 | * \`comment-added\` event can have multiple approvals | |
380 | * Add support for the \`draft-published\` event | |
381 | * Minor refactoring of unit tests | |
382 | * Fixing PEP-8 and pylint warnings | |
383 | * Python cleanup: inherit from object | |
384 | * Undefined variable in GerritCommentAddedEvent | |
385 | * Check for callable event handler on attach | |
386 | * Minor refactoring and adding initial unit tests | |
387 | * Initial version of class for handling Gerrit stream events | |
388 | * Initial empty commit |
0 | The MIT License | |
1 | ||
2 | Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. | |
3 | Copyright 2012 Sony Mobile Communications. All rights reserved. | |
4 | ||
5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | of this software and associated documentation files (the "Software"), to deal | |
7 | in the Software without restriction, including without limitation the rights | |
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | copies of the Software, and to permit persons to whom the Software is | |
10 | furnished to do so, subject to the following conditions: | |
11 | ||
12 | The above copyright notice and this permission notice shall be included in | |
13 | all copies or substantial portions of the Software. | |
14 | ||
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | THE SOFTWARE. |
0 | include README.rst LICENSE requirements.txt⏎ |
0 | # The MIT License | |
1 | # | |
2 | # Copyright 2013 Sony Mobile Communications. All rights reserved. | |
3 | # | |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | # of this software and associated documentation files (the "Software"), to deal | |
6 | # in the Software without restriction, including without limitation the rights | |
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | # copies of the Software, and to permit persons to whom the Software is | |
9 | # furnished to do so, subject to the following conditions: | |
10 | # | |
11 | # The above copyright notice and this permission notice shall be included in | |
12 | # all copies or substantial portions of the Software. | |
13 | # | |
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | # THE SOFTWARE. | |
21 | ||
22 | PWD := $(shell pwd) | |
23 | VERSION := $(shell git describe) | |
24 | ||
25 | VIRTUALENV := $(shell which virtualenv) | |
26 | ifeq ($(wildcard $(VIRTUALENV)),) | |
27 | $(error virtualenv must be available) | |
28 | endif | |
29 | ||
30 | PIP := $(shell which pip) | |
31 | ifeq ($(wildcard $(PIP)),) | |
32 | $(error pip must be available) | |
33 | endif | |
34 | ||
35 | REQUIRED_VIRTUALENV ?= 1.10 | |
36 | VIRTUALENV_OK := $(shell expr `virtualenv --version | \ | |
37 | cut -f2 -d' '` \>= $(REQUIRED_VIRTUALENV)) | |
38 | ||
39 | all: test | |
40 | ||
41 | test: clean unittests pyflakes pep8 pydocstyle | |
42 | ||
43 | sdist: valid-virtualenv test | |
44 | bash -c "\ | |
45 | source ./pygerrit2env/bin/activate && \ | |
46 | python setup.py sdist" | |
47 | ||
48 | valid-virtualenv: | |
49 | ifeq ($(VIRTUALENV_OK),0) | |
50 | $(error virtualenv version $(REQUIRED_VIRTUALENV) or higher is needed) | |
51 | endif | |
52 | ||
53 | pydocstyle: testenvsetup | |
54 | bash -c "\ | |
55 | source ./pygerrit2env/bin/activate && \ | |
56 | git ls-files | grep \"\.py$$\" | grep -v docs | xargs pydocstyle" | |
57 | ||
58 | pep8: testenvsetup | |
59 | bash -c "\ | |
60 | source ./pygerrit2env/bin/activate && \ | |
61 | git ls-files | grep \"\.py$$\" | grep -v docs | xargs flake8 --max-line-length 80" | |
62 | ||
63 | pyflakes: testenvsetup | |
64 | bash -c "\ | |
65 | source ./pygerrit2env/bin/activate && \ | |
66 | git ls-files | grep \"\.py$$\" | grep -v docs | xargs pyflakes" | |
67 | ||
68 | unittests: testenvsetup | |
69 | bash -c "\ | |
70 | source ./pygerrit2env/bin/activate && \ | |
71 | python unittests.py" | |
72 | ||
73 | testenvsetup: envsetup | |
74 | bash -c "\ | |
75 | source ./pygerrit2env/bin/activate && \ | |
76 | pip install --upgrade -r test_requirements.txt" | |
77 | ||
78 | docenvsetup: envsetup | |
79 | bash -c "\ | |
80 | source ./pygerrit2env/bin/activate && \ | |
81 | pip install --upgrade -r doc_requirements.txt" | |
82 | ||
83 | envsetup: envinit | |
84 | bash -c "\ | |
85 | source ./pygerrit2env/bin/activate && \ | |
86 | pip install --upgrade -r requirements.txt" | |
87 | ||
88 | envinit: | |
89 | bash -c "[ -e ./pygerrit2env/bin/activate ] || virtualenv --system-site-packages ./pygerrit2env" | |
90 | ||
91 | clean: | |
92 | @find . -type f -name "*.pyc" -exec rm -f {} \; | |
93 | @rm -rf pygerrit2env pygerrit2.egg-info build dist |
0 | Metadata-Version: 1.1 | |
1 | Name: pygerrit2 | |
2 | Version: 2.0.4 | |
3 | Summary: Client library for interacting with Gerrit's REST API | |
4 | Home-page: https://github.com/dpursehouse/pygerrit2 | |
5 | Author: David Pursehouse | |
6 | Author-email: david.pursehouse@gmail.com | |
7 | License: The MIT License | |
8 | Description-Content-Type: UNKNOWN | |
9 | Description: Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API | |
10 | ============================================================================= | |
11 | ||
12 | .. image:: https://img.shields.io/pypi/v/pygerrit2.png | |
13 | ||
14 | .. image:: https://img.shields.io/pypi/l/pygerrit2.png | |
15 | ||
16 | Pygerrit2 provides a simple interface for clients to interact with | |
17 | `Gerrit Code Review`_ via the REST API. | |
18 | ||
19 | Prerequisites | |
20 | ------------- | |
21 | ||
22 | Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 | |
23 | is experimental. | |
24 | ||
25 | Pygerrit2 depends on the `requests`_ library. | |
26 | ||
27 | ||
28 | Installation | |
29 | ------------ | |
30 | ||
31 | To install pygerrit2, simply:: | |
32 | ||
33 | $ pip install pygerrit2 | |
34 | ||
35 | ||
36 | Usage | |
37 | ----- | |
38 | ||
39 | This simple example shows how to get the user's open changes. Authentication | |
40 | to Gerrit is done via HTTP Basic authentication, using an explicitly given | |
41 | username and password:: | |
42 | ||
43 | >>> from requests.auth import HTTPBasicAuth | |
44 | >>> from pygerrit2.rest import GerritRestAPI | |
45 | >>> auth = HTTPBasicAuth('username', 'password') | |
46 | >>> rest = GerritRestAPI(url='http://review.example.net', auth=auth) | |
47 | >>> changes = rest.get("/changes/?q=owner:self%20status:open") | |
48 | ||
49 | Note that is is not necessary to add the ``/a/`` prefix on the endpoint | |
50 | URLs. This is automatically added when the API is instantiated with an | |
51 | authentication object. | |
52 | ||
53 | If the user's HTTP username and password are defined in the ``.netrc`` | |
54 | file:: | |
55 | ||
56 | machine review.example.net login MyUsername password MyPassword | |
57 | ||
58 | then it is possible to authenticate with those credentials:: | |
59 | ||
60 | >>> from pygerrit2.rest import GerritRestAPI | |
61 | >>> from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc | |
62 | >>> url = 'http://review.example.net' | |
63 | >>> auth = HTTPBasicAuthFromNetrc(url=url) | |
64 | >>> rest = GerritRestAPI(url=url, auth=auth) | |
65 | >>> changes = rest.get("/changes/?q=owner:self%20status:open") | |
66 | ||
67 | Note that the HTTP password is not the same as the SSH password. For | |
68 | instructions on how to obtain the HTTP password, refer to Gerrit's | |
69 | `HTTP upload settings`_ documentation. | |
70 | ||
71 | Also note that in Gerrit version 2.14, support for HTTP Digest authentication | |
72 | was removed and only HTTP Basic authentication is supported. When using | |
73 | pygerrit2 against an earlier Gerrit version, it may be necessary to replace | |
74 | the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. | |
75 | ||
76 | Refer to the `example`_ script for a full working example. | |
77 | ||
78 | Copyright and License | |
79 | --------------------- | |
80 | ||
81 | Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. | |
82 | ||
83 | Copyright 2012 Sony Mobile Communications. All rights reserved. | |
84 | ||
85 | Copyright 2016 David Pursehouse. All rights reserved. | |
86 | ||
87 | Licensed under The MIT License. Please refer to the `LICENSE`_ file for full | |
88 | license details. | |
89 | ||
90 | .. _`Gerrit Code Review`: https://gerritcodereview.com/ | |
91 | .. _`requests`: https://github.com/kennethreitz/requests | |
92 | .. _example: https://github.com/dpursehouse/pygerrit2/blob/master/example.py | |
93 | .. _`HTTP upload settings`: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http | |
94 | .. _LICENSE: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE | |
95 | ||
96 | ||
97 | Keywords: gerrit | |
98 | rest | |
99 | http | |
100 | Platform: UNKNOWN | |
101 | Classifier: Development Status :: 3 - Alpha | |
102 | Classifier: Environment :: Console | |
103 | Classifier: Intended Audience :: Developers | |
104 | Classifier: License :: OSI Approved :: MIT License | |
105 | Classifier: Natural Language :: English | |
106 | Classifier: Programming Language :: Python | |
107 | Classifier: Programming Language :: Python :: 2.6 | |
108 | Classifier: Programming Language :: Python :: 2.7 |
0 | # Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API | |
1 | ||
2 | ![Version](https://img.shields.io/pypi/v/pygerrit2.png) | |
3 | ![License](https://img.shields.io/pypi/l/pygerrit2.png) | |
4 | ||
5 | Pygerrit2 provides a simple interface for clients to interact with | |
6 | [Gerrit Code Review][gerrit] via the REST API. | |
7 | ||
8 | ## Prerequisites | |
9 | ||
10 | Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 | |
11 | is experimental. | |
12 | ||
13 | Pygerrit2 depends on the [requests library][requests]. | |
14 | ||
15 | ||
16 | ## Installation | |
17 | ||
18 | To install pygerrit2, simply: | |
19 | ||
20 | ```bash | |
21 | $ pip install pygerrit2 | |
22 | ``` | |
23 | ||
24 | ## Usage | |
25 | ||
26 | This simple example shows how to get the user's open changes. Authentication | |
27 | to Gerrit is done via HTTP Basic authentication, using an explicitly given | |
28 | username and password: | |
29 | ||
30 | ```python | |
31 | from requests.auth import HTTPBasicAuth | |
32 | from pygerrit2.rest import GerritRestAPI | |
33 | ||
34 | auth = HTTPBasicAuth('username', 'password') | |
35 | rest = GerritRestAPI(url='http://review.example.net', auth=auth) | |
36 | changes = rest.get("/changes/?q=owner:self%20status:open") | |
37 | ``` | |
38 | ||
39 | Note that is is not necessary to add the `/a/` prefix on the endpoint | |
40 | URLs. This is automatically added when the API is instantiated with an | |
41 | authentication object. | |
42 | ||
43 | If the user's HTTP username and password are defined in the `.netrc` | |
44 | file: | |
45 | ||
46 | ```bash | |
47 | machine review.example.net login MyUsername password MyPassword | |
48 | ``` | |
49 | ||
50 | then it is possible to authenticate with those credentials: | |
51 | ||
52 | ```python | |
53 | from pygerrit2.rest import GerritRestAPI | |
54 | from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc | |
55 | ||
56 | url = 'http://review.example.net' | |
57 | auth = HTTPBasicAuthFromNetrc(url=url) | |
58 | rest = GerritRestAPI(url=url, auth=auth) | |
59 | changes = rest.get("/changes/?q=owner:self%20status:open") | |
60 | ``` | |
61 | ||
62 | Note that the HTTP password is not the same as the SSH password. For | |
63 | instructions on how to obtain the HTTP password, refer to Gerrit's | |
64 | [HTTP upload settings documentation][settings]. | |
65 | ||
66 | Also note that in Gerrit version 2.14, support for HTTP Digest authentication | |
67 | was removed and only HTTP Basic authentication is supported. When using | |
68 | pygerrit2 against an earlier Gerrit version, it may be necessary to replace | |
69 | the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. | |
70 | ||
71 | Refer to the [example script][example] for a full working example. | |
72 | ||
73 | ||
74 | # Copyright and License | |
75 | ||
76 | Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. | |
77 | ||
78 | Copyright 2012 Sony Mobile Communications. All rights reserved. | |
79 | ||
80 | Copyright 2016 David Pursehouse. All rights reserved. | |
81 | ||
82 | Licensed under The MIT License. Please refer to the [LICENSE file][license] | |
83 | for full license details. | |
84 | ||
85 | [gerrit]: https://gerritcodereview.com/ | |
86 | [requests]: https://github.com/kennethreitz/requests | |
87 | [example]: https://github.com/dpursehouse/pygerrit2/blob/master/example.py | |
88 | [settings]: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http | |
89 | [license]: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE |
0 | Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API | |
1 | ============================================================================= | |
2 | ||
3 | .. image:: https://img.shields.io/pypi/v/pygerrit2.png | |
4 | ||
5 | .. image:: https://img.shields.io/pypi/l/pygerrit2.png | |
6 | ||
7 | Pygerrit2 provides a simple interface for clients to interact with | |
8 | `Gerrit Code Review`_ via the REST API. | |
9 | ||
10 | Prerequisites | |
11 | ------------- | |
12 | ||
13 | Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 | |
14 | is experimental. | |
15 | ||
16 | Pygerrit2 depends on the `requests`_ library. | |
17 | ||
18 | ||
19 | Installation | |
20 | ------------ | |
21 | ||
22 | To install pygerrit2, simply:: | |
23 | ||
24 | $ pip install pygerrit2 | |
25 | ||
26 | ||
27 | Usage | |
28 | ----- | |
29 | ||
30 | This simple example shows how to get the user's open changes. Authentication | |
31 | to Gerrit is done via HTTP Basic authentication, using an explicitly given | |
32 | username and password:: | |
33 | ||
34 | >>> from requests.auth import HTTPBasicAuth | |
35 | >>> from pygerrit2.rest import GerritRestAPI | |
36 | >>> auth = HTTPBasicAuth('username', 'password') | |
37 | >>> rest = GerritRestAPI(url='http://review.example.net', auth=auth) | |
38 | >>> changes = rest.get("/changes/?q=owner:self%20status:open") | |
39 | ||
40 | Note that is is not necessary to add the ``/a/`` prefix on the endpoint | |
41 | URLs. This is automatically added when the API is instantiated with an | |
42 | authentication object. | |
43 | ||
44 | If the user's HTTP username and password are defined in the ``.netrc`` | |
45 | file:: | |
46 | ||
47 | machine review.example.net login MyUsername password MyPassword | |
48 | ||
49 | then it is possible to authenticate with those credentials:: | |
50 | ||
51 | >>> from pygerrit2.rest import GerritRestAPI | |
52 | >>> from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc | |
53 | >>> url = 'http://review.example.net' | |
54 | >>> auth = HTTPBasicAuthFromNetrc(url=url) | |
55 | >>> rest = GerritRestAPI(url=url, auth=auth) | |
56 | >>> changes = rest.get("/changes/?q=owner:self%20status:open") | |
57 | ||
58 | Note that the HTTP password is not the same as the SSH password. For | |
59 | instructions on how to obtain the HTTP password, refer to Gerrit's | |
60 | `HTTP upload settings`_ documentation. | |
61 | ||
62 | Also note that in Gerrit version 2.14, support for HTTP Digest authentication | |
63 | was removed and only HTTP Basic authentication is supported. When using | |
64 | pygerrit2 against an earlier Gerrit version, it may be necessary to replace | |
65 | the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. | |
66 | ||
67 | Refer to the `example`_ script for a full working example. | |
68 | ||
69 | Copyright and License | |
70 | --------------------- | |
71 | ||
72 | Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. | |
73 | ||
74 | Copyright 2012 Sony Mobile Communications. All rights reserved. | |
75 | ||
76 | Copyright 2016 David Pursehouse. All rights reserved. | |
77 | ||
78 | Licensed under The MIT License. Please refer to the `LICENSE`_ file for full | |
79 | license details. | |
80 | ||
81 | .. _`Gerrit Code Review`: https://gerritcodereview.com/ | |
82 | .. _`requests`: https://github.com/kennethreitz/requests | |
83 | .. _example: https://github.com/dpursehouse/pygerrit2/blob/master/example.py | |
84 | .. _`HTTP upload settings`: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http | |
85 | .. _LICENSE: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE |
0 | # Minimal makefile for Sphinx documentation | |
1 | # | |
2 | ||
3 | # You can set these variables from the command line. | |
4 | SPHINXOPTS = | |
5 | SPHINXBUILD = python -msphinx | |
6 | SPHINXPROJ = pygerrit2 | |
7 | SOURCEDIR = . | |
8 | BUILDDIR = _build | |
9 | ||
10 | # Put it first so that "make" without argument is like "make help". | |
11 | help: | |
12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | |
13 | ||
14 | .PHONY: help Makefile | |
15 | ||
16 | # Catch-all target: route all unknown targets to Sphinx using the new | |
17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). | |
18 | %: Makefile | |
19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)⏎ |
0 | # -*- coding: utf-8 -*- | |
1 | # | |
2 | # pygerrit2 documentation build configuration file, created by | |
3 | # sphinx-quickstart on Sun Oct 22 17:32:32 2017. | |
4 | # | |
5 | # This file is execfile()d with the current directory set to its | |
6 | # containing dir. | |
7 | # | |
8 | # Note that not all possible configuration values are present in this | |
9 | # autogenerated file. | |
10 | # | |
11 | # All configuration values have a default; values that are commented out | |
12 | # serve to show the default. | |
13 | ||
14 | # If extensions (or modules to document with autodoc) are in another directory, | |
15 | # add these directories to sys.path here. If the directory is relative to the | |
16 | # documentation root, use os.path.abspath to make it absolute, like shown here. | |
17 | # | |
18 | # import os | |
19 | # import sys | |
20 | # sys.path.insert(0, os.path.abspath('.')) | |
21 | ||
22 | ||
23 | # -- General configuration ------------------------------------------------ | |
24 | ||
25 | # If your documentation needs a minimal Sphinx version, state it here. | |
26 | # | |
27 | # needs_sphinx = '1.0' | |
28 | ||
29 | # Add any Sphinx extension module names here, as strings. They can be | |
30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom | |
31 | # ones. | |
32 | extensions = ['sphinx.ext.autodoc'] | |
33 | ||
34 | # Add any paths that contain templates here, relative to this directory. | |
35 | templates_path = ['_templates'] | |
36 | ||
37 | # The suffix(es) of source filenames. | |
38 | # You can specify multiple suffix as a list of string: | |
39 | # | |
40 | # source_suffix = ['.rst', '.md'] | |
41 | source_suffix = '.rst' | |
42 | ||
43 | # The master toctree document. | |
44 | master_doc = 'index' | |
45 | ||
46 | # General information about the project. | |
47 | project = u'pygerrit2' | |
48 | copyright = u'2017, David Pursehouse' | |
49 | author = u'David Pursehouse' | |
50 | ||
51 | # The version info for the project you're documenting, acts as replacement for | |
52 | # |version| and |release|, also used in various other places throughout the | |
53 | # built documents. | |
54 | # | |
55 | # The short X.Y version. | |
56 | version = u'' | |
57 | # The full version, including alpha/beta/rc tags. | |
58 | release = u'' | |
59 | ||
60 | # The language for content autogenerated by Sphinx. Refer to documentation | |
61 | # for a list of supported languages. | |
62 | # | |
63 | # This is also used if you do content translation via gettext catalogs. | |
64 | # Usually you set "language" from the command line for these cases. | |
65 | language = None | |
66 | ||
67 | # List of patterns, relative to source directory, that match files and | |
68 | # directories to ignore when looking for source files. | |
69 | # This patterns also effect to html_static_path and html_extra_path | |
70 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] | |
71 | ||
72 | # The name of the Pygments (syntax highlighting) style to use. | |
73 | pygments_style = 'sphinx' | |
74 | ||
75 | # If true, `todo` and `todoList` produce output, else they produce nothing. | |
76 | todo_include_todos = False | |
77 | ||
78 | ||
79 | # -- Options for HTML output ---------------------------------------------- | |
80 | ||
81 | # The theme to use for HTML and HTML Help pages. See the documentation for | |
82 | # a list of builtin themes. | |
83 | # | |
84 | html_theme = 'alabaster' | |
85 | ||
86 | # Theme options are theme-specific and customize the look and feel of a theme | |
87 | # further. For a list of options available for each theme, see the | |
88 | # documentation. | |
89 | # | |
90 | # html_theme_options = {} | |
91 | ||
92 | # Add any paths that contain custom static files (such as style sheets) here, | |
93 | # relative to this directory. They are copied after the builtin static files, | |
94 | # so a file named "default.css" will overwrite the builtin "default.css". | |
95 | html_static_path = ['_static'] | |
96 | ||
97 | # Custom sidebar templates, must be a dictionary that maps document names | |
98 | # to template names. | |
99 | # | |
100 | # This is required for the alabaster theme | |
101 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars | |
102 | html_sidebars = { | |
103 | '**': [ | |
104 | 'about.html', | |
105 | 'navigation.html', | |
106 | 'relations.html', # needs 'show_related': True theme option to display | |
107 | 'searchbox.html', | |
108 | 'donate.html', | |
109 | ] | |
110 | } | |
111 | ||
112 | ||
113 | # -- Options for HTMLHelp output ------------------------------------------ | |
114 | ||
115 | # Output file base name for HTML help builder. | |
116 | htmlhelp_basename = 'pygerrit2doc' | |
117 | ||
118 | ||
119 | # -- Options for LaTeX output --------------------------------------------- | |
120 | ||
121 | latex_elements = { | |
122 | # The paper size ('letterpaper' or 'a4paper'). | |
123 | # | |
124 | # 'papersize': 'letterpaper', | |
125 | ||
126 | # The font size ('10pt', '11pt' or '12pt'). | |
127 | # | |
128 | # 'pointsize': '10pt', | |
129 | ||
130 | # Additional stuff for the LaTeX preamble. | |
131 | # | |
132 | # 'preamble': '', | |
133 | ||
134 | # Latex figure (float) alignment | |
135 | # | |
136 | # 'figure_align': 'htbp', | |
137 | } | |
138 | ||
139 | # Grouping the document tree into LaTeX files. List of tuples | |
140 | # (source start file, target name, title, | |
141 | # author, documentclass [howto, manual, or own class]). | |
142 | latex_documents = [ | |
143 | (master_doc, 'pygerrit2.tex', u'pygerrit2 Documentation', | |
144 | u'David Pursehouse', 'manual'), | |
145 | ] | |
146 | ||
147 | ||
148 | # -- Options for manual page output --------------------------------------- | |
149 | ||
150 | # One entry per manual page. List of tuples | |
151 | # (source start file, name, description, authors, manual section). | |
152 | man_pages = [ | |
153 | (master_doc, 'pygerrit2', u'pygerrit2 Documentation', | |
154 | [author], 1) | |
155 | ] | |
156 | ||
157 | ||
158 | # -- Options for Texinfo output ------------------------------------------- | |
159 | ||
160 | # Grouping the document tree into Texinfo files. List of tuples | |
161 | # (source start file, target name, title, author, | |
162 | # dir menu entry, description, category) | |
163 | texinfo_documents = [ | |
164 | (master_doc, 'pygerrit2', u'pygerrit2 Documentation', | |
165 | author, 'pygerrit2', 'One line description of project.', | |
166 | 'Miscellaneous'), | |
167 | ] |
0 | .. pygerrit2 documentation master file, created by | |
1 | sphinx-quickstart on Sun Oct 22 17:32:32 2017. | |
2 | You can adapt this file completely to your liking, but it should at least | |
3 | contain the root `toctree` directive. | |
4 | ||
5 | Welcome to pygerrit2's documentation! | |
6 | ===================================== | |
7 | ||
8 | .. toctree:: | |
9 | :maxdepth: 2 | |
10 | :caption: Contents: | |
11 | ||
12 | ||
13 | ||
14 | Indices and tables | |
15 | ================== | |
16 | ||
17 | * :ref:`genindex` | |
18 | * :ref:`modindex` | |
19 | * :ref:`search` |
0 | @ECHO OFF | |
1 | ||
2 | pushd %~dp0 | |
3 | ||
4 | REM Command file for Sphinx documentation | |
5 | ||
6 | if "%SPHINXBUILD%" == "" ( | |
7 | set SPHINXBUILD=python -msphinx | |
8 | ) | |
9 | set SOURCEDIR=. | |
10 | set BUILDDIR=_build | |
11 | set SPHINXPROJ=pygerrit2 | |
12 | ||
13 | if "%1" == "" goto help | |
14 | ||
15 | %SPHINXBUILD% >NUL 2>NUL | |
16 | if errorlevel 9009 ( | |
17 | echo. | |
18 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, | |
19 | echo.then set the SPHINXBUILD environment variable to point to the full | |
20 | echo.path of the 'sphinx-build' executable. Alternatively you may add the | |
21 | echo.Sphinx directory to PATH. | |
22 | echo. | |
23 | echo.If you don't have Sphinx installed, grab it from | |
24 | echo.http://sphinx-doc.org/ | |
25 | exit /b 1 | |
26 | ) | |
27 | ||
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% | |
29 | goto end | |
30 | ||
31 | :help | |
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% | |
33 | ||
34 | :end | |
35 | popd |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
2 | ||
3 | # The MIT License | |
4 | # | |
5 | # Copyright 2013 Sony Mobile Communications. All rights reserved. | |
6 | # | |
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | # of this software and associated documentation files (the "Software"), to deal | |
9 | # in the Software without restriction, including without limitation the rights | |
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | # copies of the Software, and to permit persons to whom the Software is | |
12 | # furnished to do so, subject to the following conditions: | |
13 | # | |
14 | # The above copyright notice and this permission notice shall be included in | |
15 | # all copies or substantial portions of the Software. | |
16 | # | |
17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | # THE SOFTWARE. | |
24 | ||
25 | """Example of using the Gerrit client REST API.""" | |
26 | ||
27 | import argparse | |
28 | import logging | |
29 | import sys | |
30 | ||
31 | from requests.auth import HTTPBasicAuth, HTTPDigestAuth | |
32 | from requests.exceptions import RequestException | |
33 | try: | |
34 | from requests_kerberos import HTTPKerberosAuth, OPTIONAL | |
35 | _KERBEROS_SUPPORT = True | |
36 | except ImportError: | |
37 | _KERBEROS_SUPPORT = False | |
38 | ||
39 | from pygerrit2.rest import GerritRestAPI | |
40 | from pygerrit2.rest.auth import HTTPDigestAuthFromNetrc, HTTPBasicAuthFromNetrc | |
41 | ||
42 | ||
43 | def _main(): | |
44 | descr = 'Send request using Gerrit HTTP API' | |
45 | parser = argparse.ArgumentParser( | |
46 | description=descr, | |
47 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) | |
48 | parser.add_argument('-g', '--gerrit-url', dest='gerrit_url', | |
49 | required=True, | |
50 | help='gerrit server url') | |
51 | parser.add_argument('-b', '--basic-auth', dest='basic_auth', | |
52 | action='store_true', | |
53 | help='(deprecated) use basic auth instead of digest') | |
54 | parser.add_argument('-d', '--digest-auth', dest='digest_auth', | |
55 | action='store_true', | |
56 | help='use digest auth instead of basic') | |
57 | if _KERBEROS_SUPPORT: | |
58 | parser.add_argument('-k', '--kerberos-auth', dest='kerberos_auth', | |
59 | action='store_true', | |
60 | help='use kerberos auth') | |
61 | parser.add_argument('-u', '--username', dest='username', | |
62 | help='username') | |
63 | parser.add_argument('-p', '--password', dest='password', | |
64 | help='password') | |
65 | parser.add_argument('-n', '--netrc', dest='netrc', | |
66 | action='store_true', | |
67 | help='Use credentials from netrc') | |
68 | parser.add_argument('-v', '--verbose', dest='verbose', | |
69 | action='store_true', | |
70 | help='enable verbose (debug) logging') | |
71 | ||
72 | options = parser.parse_args() | |
73 | ||
74 | level = logging.DEBUG if options.verbose else logging.INFO | |
75 | logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', | |
76 | level=level) | |
77 | ||
78 | if _KERBEROS_SUPPORT and options.kerberos_auth: | |
79 | if options.username or options.password \ | |
80 | or options.basic_auth or options.netrc: | |
81 | parser.error("--kerberos-auth may not be used together with " | |
82 | "--username, --password, --basic-auth or --netrc") | |
83 | auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL) | |
84 | elif options.username and options.password: | |
85 | if options.netrc: | |
86 | logging.warning("--netrc option ignored") | |
87 | if options.digest_auth: | |
88 | auth = HTTPDigestAuth(options.username, options.password) | |
89 | else: | |
90 | auth = HTTPBasicAuth(options.username, options.password) | |
91 | elif options.netrc: | |
92 | if options.digest_auth: | |
93 | auth = HTTPDigestAuthFromNetrc(url=options.gerrit_url) | |
94 | else: | |
95 | auth = HTTPBasicAuthFromNetrc(url=options.gerrit_url) | |
96 | else: | |
97 | auth = None | |
98 | ||
99 | rest = GerritRestAPI(url=options.gerrit_url, auth=auth) | |
100 | ||
101 | try: | |
102 | query = ["status:open"] | |
103 | if auth: | |
104 | query += ["owner:self"] | |
105 | else: | |
106 | query += ["limit:10"] | |
107 | changes = rest.get("/changes/?q=%s" % "%20".join(query)) | |
108 | logging.info("%d changes", len(changes)) | |
109 | for change in changes: | |
110 | logging.info(change['change_id']) | |
111 | except RequestException as err: | |
112 | logging.error("Error: %s", str(err)) | |
113 | ||
114 | ||
115 | if __name__ == "__main__": | |
116 | sys.exit(_main()) |
0 | # The MIT License | |
1 | # | |
2 | # Copyright 2012 Sony Mobile Communications. All rights reserved. | |
3 | # | |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | # of this software and associated documentation files (the "Software"), to deal | |
6 | # in the Software without restriction, including without limitation the rights | |
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | # copies of the Software, and to permit persons to whom the Software is | |
9 | # furnished to do so, subject to the following conditions: | |
10 | # | |
11 | # The above copyright notice and this permission notice shall be included in | |
12 | # all copies or substantial portions of the Software. | |
13 | # | |
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | # THE SOFTWARE. | |
21 | ||
22 | """Module to interface with Gerrit.""" | |
23 | ||
24 | ||
25 | def from_json(json_data, key): | |
26 | """Extract values from JSON data. | |
27 | ||
28 | :arg dict json_data: The JSON data | |
29 | :arg str key: Key to get data for. | |
30 | ||
31 | :Returns: The value of `key` from `json_data`, or None if `json_data` | |
32 | does not contain `key`. | |
33 | ||
34 | """ | |
35 | if key in json_data: | |
36 | return json_data[key] | |
37 | return None | |
38 | ||
39 | ||
40 | def escape_string(string): | |
41 | """Escape a string for use in Gerrit commands. | |
42 | ||
43 | :arg str string: The string to escape. | |
44 | ||
45 | :returns: The string with necessary escapes and surrounding double quotes | |
46 | so that it can be passed to any of the Gerrit commands that require | |
47 | double-quoted strings. | |
48 | ||
49 | """ | |
50 | result = string | |
51 | result = result.replace('\\', '\\\\') | |
52 | result = result.replace('"', '\\"') | |
53 | return '"' + result + '"' | |
54 | ||
55 | ||
56 | class GerritReviewMessageFormatter(object): | |
57 | """Helper class to format review messages that are sent to Gerrit. | |
58 | ||
59 | :arg str header: (optional) If specified, will be prepended as the first | |
60 | paragraph of the output message. | |
61 | :arg str footer: (optional) If specified, will be appended as the last | |
62 | paragraph of the output message. | |
63 | ||
64 | """ | |
65 | ||
66 | def __init__(self, header=None, footer=None): | |
67 | """See class docstring.""" | |
68 | self.paragraphs = [] | |
69 | if header: | |
70 | self.header = header.strip() | |
71 | else: | |
72 | self.header = "" | |
73 | if footer: | |
74 | self.footer = footer.strip() | |
75 | else: | |
76 | self.footer = "" | |
77 | ||
78 | def append(self, data): | |
79 | """Append the given `data` to the output. | |
80 | ||
81 | :arg data: If a list, it is formatted as a bullet list with each | |
82 | entry in the list being a separate bullet. Otherwise if it is a | |
83 | string, the string is added as a paragraph. | |
84 | ||
85 | :raises: ValueError if `data` is not a list or a string. | |
86 | ||
87 | """ | |
88 | if not data: | |
89 | return | |
90 | ||
91 | if isinstance(data, list): | |
92 | # First we need to clean up the data. | |
93 | # | |
94 | # Gerrit creates new bullet items when it gets newline characters | |
95 | # within a bullet list paragraph, so unless we remove the newlines | |
96 | # from the texts the resulting bullet list will contain multiple | |
97 | # bullets and look crappy. | |
98 | # | |
99 | # We add the '*' character on the beginning of each bullet text in | |
100 | # the next step, so we strip off any existing leading '*' that the | |
101 | # caller has added, and then strip off any leading or trailing | |
102 | # whitespace. | |
103 | _items = [x.replace("\n", " ").strip().lstrip('*').strip() | |
104 | for x in data] | |
105 | ||
106 | # Create the bullet list only with the items that still have any | |
107 | # text in them after cleaning up. | |
108 | _paragraph = "\n".join(["* %s" % x for x in _items if x]) | |
109 | if _paragraph: | |
110 | self.paragraphs.append(_paragraph) | |
111 | elif isinstance(data, str): | |
112 | _paragraph = data.strip() | |
113 | if _paragraph: | |
114 | self.paragraphs.append(_paragraph) | |
115 | else: | |
116 | raise ValueError('Data must be a list or a string') | |
117 | ||
118 | def is_empty(self): | |
119 | """Check if the formatter is empty. | |
120 | ||
121 | :Returns: True if empty, i.e. no paragraphs have been added. | |
122 | ||
123 | """ | |
124 | return not self.paragraphs | |
125 | ||
126 | def format(self): | |
127 | """Format the message parts to a string. | |
128 | ||
129 | :Returns: A string of all the message parts separated into paragraphs, | |
130 | with header and footer paragraphs if they were specified in the | |
131 | constructor. | |
132 | ||
133 | """ | |
134 | message = "" | |
135 | if self.paragraphs: | |
136 | if self.header: | |
137 | message += (self.header + '\n\n') | |
138 | message += "\n\n".join(self.paragraphs) | |
139 | if self.footer: | |
140 | message += ('\n\n' + self.footer) | |
141 | return message |
0 | # The MIT License | |
1 | # | |
2 | # Copyright 2013 Sony Mobile Communications. All rights reserved. | |
3 | # | |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | # of this software and associated documentation files (the "Software"), to deal | |
6 | # in the Software without restriction, including without limitation the rights | |
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | # copies of the Software, and to permit persons to whom the Software is | |
9 | # furnished to do so, subject to the following conditions: | |
10 | # | |
11 | # The above copyright notice and this permission notice shall be included in | |
12 | # all copies or substantial portions of the Software. | |
13 | # | |
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | # THE SOFTWARE. | |
21 | ||
22 | """Interface to the Gerrit REST API.""" | |
23 | ||
24 | import json | |
25 | import logging | |
26 | import requests | |
27 | ||
28 | GERRIT_MAGIC_JSON_PREFIX = ")]}\'\n" | |
29 | GERRIT_AUTH_SUFFIX = "/a" | |
30 | ||
31 | ||
32 | def _decode_response(response): | |
33 | """Strip off Gerrit's magic prefix and decode a response. | |
34 | ||
35 | :returns: | |
36 | Decoded JSON content as a dict, or raw text if content could not be | |
37 | decoded as JSON. | |
38 | ||
39 | :raises: | |
40 | requests.HTTPError if the response contains an HTTP error status code. | |
41 | ||
42 | """ | |
43 | content = response.content.strip() | |
44 | if response.encoding: | |
45 | content = content.decode(response.encoding) | |
46 | response.raise_for_status() | |
47 | content_type = response.headers.get('content-type', '') | |
48 | if content_type.split(';')[0] != 'application/json': | |
49 | return content | |
50 | if content.startswith(GERRIT_MAGIC_JSON_PREFIX): | |
51 | content = content[len(GERRIT_MAGIC_JSON_PREFIX):] | |
52 | try: | |
53 | return json.loads(content) | |
54 | except ValueError: | |
55 | logging.error('Invalid json content: %s', content) | |
56 | raise | |
57 | ||
58 | ||
59 | def _merge_dict(result, overrides): | |
60 | """Deep-merge dictionaries. | |
61 | ||
62 | :arg dict result: The resulting dictionary | |
63 | :arg dict overrides: Dictionay being merged into the result | |
64 | ||
65 | :returns: | |
66 | The resulting dictionary | |
67 | ||
68 | """ | |
69 | for key in overrides: | |
70 | if (key in result and | |
71 | isinstance(result[key], dict) and | |
72 | isinstance(overrides[key], dict)): | |
73 | _merge_dict(result[key], overrides[key]) | |
74 | else: | |
75 | result[key] = overrides[key] | |
76 | return result | |
77 | ||
78 | ||
79 | class GerritRestAPI(object): | |
80 | """Interface to the Gerrit REST API. | |
81 | ||
82 | :arg str url: The full URL to the server, including the `http(s)://` | |
83 | prefix. If `auth` is given, `url` will be automatically adjusted to | |
84 | include Gerrit's authentication suffix. | |
85 | :arg auth: (optional) Authentication handler. Must be derived from | |
86 | `requests.auth.AuthBase`. | |
87 | :arg boolean verify: (optional) Set to False to disable verification of | |
88 | SSL certificates. | |
89 | ||
90 | """ | |
91 | ||
92 | def __init__(self, url, auth=None, verify=True): | |
93 | """See class docstring.""" | |
94 | headers = {'Accept': 'application/json', | |
95 | 'Accept-Encoding': 'gzip'} | |
96 | self.kwargs = {'auth': auth, | |
97 | 'verify': verify, | |
98 | 'headers': headers} | |
99 | self.url = url.rstrip('/') | |
100 | self.session = requests.session() | |
101 | ||
102 | if auth: | |
103 | if not isinstance(auth, requests.auth.AuthBase): | |
104 | raise ValueError('Invalid auth type; must be derived ' | |
105 | 'from requests.auth.AuthBase') | |
106 | ||
107 | if not self.url.endswith(GERRIT_AUTH_SUFFIX): | |
108 | self.url += GERRIT_AUTH_SUFFIX | |
109 | else: | |
110 | if self.url.endswith(GERRIT_AUTH_SUFFIX): | |
111 | self.url = self.url[: - len(GERRIT_AUTH_SUFFIX)] | |
112 | ||
113 | if not self.url.endswith('/'): | |
114 | self.url += '/' | |
115 | ||
116 | def make_url(self, endpoint): | |
117 | """Make the full url for the endpoint. | |
118 | ||
119 | :arg str endpoint: The endpoint. | |
120 | ||
121 | :returns: | |
122 | The full url. | |
123 | ||
124 | """ | |
125 | endpoint = endpoint.lstrip('/') | |
126 | return self.url + endpoint | |
127 | ||
128 | def get(self, endpoint, return_response=False, **kwargs): | |
129 | """Send HTTP GET to the endpoint. | |
130 | ||
131 | :arg str endpoint: The endpoint to send to. | |
132 | :arg bool return_response: If true will also return the response | |
133 | ||
134 | :returns: | |
135 | JSON decoded result. | |
136 | ||
137 | :raises: | |
138 | requests.RequestException on timeout or connection error. | |
139 | ||
140 | """ | |
141 | kwargs.update(self.kwargs.copy()) | |
142 | response = self.session.get(self.make_url(endpoint), **kwargs) | |
143 | ||
144 | decoded_response = _decode_response(response) | |
145 | ||
146 | if return_response: | |
147 | return decoded_response, response | |
148 | return decoded_response | |
149 | ||
150 | def put(self, endpoint, return_response=False, **kwargs): | |
151 | """Send HTTP PUT to the endpoint. | |
152 | ||
153 | :arg str endpoint: The endpoint to send to. | |
154 | ||
155 | :returns: | |
156 | JSON decoded result. | |
157 | ||
158 | :raises: | |
159 | requests.RequestException on timeout or connection error. | |
160 | ||
161 | """ | |
162 | args = {} | |
163 | if ("data" in kwargs and isinstance(kwargs["data"], dict)) or \ | |
164 | "json" in kwargs: | |
165 | _merge_dict( | |
166 | args, { | |
167 | "headers": { | |
168 | "Content-Type": "application/json;charset=UTF-8" | |
169 | } | |
170 | } | |
171 | ) | |
172 | _merge_dict(args, self.kwargs.copy()) | |
173 | _merge_dict(args, kwargs) | |
174 | response = self.session.put(self.make_url(endpoint), **args) | |
175 | ||
176 | decoded_response = _decode_response(response) | |
177 | ||
178 | if return_response: | |
179 | return decoded_response, response | |
180 | return decoded_response | |
181 | ||
182 | def post(self, endpoint, return_response=False, **kwargs): | |
183 | """Send HTTP POST to the endpoint. | |
184 | ||
185 | :arg str endpoint: The endpoint to send to. | |
186 | ||
187 | :returns: | |
188 | JSON decoded result. | |
189 | ||
190 | :raises: | |
191 | requests.RequestException on timeout or connection error. | |
192 | ||
193 | """ | |
194 | args = {} | |
195 | if ("data" in kwargs and isinstance(kwargs["data"], dict)) or \ | |
196 | "json" in kwargs: | |
197 | _merge_dict( | |
198 | args, { | |
199 | "headers": { | |
200 | "Content-Type": "application/json;charset=UTF-8" | |
201 | } | |
202 | } | |
203 | ) | |
204 | _merge_dict(args, self.kwargs.copy()) | |
205 | _merge_dict(args, kwargs) | |
206 | response = self.session.post(self.make_url(endpoint), **args) | |
207 | ||
208 | decoded_response = _decode_response(response) | |
209 | ||
210 | if return_response: | |
211 | return decoded_response, response | |
212 | return decoded_response | |
213 | ||
214 | def delete(self, endpoint, return_response=False, **kwargs): | |
215 | """Send HTTP DELETE to the endpoint. | |
216 | ||
217 | :arg str endpoint: The endpoint to send to. | |
218 | ||
219 | :returns: | |
220 | JSON decoded result. | |
221 | ||
222 | :raises: | |
223 | requests.RequestException on timeout or connection error. | |
224 | ||
225 | """ | |
226 | args = {} | |
227 | if "data" in kwargs or "json" in kwargs: | |
228 | _merge_dict( | |
229 | args, { | |
230 | "headers": { | |
231 | "Content-Type": "application/json;charset=UTF-8" | |
232 | } | |
233 | } | |
234 | ) | |
235 | _merge_dict(args, self.kwargs.copy()) | |
236 | _merge_dict(args, kwargs) | |
237 | response = self.session.delete(self.make_url(endpoint), **args) | |
238 | ||
239 | decoded_response = _decode_response(response) | |
240 | ||
241 | if return_response: | |
242 | return decoded_response, response | |
243 | return decoded_response | |
244 | ||
245 | def review(self, change_id, revision, review): | |
246 | """Submit a review. | |
247 | ||
248 | :arg str change_id: The change ID. | |
249 | :arg str revision: The revision. | |
250 | :arg str review: The review details as a :class:`GerritReview`. | |
251 | ||
252 | :returns: | |
253 | JSON decoded result. | |
254 | ||
255 | :raises: | |
256 | requests.RequestException on timeout or connection error. | |
257 | ||
258 | """ | |
259 | endpoint = "changes/%s/revisions/%s/review" % (change_id, revision) | |
260 | self.post(endpoint, data=str(review)) | |
261 | ||
262 | ||
263 | class GerritReview(object): | |
264 | """Encapsulation of a Gerrit review. | |
265 | ||
266 | :arg str message: (optional) Cover message. | |
267 | :arg dict labels: (optional) Review labels. | |
268 | :arg dict comments: (optional) Inline comments. | |
269 | ||
270 | """ | |
271 | ||
272 | def __init__(self, message=None, labels=None, comments=None): | |
273 | """See class docstring.""" | |
274 | self.message = message if message else "" | |
275 | if labels: | |
276 | if not isinstance(labels, dict): | |
277 | raise ValueError("labels must be a dict.") | |
278 | self.labels = labels | |
279 | else: | |
280 | self.labels = {} | |
281 | if comments: | |
282 | if not isinstance(comments, list): | |
283 | raise ValueError("comments must be a list.") | |
284 | self.comments = {} | |
285 | self.add_comments(comments) | |
286 | else: | |
287 | self.comments = {} | |
288 | ||
289 | def set_message(self, message): | |
290 | """Set review cover message. | |
291 | ||
292 | :arg str message: Cover message. | |
293 | ||
294 | """ | |
295 | self.message = message | |
296 | ||
297 | def add_labels(self, labels): | |
298 | """Add labels. | |
299 | ||
300 | :arg dict labels: Labels to add, for example | |
301 | ||
302 | Usage:: | |
303 | ||
304 | add_labels({'Verified': 1, | |
305 | 'Code-Review': -1}) | |
306 | ||
307 | """ | |
308 | self.labels.update(labels) | |
309 | ||
310 | def add_comments(self, comments): | |
311 | """Add inline comments. | |
312 | ||
313 | :arg dict comments: Comments to add. | |
314 | ||
315 | Usage:: | |
316 | ||
317 | add_comments([{'filename': 'Makefile', | |
318 | 'line': 10, | |
319 | 'message': 'inline message'}]) | |
320 | ||
321 | add_comments([{'filename': 'Makefile', | |
322 | 'range': {'start_line': 0, | |
323 | 'start_character': 1, | |
324 | 'end_line': 0, | |
325 | 'end_character': 5}, | |
326 | 'message': 'inline message'}]) | |
327 | ||
328 | """ | |
329 | for comment in comments: | |
330 | if 'filename' and 'message' in list(comment.keys()): | |
331 | msg = {} | |
332 | if 'range' in list(comment.keys()): | |
333 | msg = {"range": comment['range'], | |
334 | "message": comment['message']} | |
335 | elif 'line' in list(comment.keys()): | |
336 | msg = {"line": comment['line'], | |
337 | "message": comment['message']} | |
338 | else: | |
339 | continue | |
340 | file_comment = {comment['filename']: [msg]} | |
341 | if self.comments: | |
342 | if comment['filename'] in list(self.comments.keys()): | |
343 | self.comments[comment['filename']].append(msg) | |
344 | else: | |
345 | self.comments.update(file_comment) | |
346 | else: | |
347 | self.comments.update(file_comment) | |
348 | ||
349 | def __str__(self): | |
350 | """Return a string representation.""" | |
351 | review_input = {} | |
352 | if self.message: | |
353 | review_input.update({'message': self.message}) | |
354 | if self.labels: | |
355 | review_input.update({'labels': self.labels}) | |
356 | if self.comments: | |
357 | review_input.update({'comments': self.comments}) | |
358 | return json.dumps(review_input, sort_keys=True) |
0 | # The MIT License | |
1 | # | |
2 | # Copyright 2013 Sony Mobile Communications. All rights reserved. | |
3 | # | |
4 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | # of this software and associated documentation files (the "Software"), to deal | |
6 | # in the Software without restriction, including without limitation the rights | |
7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | # copies of the Software, and to permit persons to whom the Software is | |
9 | # furnished to do so, subject to the following conditions: | |
10 | # | |
11 | # The above copyright notice and this permission notice shall be included in | |
12 | # all copies or substantial portions of the Software. | |
13 | # | |
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | # THE SOFTWARE. | |
21 | ||
22 | """Authentication handlers.""" | |
23 | ||
24 | from requests.auth import HTTPDigestAuth, HTTPBasicAuth | |
25 | from requests.utils import get_netrc_auth | |
26 | ||
27 | ||
28 | class HTTPDigestAuthFromNetrc(HTTPDigestAuth): | |
29 | """HTTP Digest Auth with netrc credentials.""" | |
30 | ||
31 | def __init__(self, url): | |
32 | """See class docstring.""" | |
33 | auth = get_netrc_auth(url) | |
34 | if not auth: | |
35 | raise ValueError("netrc missing or no credentials found in netrc") | |
36 | username, password = auth | |
37 | super(HTTPDigestAuthFromNetrc, self).__init__(username, password) | |
38 | ||
39 | ||
40 | class HTTPBasicAuthFromNetrc(HTTPBasicAuth): | |
41 | """HTTP Basic Auth with netrc credentials.""" | |
42 | ||
43 | def __init__(self, url): | |
44 | """See class docstring.""" | |
45 | auth = get_netrc_auth(url) | |
46 | if not auth: | |
47 | raise ValueError("netrc missing or no credentials found in netrc") | |
48 | username, password = auth | |
49 | super(HTTPBasicAuthFromNetrc, self).__init__(username, password) |
0 | Metadata-Version: 1.1 | |
1 | Name: pygerrit2 | |
2 | Version: 2.0.4 | |
3 | Summary: Client library for interacting with Gerrit's REST API | |
4 | Home-page: https://github.com/dpursehouse/pygerrit2 | |
5 | Author: David Pursehouse | |
6 | Author-email: david.pursehouse@gmail.com | |
7 | License: The MIT License | |
8 | Description-Content-Type: UNKNOWN | |
9 | Description: Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API | |
10 | ============================================================================= | |
11 | ||
12 | .. image:: https://img.shields.io/pypi/v/pygerrit2.png | |
13 | ||
14 | .. image:: https://img.shields.io/pypi/l/pygerrit2.png | |
15 | ||
16 | Pygerrit2 provides a simple interface for clients to interact with | |
17 | `Gerrit Code Review`_ via the REST API. | |
18 | ||
19 | Prerequisites | |
20 | ------------- | |
21 | ||
22 | Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 | |
23 | is experimental. | |
24 | ||
25 | Pygerrit2 depends on the `requests`_ library. | |
26 | ||
27 | ||
28 | Installation | |
29 | ------------ | |
30 | ||
31 | To install pygerrit2, simply:: | |
32 | ||
33 | $ pip install pygerrit2 | |
34 | ||
35 | ||
36 | Usage | |
37 | ----- | |
38 | ||
39 | This simple example shows how to get the user's open changes. Authentication | |
40 | to Gerrit is done via HTTP Basic authentication, using an explicitly given | |
41 | username and password:: | |
42 | ||
43 | >>> from requests.auth import HTTPBasicAuth | |
44 | >>> from pygerrit2.rest import GerritRestAPI | |
45 | >>> auth = HTTPBasicAuth('username', 'password') | |
46 | >>> rest = GerritRestAPI(url='http://review.example.net', auth=auth) | |
47 | >>> changes = rest.get("/changes/?q=owner:self%20status:open") | |
48 | ||
49 | Note that is is not necessary to add the ``/a/`` prefix on the endpoint | |
50 | URLs. This is automatically added when the API is instantiated with an | |
51 | authentication object. | |
52 | ||
53 | If the user's HTTP username and password are defined in the ``.netrc`` | |
54 | file:: | |
55 | ||
56 | machine review.example.net login MyUsername password MyPassword | |
57 | ||
58 | then it is possible to authenticate with those credentials:: | |
59 | ||
60 | >>> from pygerrit2.rest import GerritRestAPI | |
61 | >>> from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc | |
62 | >>> url = 'http://review.example.net' | |
63 | >>> auth = HTTPBasicAuthFromNetrc(url=url) | |
64 | >>> rest = GerritRestAPI(url=url, auth=auth) | |
65 | >>> changes = rest.get("/changes/?q=owner:self%20status:open") | |
66 | ||
67 | Note that the HTTP password is not the same as the SSH password. For | |
68 | instructions on how to obtain the HTTP password, refer to Gerrit's | |
69 | `HTTP upload settings`_ documentation. | |
70 | ||
71 | Also note that in Gerrit version 2.14, support for HTTP Digest authentication | |
72 | was removed and only HTTP Basic authentication is supported. When using | |
73 | pygerrit2 against an earlier Gerrit version, it may be necessary to replace | |
74 | the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. | |
75 | ||
76 | Refer to the `example`_ script for a full working example. | |
77 | ||
78 | Copyright and License | |
79 | --------------------- | |
80 | ||
81 | Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. | |
82 | ||
83 | Copyright 2012 Sony Mobile Communications. All rights reserved. | |
84 | ||
85 | Copyright 2016 David Pursehouse. All rights reserved. | |
86 | ||
87 | Licensed under The MIT License. Please refer to the `LICENSE`_ file for full | |
88 | license details. | |
89 | ||
90 | .. _`Gerrit Code Review`: https://gerritcodereview.com/ | |
91 | .. _`requests`: https://github.com/kennethreitz/requests | |
92 | .. _example: https://github.com/dpursehouse/pygerrit2/blob/master/example.py | |
93 | .. _`HTTP upload settings`: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http | |
94 | .. _LICENSE: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE | |
95 | ||
96 | ||
97 | Keywords: gerrit | |
98 | rest | |
99 | http | |
100 | Platform: UNKNOWN | |
101 | Classifier: Development Status :: 3 - Alpha | |
102 | Classifier: Environment :: Console | |
103 | Classifier: Intended Audience :: Developers | |
104 | Classifier: License :: OSI Approved :: MIT License | |
105 | Classifier: Natural Language :: English | |
106 | Classifier: Programming Language :: Python | |
107 | Classifier: Programming Language :: Python :: 2.6 | |
108 | Classifier: Programming Language :: Python :: 2.7 |
0 | AUTHORS | |
1 | ChangeLog | |
2 | LICENSE | |
3 | MANIFEST.in | |
4 | Makefile | |
5 | README.md | |
6 | README.rst | |
7 | example.py | |
8 | requirements.txt | |
9 | setup.cfg | |
10 | setup.py | |
11 | test_requirements.txt | |
12 | tox.ini | |
13 | unittests.py | |
14 | docs/Makefile | |
15 | docs/conf.py | |
16 | docs/index.rst | |
17 | docs/make.bat | |
18 | pygerrit2/__init__.py | |
19 | pygerrit2.egg-info/PKG-INFO | |
20 | pygerrit2.egg-info/SOURCES.txt | |
21 | pygerrit2.egg-info/dependency_links.txt | |
22 | pygerrit2.egg-info/not-zip-safe | |
23 | pygerrit2.egg-info/pbr.json | |
24 | pygerrit2.egg-info/requires.txt | |
25 | pygerrit2.egg-info/top_level.txt | |
26 | pygerrit2/rest/__init__.py | |
27 | pygerrit2/rest/auth.py⏎ |
0 | {"git_version": "5eb119b", "is_release": false}⏎ |
0 | pygerrit2 |
0 | [metadata] | |
1 | name = pygerrit2 | |
2 | summary = Client library for interacting with Gerrit's REST API | |
3 | author = David Pursehouse | |
4 | author_email = david.pursehouse@gmail.com | |
5 | home-page = https://github.com/dpursehouse/pygerrit2 | |
6 | license = The MIT License | |
7 | description-file = README.rst | |
8 | keywords = | |
9 | gerrit | |
10 | rest | |
11 | http | |
12 | classifiers = | |
13 | Development Status :: 3 - Alpha | |
14 | Environment :: Console | |
15 | Intended Audience :: Developers | |
16 | License :: OSI Approved :: MIT License | |
17 | Natural Language :: English | |
18 | Programming Language :: Python | |
19 | Programming Language :: Python :: 2.6 | |
20 | Programming Language :: Python :: 2.7 | |
21 | ||
22 | [egg_info] | |
23 | tag_build = | |
24 | tag_date = 0 | |
25 |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
2 | ||
3 | # The MIT License | |
4 | # | |
5 | # Copyright 2012 Sony Mobile Communications. All rights reserved. | |
6 | # | |
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | # of this software and associated documentation files (the "Software"), to deal | |
9 | # in the Software without restriction, including without limitation the rights | |
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | # copies of the Software, and to permit persons to whom the Software is | |
12 | # furnished to do so, subject to the following conditions: | |
13 | # | |
14 | # The above copyright notice and this permission notice shall be included in | |
15 | # all copies or substantial portions of the Software. | |
16 | # | |
17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | # THE SOFTWARE. | |
24 | ||
25 | """Client library for interacting with Gerrit.""" | |
26 | ||
27 | import setuptools | |
28 | ||
29 | ||
30 | def _main(): | |
31 | setuptools.setup( | |
32 | packages=setuptools.find_packages(), | |
33 | setup_requires=['pbr'], | |
34 | pbr=True) | |
35 | ||
36 | ||
37 | if __name__ == "__main__": | |
38 | _main() |
0 | [tox] | |
1 | minversion = 2.1.1 | |
2 | envlist = pep8, py35, py27, py26 | |
3 | ||
4 | [testenv] | |
5 | setenv = VIRTUAL_ENV={envdir} | |
6 | SUBUNIT_FORMATTER=tee testr_subunit_log | |
7 | OS_STDOUT_NOCAPTURE=False | |
8 | LANG=en_US.UTF-8 | |
9 | usedevelop = True | |
10 | install_command = pip install {opts} {packages} | |
11 | deps = -r{toxinidir}/test_requirements.txt | |
12 | commands = python unittests.py | |
13 | ||
14 | [testenv:pep8] | |
15 | commands = flake8 | |
16 | ||
17 | [testenv:pyflakes] | |
18 | deps = pyflakes | |
19 | commands = pyflakes pygerrit2 unittests.py example.py setup.py | |
20 | ||
21 | [flake8] | |
22 | max-line-length = 80 | |
23 | show-source = True | |
24 | exclude = .venv,.tox,dist,docs,build,*.egg,.test,pygerrit2env |
0 | #!/usr/bin/env python | |
1 | # -*- coding: utf-8 -*- | |
2 | ||
3 | # The MIT License | |
4 | # | |
5 | # Copyright 2012 Sony Mobile Communications. All rights reserved. | |
6 | # | |
7 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | # of this software and associated documentation files (the "Software"), to deal | |
9 | # in the Software without restriction, including without limitation the rights | |
10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | # copies of the Software, and to permit persons to whom the Software is | |
12 | # furnished to do so, subject to the following conditions: | |
13 | # | |
14 | # The above copyright notice and this permission notice shall be included in | |
15 | # all copies or substantial portions of the Software. | |
16 | # | |
17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | # THE SOFTWARE. | |
24 | ||
25 | """Unit tests for the Pygerrit2 helper methods.""" | |
26 | ||
27 | import unittest | |
28 | ||
29 | from pygerrit2 import GerritReviewMessageFormatter | |
30 | from pygerrit2.rest import GerritReview, _merge_dict | |
31 | ||
32 | EXPECTED_TEST_CASE_FIELDS = ['header', 'footer', 'paragraphs', 'result'] | |
33 | ||
34 | ||
35 | TEST_CASES = [ | |
36 | {'header': None, | |
37 | 'footer': None, | |
38 | 'paragraphs': [], | |
39 | 'result': ""}, | |
40 | {'header': "Header", | |
41 | 'footer': "Footer", | |
42 | 'paragraphs': [], | |
43 | 'result': ""}, | |
44 | {'header': None, | |
45 | 'footer': None, | |
46 | 'paragraphs': ["Test"], | |
47 | 'result': "Test"}, | |
48 | {'header': None, | |
49 | 'footer': None, | |
50 | 'paragraphs': ["Test", "Test"], | |
51 | 'result': "Test\n\nTest"}, | |
52 | {'header': "Header", | |
53 | 'footer': None, | |
54 | 'paragraphs': ["Test"], | |
55 | 'result': "Header\n\nTest"}, | |
56 | {'header': "Header", | |
57 | 'footer': None, | |
58 | 'paragraphs': ["Test", "Test"], | |
59 | 'result': "Header\n\nTest\n\nTest"}, | |
60 | {'header': "Header", | |
61 | 'footer': "Footer", | |
62 | 'paragraphs': ["Test", "Test"], | |
63 | 'result': "Header\n\nTest\n\nTest\n\nFooter"}, | |
64 | {'header': "Header", | |
65 | 'footer': "Footer", | |
66 | 'paragraphs': [["One"]], | |
67 | 'result': "Header\n\n* One\n\nFooter"}, | |
68 | {'header': "Header", | |
69 | 'footer': "Footer", | |
70 | 'paragraphs': [["One", "Two"]], | |
71 | 'result': "Header\n\n* One\n* Two\n\nFooter"}, | |
72 | {'header': "Header", | |
73 | 'footer': "Footer", | |
74 | 'paragraphs': ["Test", ["One"], "Test"], | |
75 | 'result': "Header\n\nTest\n\n* One\n\nTest\n\nFooter"}, | |
76 | {'header': "Header", | |
77 | 'footer': "Footer", | |
78 | 'paragraphs': ["Test", ["One", "Two"], "Test"], | |
79 | 'result': "Header\n\nTest\n\n* One\n* Two\n\nTest\n\nFooter"}, | |
80 | {'header': "Header", | |
81 | 'footer': "Footer", | |
82 | 'paragraphs': ["Test", "Test", ["One"]], | |
83 | 'result': "Header\n\nTest\n\nTest\n\n* One\n\nFooter"}, | |
84 | {'header': None, | |
85 | 'footer': None, | |
86 | 'paragraphs': [["* One", "* Two"]], | |
87 | 'result': "* One\n* Two"}, | |
88 | {'header': None, | |
89 | 'footer': None, | |
90 | 'paragraphs': [["* One ", " * Two "]], | |
91 | 'result': "* One\n* Two"}, | |
92 | {'header': None, | |
93 | 'footer': None, | |
94 | 'paragraphs': [["*", "*"]], | |
95 | 'result': ""}, | |
96 | {'header': None, | |
97 | 'footer': None, | |
98 | 'paragraphs': [["", ""]], | |
99 | 'result': ""}, | |
100 | {'header': None, | |
101 | 'footer': None, | |
102 | 'paragraphs': [[" ", " "]], | |
103 | 'result': ""}, | |
104 | {'header': None, | |
105 | 'footer': None, | |
106 | 'paragraphs': [["* One", " ", "* Two"]], | |
107 | 'result': "* One\n* Two"}] | |
108 | ||
109 | ||
110 | class TestMergeDict(unittest.TestCase): | |
111 | """Tests for the `_merge_dict` method.""" | |
112 | ||
113 | def test_merge_into_empty_dict(self): | |
114 | """Test merging into an empty dict.""" | |
115 | dct = {} | |
116 | _merge_dict(dct, {'a': 1, 'b': 2}) | |
117 | self.assertEqual(dct, {'a': 1, 'b': 2}) | |
118 | ||
119 | def test_merge_flat(self): | |
120 | """Test merging a flat dict.""" | |
121 | dct = {'c': 3} | |
122 | _merge_dict(dct, {'a': 1, 'b': 2}) | |
123 | self.assertEqual(dct, {'a': 1, 'b': 2, 'c': 3}) | |
124 | ||
125 | def test_merge_with_override(self): | |
126 | """Test merging a dict and overriding values.""" | |
127 | dct = {'a': 1} | |
128 | _merge_dict(dct, {'a': 0, 'b': 2}) | |
129 | self.assertEqual(dct, {'a': 0, 'b': 2}) | |
130 | ||
131 | def test_merge_two_levels(self): | |
132 | """Test merging a dict with two levels.""" | |
133 | dct = { | |
134 | 'a': { | |
135 | 'A': 1, | |
136 | 'AA': 2, | |
137 | }, | |
138 | 'b': { | |
139 | 'B': 1, | |
140 | 'BB': 2, | |
141 | }, | |
142 | } | |
143 | overrides = { | |
144 | 'a': { | |
145 | 'AAA': 3, | |
146 | }, | |
147 | 'b': { | |
148 | 'BBB': 3, | |
149 | }, | |
150 | } | |
151 | _merge_dict(dct, overrides) | |
152 | self.assertEqual( | |
153 | dct, | |
154 | { | |
155 | 'a': { | |
156 | 'A': 1, | |
157 | 'AA': 2, | |
158 | 'AAA': 3, | |
159 | }, | |
160 | 'b': { | |
161 | 'B': 1, | |
162 | 'BB': 2, | |
163 | 'BBB': 3, | |
164 | }, | |
165 | } | |
166 | ) | |
167 | ||
168 | ||
169 | class TestGerritReviewMessageFormatter(unittest.TestCase): | |
170 | """Test that the GerritReviewMessageFormatter class behaves properly.""" | |
171 | ||
172 | def _check_test_case_fields(self, test_case, i): | |
173 | for field in EXPECTED_TEST_CASE_FIELDS: | |
174 | self.assertTrue(field in test_case, | |
175 | "field '%s' not present in test case #%d" % | |
176 | (field, i)) | |
177 | self.assertTrue( | |
178 | isinstance(test_case['paragraphs'], list), | |
179 | "'paragraphs' field is not a list in test case #%d" % i) | |
180 | ||
181 | def test_is_empty(self): | |
182 | """Test if message is empty for missing header and footer.""" | |
183 | fmt = GerritReviewMessageFormatter(header=None, footer=None) | |
184 | self.assertTrue(fmt.is_empty()) | |
185 | fmt.append(['test']) | |
186 | self.assertFalse(fmt.is_empty()) | |
187 | ||
188 | def test_message_formatting(self): | |
189 | """Test message formatter for different test cases.""" | |
190 | for i, test_case in enumerate(TEST_CASES): | |
191 | self._check_test_case_fields(test_case, i) | |
192 | fmt = GerritReviewMessageFormatter(header=test_case['header'], | |
193 | footer=test_case['footer']) | |
194 | for paragraph in test_case['paragraphs']: | |
195 | fmt.append(paragraph) | |
196 | msg = fmt.format() | |
197 | self.assertEqual(msg, test_case['result'], | |
198 | "Formatted message does not match expected " | |
199 | "result in test case #%d:\n[%s]" % (i, msg)) | |
200 | ||
201 | ||
202 | class TestGerritReview(unittest.TestCase): | |
203 | """Test that the GerritReview class behaves properly.""" | |
204 | ||
205 | def test_str(self): | |
206 | """Test for str function.""" | |
207 | obj = GerritReview() | |
208 | self.assertEqual(str(obj), '{}') | |
209 | ||
210 | obj2 = GerritReview(labels={'Verified': 1, 'Code-Review': -1}) | |
211 | self.assertEqual( | |
212 | str(obj2), | |
213 | '{"labels": {"Code-Review": -1, "Verified": 1}}') | |
214 | ||
215 | obj3 = GerritReview(comments=[{'filename': 'Makefile', | |
216 | 'line': 10, 'message': 'test'}]) | |
217 | self.assertEqual( | |
218 | str(obj3), | |
219 | '{"comments": {"Makefile": [{"line": 10, "message": "test"}]}}') | |
220 | ||
221 | obj4 = GerritReview(labels={'Verified': 1, 'Code-Review': -1}, | |
222 | comments=[{'filename': 'Makefile', 'line': 10, | |
223 | 'message': 'test'}]) | |
224 | self.assertEqual( | |
225 | str(obj4), | |
226 | '{"comments": {"Makefile": [{"line": 10, "message": "test"}]},' | |
227 | ' "labels": {"Code-Review": -1, "Verified": 1}}') | |
228 | ||
229 | obj5 = GerritReview(comments=[ | |
230 | {'filename': 'Makefile', 'line': 15, 'message': 'test'}, | |
231 | {'filename': 'Make', 'line': 10, 'message': 'test1'} | |
232 | ]) | |
233 | self.assertEqual( | |
234 | str(obj5), | |
235 | '{"comments": {"Make": [{"line": 10, "message": "test1"}],' | |
236 | ' "Makefile": [{"line": 15, "message": "test"}]}}') | |
237 | ||
238 | ||
239 | if __name__ == '__main__': | |
240 | unittest.main() |