pygerrit2-2.0.4/0000755€][@€/";‰0000000000013215133522014470 5ustar dpursehouse00000000000000pygerrit2-2.0.4/test_requirements.txt0000644€][@€/";‰0000000006013214125204021004 0ustar dpursehouse00000000000000pydocstyle==2.1.1 flake8==3.5.0 pyflakes==1.6.0 pygerrit2-2.0.4/PKG-INFO0000644€][@€/";‰0000001060013215133522015562 0ustar dpursehouse00000000000000Metadata-Version: 1.1 Name: pygerrit2 Version: 2.0.4 Summary: Client library for interacting with Gerrit's REST API Home-page: https://github.com/dpursehouse/pygerrit2 Author: David Pursehouse Author-email: david.pursehouse@gmail.com License: The MIT License Description-Content-Type: UNKNOWN Description: Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API ============================================================================= .. image:: https://img.shields.io/pypi/v/pygerrit2.png .. image:: https://img.shields.io/pypi/l/pygerrit2.png Pygerrit2 provides a simple interface for clients to interact with `Gerrit Code Review`_ via the REST API. Prerequisites ------------- Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 is experimental. Pygerrit2 depends on the `requests`_ library. Installation ------------ To install pygerrit2, simply:: $ pip install pygerrit2 Usage ----- This simple example shows how to get the user's open changes. Authentication to Gerrit is done via HTTP Basic authentication, using an explicitly given username and password:: >>> from requests.auth import HTTPBasicAuth >>> from pygerrit2.rest import GerritRestAPI >>> auth = HTTPBasicAuth('username', 'password') >>> rest = GerritRestAPI(url='http://review.example.net', auth=auth) >>> changes = rest.get("/changes/?q=owner:self%20status:open") Note that is is not necessary to add the ``/a/`` prefix on the endpoint URLs. This is automatically added when the API is instantiated with an authentication object. If the user's HTTP username and password are defined in the ``.netrc`` file:: machine review.example.net login MyUsername password MyPassword then it is possible to authenticate with those credentials:: >>> from pygerrit2.rest import GerritRestAPI >>> from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc >>> url = 'http://review.example.net' >>> auth = HTTPBasicAuthFromNetrc(url=url) >>> rest = GerritRestAPI(url=url, auth=auth) >>> changes = rest.get("/changes/?q=owner:self%20status:open") Note that the HTTP password is not the same as the SSH password. For instructions on how to obtain the HTTP password, refer to Gerrit's `HTTP upload settings`_ documentation. Also note that in Gerrit version 2.14, support for HTTP Digest authentication was removed and only HTTP Basic authentication is supported. When using pygerrit2 against an earlier Gerrit version, it may be necessary to replace the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. Refer to the `example`_ script for a full working example. Copyright and License --------------------- Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. Copyright 2012 Sony Mobile Communications. All rights reserved. Copyright 2016 David Pursehouse. All rights reserved. Licensed under The MIT License. Please refer to the `LICENSE`_ file for full license details. .. _`Gerrit Code Review`: https://gerritcodereview.com/ .. _`requests`: https://github.com/kennethreitz/requests .. _example: https://github.com/dpursehouse/pygerrit2/blob/master/example.py .. _`HTTP upload settings`: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http .. _LICENSE: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE Keywords: gerrit rest http Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 pygerrit2-2.0.4/pygerrit2/0000755€][@€/";‰0000000000013215133522016417 5ustar dpursehouse00000000000000pygerrit2-2.0.4/pygerrit2/__init__.py0000644€][@€/";‰0000001166713214126211020537 0ustar dpursehouse00000000000000# The MIT License # # Copyright 2012 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """Module to interface with Gerrit.""" def from_json(json_data, key): """Extract values from JSON data. :arg dict json_data: The JSON data :arg str key: Key to get data for. :Returns: The value of `key` from `json_data`, or None if `json_data` does not contain `key`. """ if key in json_data: return json_data[key] return None def escape_string(string): """Escape a string for use in Gerrit commands. :arg str string: The string to escape. :returns: The string with necessary escapes and surrounding double quotes so that it can be passed to any of the Gerrit commands that require double-quoted strings. """ result = string result = result.replace('\\', '\\\\') result = result.replace('"', '\\"') return '"' + result + '"' class GerritReviewMessageFormatter(object): """Helper class to format review messages that are sent to Gerrit. :arg str header: (optional) If specified, will be prepended as the first paragraph of the output message. :arg str footer: (optional) If specified, will be appended as the last paragraph of the output message. """ def __init__(self, header=None, footer=None): """See class docstring.""" self.paragraphs = [] if header: self.header = header.strip() else: self.header = "" if footer: self.footer = footer.strip() else: self.footer = "" def append(self, data): """Append the given `data` to the output. :arg data: If a list, it is formatted as a bullet list with each entry in the list being a separate bullet. Otherwise if it is a string, the string is added as a paragraph. :raises: ValueError if `data` is not a list or a string. """ if not data: return if isinstance(data, list): # First we need to clean up the data. # # Gerrit creates new bullet items when it gets newline characters # within a bullet list paragraph, so unless we remove the newlines # from the texts the resulting bullet list will contain multiple # bullets and look crappy. # # We add the '*' character on the beginning of each bullet text in # the next step, so we strip off any existing leading '*' that the # caller has added, and then strip off any leading or trailing # whitespace. _items = [x.replace("\n", " ").strip().lstrip('*').strip() for x in data] # Create the bullet list only with the items that still have any # text in them after cleaning up. _paragraph = "\n".join(["* %s" % x for x in _items if x]) if _paragraph: self.paragraphs.append(_paragraph) elif isinstance(data, str): _paragraph = data.strip() if _paragraph: self.paragraphs.append(_paragraph) else: raise ValueError('Data must be a list or a string') def is_empty(self): """Check if the formatter is empty. :Returns: True if empty, i.e. no paragraphs have been added. """ return not self.paragraphs def format(self): """Format the message parts to a string. :Returns: A string of all the message parts separated into paragraphs, with header and footer paragraphs if they were specified in the constructor. """ message = "" if self.paragraphs: if self.header: message += (self.header + '\n\n') message += "\n\n".join(self.paragraphs) if self.footer: message += ('\n\n' + self.footer) return message pygerrit2-2.0.4/pygerrit2/rest/0000755€][@€/";‰0000000000013215133522017374 5ustar dpursehouse00000000000000pygerrit2-2.0.4/pygerrit2/rest/auth.py0000644€][@€/";‰0000000404613214126451020715 0ustar dpursehouse00000000000000# The MIT License # # Copyright 2013 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """Authentication handlers.""" from requests.auth import HTTPDigestAuth, HTTPBasicAuth from requests.utils import get_netrc_auth class HTTPDigestAuthFromNetrc(HTTPDigestAuth): """HTTP Digest Auth with netrc credentials.""" def __init__(self, url): """See class docstring.""" auth = get_netrc_auth(url) if not auth: raise ValueError("netrc missing or no credentials found in netrc") username, password = auth super(HTTPDigestAuthFromNetrc, self).__init__(username, password) class HTTPBasicAuthFromNetrc(HTTPBasicAuth): """HTTP Basic Auth with netrc credentials.""" def __init__(self, url): """See class docstring.""" auth = get_netrc_auth(url) if not auth: raise ValueError("netrc missing or no credentials found in netrc") username, password = auth super(HTTPBasicAuthFromNetrc, self).__init__(username, password) pygerrit2-2.0.4/pygerrit2/rest/__init__.py0000644€][@€/";‰0000002633713214126371021523 0ustar dpursehouse00000000000000# The MIT License # # Copyright 2013 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """Interface to the Gerrit REST API.""" import json import logging import requests GERRIT_MAGIC_JSON_PREFIX = ")]}\'\n" GERRIT_AUTH_SUFFIX = "/a" def _decode_response(response): """Strip off Gerrit's magic prefix and decode a response. :returns: Decoded JSON content as a dict, or raw text if content could not be decoded as JSON. :raises: requests.HTTPError if the response contains an HTTP error status code. """ content = response.content.strip() if response.encoding: content = content.decode(response.encoding) response.raise_for_status() content_type = response.headers.get('content-type', '') if content_type.split(';')[0] != 'application/json': return content if content.startswith(GERRIT_MAGIC_JSON_PREFIX): content = content[len(GERRIT_MAGIC_JSON_PREFIX):] try: return json.loads(content) except ValueError: logging.error('Invalid json content: %s', content) raise def _merge_dict(result, overrides): """Deep-merge dictionaries. :arg dict result: The resulting dictionary :arg dict overrides: Dictionay being merged into the result :returns: The resulting dictionary """ for key in overrides: if (key in result and isinstance(result[key], dict) and isinstance(overrides[key], dict)): _merge_dict(result[key], overrides[key]) else: result[key] = overrides[key] return result class GerritRestAPI(object): """Interface to the Gerrit REST API. :arg str url: The full URL to the server, including the `http(s)://` prefix. If `auth` is given, `url` will be automatically adjusted to include Gerrit's authentication suffix. :arg auth: (optional) Authentication handler. Must be derived from `requests.auth.AuthBase`. :arg boolean verify: (optional) Set to False to disable verification of SSL certificates. """ def __init__(self, url, auth=None, verify=True): """See class docstring.""" headers = {'Accept': 'application/json', 'Accept-Encoding': 'gzip'} self.kwargs = {'auth': auth, 'verify': verify, 'headers': headers} self.url = url.rstrip('/') self.session = requests.session() if auth: if not isinstance(auth, requests.auth.AuthBase): raise ValueError('Invalid auth type; must be derived ' 'from requests.auth.AuthBase') if not self.url.endswith(GERRIT_AUTH_SUFFIX): self.url += GERRIT_AUTH_SUFFIX else: if self.url.endswith(GERRIT_AUTH_SUFFIX): self.url = self.url[: - len(GERRIT_AUTH_SUFFIX)] if not self.url.endswith('/'): self.url += '/' def make_url(self, endpoint): """Make the full url for the endpoint. :arg str endpoint: The endpoint. :returns: The full url. """ endpoint = endpoint.lstrip('/') return self.url + endpoint def get(self, endpoint, return_response=False, **kwargs): """Send HTTP GET to the endpoint. :arg str endpoint: The endpoint to send to. :arg bool return_response: If true will also return the response :returns: JSON decoded result. :raises: requests.RequestException on timeout or connection error. """ kwargs.update(self.kwargs.copy()) response = self.session.get(self.make_url(endpoint), **kwargs) decoded_response = _decode_response(response) if return_response: return decoded_response, response return decoded_response def put(self, endpoint, return_response=False, **kwargs): """Send HTTP PUT to the endpoint. :arg str endpoint: The endpoint to send to. :returns: JSON decoded result. :raises: requests.RequestException on timeout or connection error. """ args = {} if ("data" in kwargs and isinstance(kwargs["data"], dict)) or \ "json" in kwargs: _merge_dict( args, { "headers": { "Content-Type": "application/json;charset=UTF-8" } } ) _merge_dict(args, self.kwargs.copy()) _merge_dict(args, kwargs) response = self.session.put(self.make_url(endpoint), **args) decoded_response = _decode_response(response) if return_response: return decoded_response, response return decoded_response def post(self, endpoint, return_response=False, **kwargs): """Send HTTP POST to the endpoint. :arg str endpoint: The endpoint to send to. :returns: JSON decoded result. :raises: requests.RequestException on timeout or connection error. """ args = {} if ("data" in kwargs and isinstance(kwargs["data"], dict)) or \ "json" in kwargs: _merge_dict( args, { "headers": { "Content-Type": "application/json;charset=UTF-8" } } ) _merge_dict(args, self.kwargs.copy()) _merge_dict(args, kwargs) response = self.session.post(self.make_url(endpoint), **args) decoded_response = _decode_response(response) if return_response: return decoded_response, response return decoded_response def delete(self, endpoint, return_response=False, **kwargs): """Send HTTP DELETE to the endpoint. :arg str endpoint: The endpoint to send to. :returns: JSON decoded result. :raises: requests.RequestException on timeout or connection error. """ args = {} if "data" in kwargs or "json" in kwargs: _merge_dict( args, { "headers": { "Content-Type": "application/json;charset=UTF-8" } } ) _merge_dict(args, self.kwargs.copy()) _merge_dict(args, kwargs) response = self.session.delete(self.make_url(endpoint), **args) decoded_response = _decode_response(response) if return_response: return decoded_response, response return decoded_response def review(self, change_id, revision, review): """Submit a review. :arg str change_id: The change ID. :arg str revision: The revision. :arg str review: The review details as a :class:`GerritReview`. :returns: JSON decoded result. :raises: requests.RequestException on timeout or connection error. """ endpoint = "changes/%s/revisions/%s/review" % (change_id, revision) self.post(endpoint, data=str(review)) class GerritReview(object): """Encapsulation of a Gerrit review. :arg str message: (optional) Cover message. :arg dict labels: (optional) Review labels. :arg dict comments: (optional) Inline comments. """ def __init__(self, message=None, labels=None, comments=None): """See class docstring.""" self.message = message if message else "" if labels: if not isinstance(labels, dict): raise ValueError("labels must be a dict.") self.labels = labels else: self.labels = {} if comments: if not isinstance(comments, list): raise ValueError("comments must be a list.") self.comments = {} self.add_comments(comments) else: self.comments = {} def set_message(self, message): """Set review cover message. :arg str message: Cover message. """ self.message = message def add_labels(self, labels): """Add labels. :arg dict labels: Labels to add, for example Usage:: add_labels({'Verified': 1, 'Code-Review': -1}) """ self.labels.update(labels) def add_comments(self, comments): """Add inline comments. :arg dict comments: Comments to add. Usage:: add_comments([{'filename': 'Makefile', 'line': 10, 'message': 'inline message'}]) add_comments([{'filename': 'Makefile', 'range': {'start_line': 0, 'start_character': 1, 'end_line': 0, 'end_character': 5}, 'message': 'inline message'}]) """ for comment in comments: if 'filename' and 'message' in list(comment.keys()): msg = {} if 'range' in list(comment.keys()): msg = {"range": comment['range'], "message": comment['message']} elif 'line' in list(comment.keys()): msg = {"line": comment['line'], "message": comment['message']} else: continue file_comment = {comment['filename']: [msg]} if self.comments: if comment['filename'] in list(self.comments.keys()): self.comments[comment['filename']].append(msg) else: self.comments.update(file_comment) else: self.comments.update(file_comment) def __str__(self): """Return a string representation.""" review_input = {} if self.message: review_input.update({'message': self.message}) if self.labels: review_input.update({'labels': self.labels}) if self.comments: review_input.update({'comments': self.comments}) return json.dumps(review_input, sort_keys=True) pygerrit2-2.0.4/LICENSE0000644€][@€/";‰0000000223212750566703015512 0ustar dpursehouse00000000000000The MIT License Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. Copyright 2012 Sony Mobile Communications. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pygerrit2-2.0.4/requirements.txt0000644€][@€/";‰0000000004513172614200017752 0ustar dpursehouse00000000000000pbr>=0.8.0 requests>=2.10.0,<=2.18.4 pygerrit2-2.0.4/ChangeLog0000644€][@€/";‰0000002742513215133522016254 0ustar dpursehouse00000000000000CHANGES ======= 2.0.4 ----- * Add missing docstrings to silence pydocstyle errors * Replace pep257 with pydocstyle * Fix up various pydocstyle warnings, mostly about spacing * pylint: Remove useless subclassing/super() calls * pylint: Don't iterate over range(len()), use enumerate() * Minor pylint fixes, mostly spacing, indentation, capitalization, naming * Bump pyflakes from 1.5.0 to 1.6.0 * Bump flake8 from 3.3.0 to 3.5.0 * Exclude docs from analysis * Remove doc build; add generated docs folder 2.0.3 ----- * Allow requests up to version 2.18.4 * Fixes #11 Remove pypi downloads shields * Allow to use requests version 2.18.1 * Fixes #10: Fix a bug preventing file upload in PUT and POST * Fixes #7: Be less strict about requests version * Upgrade requests to 2.16.0 * Adapt to removal of Digest authentication in Gerrit 2.14 * Update links to Gerrit 2.14 * Upgrade requests to 2.14.2 * Add support to return response code * Fix docs folder exclusion in tox config * Upgrade pyflakes and flake8 to latest versions * Remove debug logs * rest: use encoding from response instead of UTF-8 * Upgrade requests to 2.13.0 * Add back the ReStructured Text version of the README 2.0.2 ----- * Fix MANIFEST.in with correct README file name * Upgrade flake8 to version 3.2.1 * Upgrade requests to 2.12.1 * Update Gerrit documentation link to latest major release * Upgrade flake8 and pyflakes to latest versions * Fix README reference in setup.cfg * Upgrade requests to 2.11.1 * Convert README to markdown 2.0.1 ----- * Upgrade requests to version 2.11.0 * Revert "Don't use requests.session()" * Upgrade requests to 2.10.0 * returning raw response for non-JSON Content-Type * making use of requests' json argument * merging request headers * Remove redundant pylint suppressions * Rewrite the README with better usage instructions * Update prerequisites * Add tox configuration * Use flake8 instead of pep8 * GerritReview: dump json content with sorted keys * Example: Don't use 'self' in query when not authenticated * Python3 compatibility fixes 2.0.0 ----- * Remove SSH support and rename to pygerrit2 * Update Gerrit documentation link to latest version * Update link to Gerrit home page 1.0.0 ----- * Use badges from shields.io * Upgrade pyflakes to 1.0.0 * Upgrade PEP-8 to 1.7.0 * Include event name in UnhandledEvent's \_\_repr\_\_ output * Upgrade Paramiko to 1.16.0 * Upgrade requests to 2.9.1 * Raise exception if the json content is invalid * Add missing docstring for Change#from\_json * Added from\_json method for Change model * Updated add\_comment() to support line ranges 0.2.11 ------ * Upgrade paramiko to version 1.15.2 * Add keepalive support to GerritSSHClient 0.2.10 ------ * Don't use requests.session() * Don't set Content-Type on PUT/POST if there is no body * Add debug log of response content 0.2.9 ----- * Upgrade requests to 2.7.0 * Allow unicode characters in gerrit commands * Update requests to 2.5.1 * Fix incorrect docstring * Upgrade pyflakes to 0.8.1 * Be less strict about the pbr version used * Restructure docstrings for better html output * Bump pep8 to 1.5.7 * Bump paramiko to 1.15.1 and pycrypto to 2.6.1 * Bump requests to 2.4.3 * Fix documentation build * Bump python requests version to 2.4.1 0.2.8 ----- * Add inline review support * Replace deprecated assertEquals() with assertEqual() * Update PEP257 to 0.2.4 * Update pyflakes to 0.7.3 0.2.7 ----- * Add message formatter class * Upgrade requests to 2.3.0 * Fix paramiko link in the README file 0.2.6 ----- * Add support for ProxyCommand in GerritSSHClient * Upgrade paramiko to 1.14.0 * Revert "Remove support for Gerrit over ssh" * Update the README * Upgdade requests to 2.2.1 * Include full description in the setup config * Set Content-Type header on PUT and POST requests * Set HTTP headers to reduce data transfer time * Migrate setup to pbr * Stop using pylint * Remove support for Gerrit over ssh 0.2.5 ----- * Version 0.2.5 * Fix up usage of argparse in examples * Update examples to use argparse instead of optparse * Fix for hanging connections 0.2.4 ----- * Version 0.2.4 * Accept kwargs for request functions * Correct Gerrit documentation link * Add badges to the README * Fix README markup to work properly on Gitlab 0.2.3 ----- * Version 0.2.3 * Use find\_packages() in setup 0.2.2 ----- * Bump version to 0.2.2 * Release notes for 0.2.2 * Add user in the Approval object * Add support for CurrentPatchSet * Include change status in Change object * Always set sortkey in Change object * Handle errors in REST API example * Simplify REST API error handling * Add username field in the account data * Add method to run a gerrit command from the client * Add method to get username and Gerrit version * Fix NameError in REST API example * Bump to requests 2.0.1 also in setup.py * Bump requests version up to 2.0.1 0.2.1 ----- * Update change log for 0.2.1 * Document the HTTP password configuration for REST API usage * Reword the prerequisites in the readme * Separate prerequisites and configuration in the readme 0.2.0 ----- * Bump version to 0.2.0 * Update change log for 0.2.0 * Suppress pylint "Unable to import" error * Suppress pylint warning about catching too general exception * Fix pylint error in rest\_example.py * Change abandoned and restored events don't have a patchset field * Add myself to authors * Add an authors list that contributors can add themselves to * Fix indentation * Ensure errors in json parsing doesn't leave everything in a broken state * Reason is optional in abandon and restore changes * Return sortKey from query result, to allow resuming query * Update the README to mention the REST API * Avoid busy loop when receiving incoming stream data * Add more detailed examples of SSH interface usage in the README * Update the README notes about ssh configuration * Add support for Kerberos authentication in example script * Allow client to disable SSL certificate verification * Add REST API example * Refactor the authentication handling * Add MANIFEST to .gitignore * Add method to build url from endpoint 0.1.1 ----- * Bump version to 0.1.1 * Add changelog * Make get, put, post and delete REST methods public * Fix #10: Allow to manually specify ssh username and port * Completely refactor the stream event handling * Add missing \_\_repr\_\_ methods on ErrorEvent and UnhandledEvent * Fix initialisation of error event * Fix #11: correct handling of \`identityfile\` in the ssh config * Allow example script to continue if errors are received * Fix #9: Add a bit more detail in the documentation * Fix #8: Support the "topic-changed" stream event * Fix #7: Support the "reviewer-added" stream event * Fix #6: Support the "merge-failed" stream event * Fix #5: Move json parsing and error handling into the event factory * Improved logging in the example script * Fix #3: Don't treat unhandled event types as errors * Fix #4: Keep event's raw json data in the event object * Add \_\_repr\_\_ methods on event and model classes * Remove redundant \`exec\_command\` method * Fix #2: Establish SSH connection in a thread-safe way * Fix #1: Use select.select() instead of select.poll() * Fix authentication setup * Add handling of HTTP error status codes in responses * Add support for HTTP digest authentication * Initial implementation of Gerrit REST API interface * Disable W0142 'Used \* or \*\* magic' in the Pylint configuration 0.1.0 ----- * Bump version to 0.1.0 * Add Python trove classifiers to the setup * Include full license text in the license parameter of setup * Add long description in the setup * Add MANIFEST.in file * Improve formatting in the README file * Rename README to README.rst * Add make target and rules to ensure environment setup tools are OK * Add a make target to build the API documentation zip archive * Don't put copyright and license headers in Python module docstrings * Add a make target to build the source distribution * Add a make target to check for clean git status * Add a make target to check that the git is tagged properly * Add generation of package documentation * Separate test dependency installation into its own build target * Separate environment intialisation into its own build target * Make the shebangs consistent * Add pylint check in the Makefile * Fix pylint warnings * Add PEP-257 conformance check in the Makefile * Add a unit test to ensure dependency package version consistency * Separate package dependencies from verification/test dependencies * Bump paramiko dependency to version 1.11.0 * Fix relative imports * Fix one more PEP-257 violation * Add a \`clean\` target in the Makefile * Add PEP-8 conformance check in the Makefile * Add pyflakes check in the Makefile * Add a Makefile to install dependencies and run unit tests * Only allow strings as query terms and commands * Don't hard code version in setup.py * Include command in GerritCommandResult * Don't open ssh connection until needed * Fix UnboundLocalError when stream-event connection fails * Add missing docstrings to conform to PEP-257 * Explicitly depend on paramiko v1.7.6 * Move requirements.txt to the root * Add requirements file 0.0.7 ----- * Bump to version 0.0.7 * Better error message in example script * Rename the readme and license files 0.0.6 ----- * Bump to version 0.0.6 * Better error handling in the example script * Move license to its own file and add basic documentation * Use correct URL in setup information * Handle socket connection error in SSH client * Python 2.6 style exception handling * Add Eclipse and Pydev project files 0.0.5 ----- * Bump to version 0.0.5 * Revert "Basic thread-safeness in the SSH client" * Encapsulate the SSH command results in a class * Update pylint config to work with version 0.26 * Only add query result lines to returned data * Handle JSON errors in query results * Refactor getting the Gerrit version * Reduce nested \`if\` blocks in stream handling * Add pylint configuration file * More error handling improvements in stream * Handle exception when running ssh command * Fix pylint warnings in stream.py * Basic thread-safeness in the SSH client * More exception handling in stream 0.0.4 ----- * Bump version to 0.0.4 * Fix hostname in unit tests * Get Gerrit version during client initialisation * Add query functionality * Add \_\_str\_\_ on event base class * Move SSH client from stream class to main client class * Add license information * Simplify the unit test structure 0.0.3 ----- * Bump version to 0.0.3 * Add an example of how the Gerrit client class is used * Wait for thread to complete when stopping stream * Handle errors when reading event stream * Fix event registration from other module * Refactor event stream handling to use SSH client * Handle invalid port in ssh config * Add GerritSSHClient * Add initial stub of GerritClient class * Inject event name into event classes with decorator * Revert "GerritEventFactory should be a singleton" * GerritEventFactory should be a singleton * Pass input stream in the constructor of GerritStream * Add support for topic name in change attribute * Add event factory and refactor event dispatching * Events unit tests should also test event dispatching * Add helper methods for initialisation from json data * Add setup.py 0.0.2 ----- * Tidy up docstrings to follow the PEP-257 docstring convention * Refactor into submodules * Add .settings to .gitignore * Remove check for supported approval types 0.0.1 ----- * Add .gitignore * Add Makefile and script for unit tests * Add unit tests for event handling * Reason is missing in ChangeRestoredEvent * \`comment-added\` event can have multiple approvals * Add support for the \`draft-published\` event * Minor refactoring of unit tests * Fixing PEP-8 and pylint warnings * Python cleanup: inherit from object * Undefined variable in GerritCommentAddedEvent * Check for callable event handler on attach * Minor refactoring and adding initial unit tests * Initial version of class for handling Gerrit stream events * Initial empty commit pygerrit2-2.0.4/AUTHORS0000644€][@€/";‰0000000166413215133522015547 0ustar dpursehouse00000000000000Alexander D. Kanevskiy Andrey Devyatkin Benjamin Foster Chad Horohoe Chris Packham Christopher Zee David Pursehouse David Pursehouse David Pursehouse Ernst Sjostrand Fabien Boucher Gabriel FΓ©ron George Peristerakis Jens Andersen Johannes Richter Justin Simon Marcin PΕ‚onka Nikki Heald Peter Theckanath Vasiliy Kleschov Vineet Naik dependabot[bot] pygerrit2-2.0.4/Makefile0000644€][@€/";‰0000000604113214125242016130 0ustar dpursehouse00000000000000# The MIT License # # Copyright 2013 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. PWD := $(shell pwd) VERSION := $(shell git describe) VIRTUALENV := $(shell which virtualenv) ifeq ($(wildcard $(VIRTUALENV)),) $(error virtualenv must be available) endif PIP := $(shell which pip) ifeq ($(wildcard $(PIP)),) $(error pip must be available) endif REQUIRED_VIRTUALENV ?= 1.10 VIRTUALENV_OK := $(shell expr `virtualenv --version | \ cut -f2 -d' '` \>= $(REQUIRED_VIRTUALENV)) all: test test: clean unittests pyflakes pep8 pydocstyle sdist: valid-virtualenv test bash -c "\ source ./pygerrit2env/bin/activate && \ python setup.py sdist" valid-virtualenv: ifeq ($(VIRTUALENV_OK),0) $(error virtualenv version $(REQUIRED_VIRTUALENV) or higher is needed) endif pydocstyle: testenvsetup bash -c "\ source ./pygerrit2env/bin/activate && \ git ls-files | grep \"\.py$$\" | grep -v docs | xargs pydocstyle" pep8: testenvsetup bash -c "\ source ./pygerrit2env/bin/activate && \ git ls-files | grep \"\.py$$\" | grep -v docs | xargs flake8 --max-line-length 80" pyflakes: testenvsetup bash -c "\ source ./pygerrit2env/bin/activate && \ git ls-files | grep \"\.py$$\" | grep -v docs | xargs pyflakes" unittests: testenvsetup bash -c "\ source ./pygerrit2env/bin/activate && \ python unittests.py" testenvsetup: envsetup bash -c "\ source ./pygerrit2env/bin/activate && \ pip install --upgrade -r test_requirements.txt" docenvsetup: envsetup bash -c "\ source ./pygerrit2env/bin/activate && \ pip install --upgrade -r doc_requirements.txt" envsetup: envinit bash -c "\ source ./pygerrit2env/bin/activate && \ pip install --upgrade -r requirements.txt" envinit: bash -c "[ -e ./pygerrit2env/bin/activate ] || virtualenv --system-site-packages ./pygerrit2env" clean: @find . -type f -name "*.pyc" -exec rm -f {} \; @rm -rf pygerrit2env pygerrit2.egg-info build dist pygerrit2-2.0.4/pygerrit2.egg-info/0000755€][@€/";‰0000000000013215133522020111 5ustar dpursehouse00000000000000pygerrit2-2.0.4/pygerrit2.egg-info/PKG-INFO0000644€][@€/";‰0000001060013215133522021203 0ustar dpursehouse00000000000000Metadata-Version: 1.1 Name: pygerrit2 Version: 2.0.4 Summary: Client library for interacting with Gerrit's REST API Home-page: https://github.com/dpursehouse/pygerrit2 Author: David Pursehouse Author-email: david.pursehouse@gmail.com License: The MIT License Description-Content-Type: UNKNOWN Description: Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API ============================================================================= .. image:: https://img.shields.io/pypi/v/pygerrit2.png .. image:: https://img.shields.io/pypi/l/pygerrit2.png Pygerrit2 provides a simple interface for clients to interact with `Gerrit Code Review`_ via the REST API. Prerequisites ------------- Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 is experimental. Pygerrit2 depends on the `requests`_ library. Installation ------------ To install pygerrit2, simply:: $ pip install pygerrit2 Usage ----- This simple example shows how to get the user's open changes. Authentication to Gerrit is done via HTTP Basic authentication, using an explicitly given username and password:: >>> from requests.auth import HTTPBasicAuth >>> from pygerrit2.rest import GerritRestAPI >>> auth = HTTPBasicAuth('username', 'password') >>> rest = GerritRestAPI(url='http://review.example.net', auth=auth) >>> changes = rest.get("/changes/?q=owner:self%20status:open") Note that is is not necessary to add the ``/a/`` prefix on the endpoint URLs. This is automatically added when the API is instantiated with an authentication object. If the user's HTTP username and password are defined in the ``.netrc`` file:: machine review.example.net login MyUsername password MyPassword then it is possible to authenticate with those credentials:: >>> from pygerrit2.rest import GerritRestAPI >>> from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc >>> url = 'http://review.example.net' >>> auth = HTTPBasicAuthFromNetrc(url=url) >>> rest = GerritRestAPI(url=url, auth=auth) >>> changes = rest.get("/changes/?q=owner:self%20status:open") Note that the HTTP password is not the same as the SSH password. For instructions on how to obtain the HTTP password, refer to Gerrit's `HTTP upload settings`_ documentation. Also note that in Gerrit version 2.14, support for HTTP Digest authentication was removed and only HTTP Basic authentication is supported. When using pygerrit2 against an earlier Gerrit version, it may be necessary to replace the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. Refer to the `example`_ script for a full working example. Copyright and License --------------------- Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. Copyright 2012 Sony Mobile Communications. All rights reserved. Copyright 2016 David Pursehouse. All rights reserved. Licensed under The MIT License. Please refer to the `LICENSE`_ file for full license details. .. _`Gerrit Code Review`: https://gerritcodereview.com/ .. _`requests`: https://github.com/kennethreitz/requests .. _example: https://github.com/dpursehouse/pygerrit2/blob/master/example.py .. _`HTTP upload settings`: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http .. _LICENSE: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE Keywords: gerrit rest http Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 pygerrit2-2.0.4/pygerrit2.egg-info/not-zip-safe0000644€][@€/";‰0000000000113215133522022337 0ustar dpursehouse00000000000000 pygerrit2-2.0.4/pygerrit2.egg-info/SOURCES.txt0000644€][@€/";‰0000000077513215133522022006 0ustar dpursehouse00000000000000AUTHORS ChangeLog LICENSE MANIFEST.in Makefile README.md README.rst example.py requirements.txt setup.cfg setup.py test_requirements.txt tox.ini unittests.py docs/Makefile docs/conf.py docs/index.rst docs/make.bat pygerrit2/__init__.py pygerrit2.egg-info/PKG-INFO pygerrit2.egg-info/SOURCES.txt pygerrit2.egg-info/dependency_links.txt pygerrit2.egg-info/not-zip-safe pygerrit2.egg-info/pbr.json pygerrit2.egg-info/requires.txt pygerrit2.egg-info/top_level.txt pygerrit2/rest/__init__.py pygerrit2/rest/auth.pypygerrit2-2.0.4/pygerrit2.egg-info/pbr.json0000644€][@€/";‰0000000005713215133522021571 0ustar dpursehouse00000000000000{"git_version": "5eb119b", "is_release": false}pygerrit2-2.0.4/pygerrit2.egg-info/requires.txt0000644€][@€/";‰0000000004513215133522022510 0ustar dpursehouse00000000000000pbr>=0.8.0 requests<=2.18.4,>=2.10.0 pygerrit2-2.0.4/pygerrit2.egg-info/top_level.txt0000644€][@€/";‰0000000001213215133522022634 0ustar dpursehouse00000000000000pygerrit2 pygerrit2-2.0.4/pygerrit2.egg-info/dependency_links.txt0000644€][@€/";‰0000000000113215133522024157 0ustar dpursehouse00000000000000 pygerrit2-2.0.4/MANIFEST.in0000644€][@€/";‰0000000005313017476112016231 0ustar dpursehouse00000000000000include README.rst LICENSE requirements.txtpygerrit2-2.0.4/docs/0000755€][@€/";‰0000000000013215133522015420 5ustar dpursehouse00000000000000pygerrit2-2.0.4/docs/index.rst0000644€][@€/";‰0000000067313173053640017274 0ustar dpursehouse00000000000000.. pygerrit2 documentation master file, created by sphinx-quickstart on Sun Oct 22 17:32:32 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pygerrit2's documentation! ===================================== .. toctree:: :maxdepth: 2 :caption: Contents: Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pygerrit2-2.0.4/docs/Makefile0000644€][@€/";‰0000000114113173053640017062 0ustar dpursehouse00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = python -msphinx SPHINXPROJ = pygerrit2 SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)pygerrit2-2.0.4/docs/conf.py0000644€][@€/";‰0000001207313214124673016730 0ustar dpursehouse00000000000000# -*- coding: utf-8 -*- # # pygerrit2 documentation build configuration file, created by # sphinx-quickstart on Sun Oct 22 17:32:32 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'pygerrit2' copyright = u'2017, David Pursehouse' author = u'David Pursehouse' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'' # The full version, including alpha/beta/rc tags. release = u'' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { '**': [ 'about.html', 'navigation.html', 'relations.html', # needs 'show_related': True theme option to display 'searchbox.html', 'donate.html', ] } # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'pygerrit2doc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'pygerrit2.tex', u'pygerrit2 Documentation', u'David Pursehouse', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'pygerrit2', u'pygerrit2 Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'pygerrit2', u'pygerrit2 Documentation', author, 'pygerrit2', 'One line description of project.', 'Miscellaneous'), ] pygerrit2-2.0.4/docs/make.bat0000644€][@€/";‰0000000144713173053640017040 0ustar dpursehouse00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=python -msphinx ) set SOURCEDIR=. set BUILDDIR=_build set SPHINXPROJ=pygerrit2 if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The Sphinx module was not found. Make sure you have Sphinx installed, echo.then set the SPHINXBUILD environment variable to point to the full echo.path of the 'sphinx-build' executable. Alternatively you may add the echo.Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd pygerrit2-2.0.4/unittests.py0000744€][@€/";‰0000001766513214126642017130 0ustar dpursehouse00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # The MIT License # # Copyright 2012 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """Unit tests for the Pygerrit2 helper methods.""" import unittest from pygerrit2 import GerritReviewMessageFormatter from pygerrit2.rest import GerritReview, _merge_dict EXPECTED_TEST_CASE_FIELDS = ['header', 'footer', 'paragraphs', 'result'] TEST_CASES = [ {'header': None, 'footer': None, 'paragraphs': [], 'result': ""}, {'header': "Header", 'footer': "Footer", 'paragraphs': [], 'result': ""}, {'header': None, 'footer': None, 'paragraphs': ["Test"], 'result': "Test"}, {'header': None, 'footer': None, 'paragraphs': ["Test", "Test"], 'result': "Test\n\nTest"}, {'header': "Header", 'footer': None, 'paragraphs': ["Test"], 'result': "Header\n\nTest"}, {'header': "Header", 'footer': None, 'paragraphs': ["Test", "Test"], 'result': "Header\n\nTest\n\nTest"}, {'header': "Header", 'footer': "Footer", 'paragraphs': ["Test", "Test"], 'result': "Header\n\nTest\n\nTest\n\nFooter"}, {'header': "Header", 'footer': "Footer", 'paragraphs': [["One"]], 'result': "Header\n\n* One\n\nFooter"}, {'header': "Header", 'footer': "Footer", 'paragraphs': [["One", "Two"]], 'result': "Header\n\n* One\n* Two\n\nFooter"}, {'header': "Header", 'footer': "Footer", 'paragraphs': ["Test", ["One"], "Test"], 'result': "Header\n\nTest\n\n* One\n\nTest\n\nFooter"}, {'header': "Header", 'footer': "Footer", 'paragraphs': ["Test", ["One", "Two"], "Test"], 'result': "Header\n\nTest\n\n* One\n* Two\n\nTest\n\nFooter"}, {'header': "Header", 'footer': "Footer", 'paragraphs': ["Test", "Test", ["One"]], 'result': "Header\n\nTest\n\nTest\n\n* One\n\nFooter"}, {'header': None, 'footer': None, 'paragraphs': [["* One", "* Two"]], 'result': "* One\n* Two"}, {'header': None, 'footer': None, 'paragraphs': [["* One ", " * Two "]], 'result': "* One\n* Two"}, {'header': None, 'footer': None, 'paragraphs': [["*", "*"]], 'result': ""}, {'header': None, 'footer': None, 'paragraphs': [["", ""]], 'result': ""}, {'header': None, 'footer': None, 'paragraphs': [[" ", " "]], 'result': ""}, {'header': None, 'footer': None, 'paragraphs': [["* One", " ", "* Two"]], 'result': "* One\n* Two"}] class TestMergeDict(unittest.TestCase): """Tests for the `_merge_dict` method.""" def test_merge_into_empty_dict(self): """Test merging into an empty dict.""" dct = {} _merge_dict(dct, {'a': 1, 'b': 2}) self.assertEqual(dct, {'a': 1, 'b': 2}) def test_merge_flat(self): """Test merging a flat dict.""" dct = {'c': 3} _merge_dict(dct, {'a': 1, 'b': 2}) self.assertEqual(dct, {'a': 1, 'b': 2, 'c': 3}) def test_merge_with_override(self): """Test merging a dict and overriding values.""" dct = {'a': 1} _merge_dict(dct, {'a': 0, 'b': 2}) self.assertEqual(dct, {'a': 0, 'b': 2}) def test_merge_two_levels(self): """Test merging a dict with two levels.""" dct = { 'a': { 'A': 1, 'AA': 2, }, 'b': { 'B': 1, 'BB': 2, }, } overrides = { 'a': { 'AAA': 3, }, 'b': { 'BBB': 3, }, } _merge_dict(dct, overrides) self.assertEqual( dct, { 'a': { 'A': 1, 'AA': 2, 'AAA': 3, }, 'b': { 'B': 1, 'BB': 2, 'BBB': 3, }, } ) class TestGerritReviewMessageFormatter(unittest.TestCase): """Test that the GerritReviewMessageFormatter class behaves properly.""" def _check_test_case_fields(self, test_case, i): for field in EXPECTED_TEST_CASE_FIELDS: self.assertTrue(field in test_case, "field '%s' not present in test case #%d" % (field, i)) self.assertTrue( isinstance(test_case['paragraphs'], list), "'paragraphs' field is not a list in test case #%d" % i) def test_is_empty(self): """Test if message is empty for missing header and footer.""" fmt = GerritReviewMessageFormatter(header=None, footer=None) self.assertTrue(fmt.is_empty()) fmt.append(['test']) self.assertFalse(fmt.is_empty()) def test_message_formatting(self): """Test message formatter for different test cases.""" for i, test_case in enumerate(TEST_CASES): self._check_test_case_fields(test_case, i) fmt = GerritReviewMessageFormatter(header=test_case['header'], footer=test_case['footer']) for paragraph in test_case['paragraphs']: fmt.append(paragraph) msg = fmt.format() self.assertEqual(msg, test_case['result'], "Formatted message does not match expected " "result in test case #%d:\n[%s]" % (i, msg)) class TestGerritReview(unittest.TestCase): """Test that the GerritReview class behaves properly.""" def test_str(self): """Test for str function.""" obj = GerritReview() self.assertEqual(str(obj), '{}') obj2 = GerritReview(labels={'Verified': 1, 'Code-Review': -1}) self.assertEqual( str(obj2), '{"labels": {"Code-Review": -1, "Verified": 1}}') obj3 = GerritReview(comments=[{'filename': 'Makefile', 'line': 10, 'message': 'test'}]) self.assertEqual( str(obj3), '{"comments": {"Makefile": [{"line": 10, "message": "test"}]}}') obj4 = GerritReview(labels={'Verified': 1, 'Code-Review': -1}, comments=[{'filename': 'Makefile', 'line': 10, 'message': 'test'}]) self.assertEqual( str(obj4), '{"comments": {"Makefile": [{"line": 10, "message": "test"}]},' ' "labels": {"Code-Review": -1, "Verified": 1}}') obj5 = GerritReview(comments=[ {'filename': 'Makefile', 'line': 15, 'message': 'test'}, {'filename': 'Make', 'line': 10, 'message': 'test1'} ]) self.assertEqual( str(obj5), '{"comments": {"Make": [{"line": 10, "message": "test1"}],' ' "Makefile": [{"line": 15, "message": "test"}]}}') if __name__ == '__main__': unittest.main() pygerrit2-2.0.4/README.md0000644€][@€/";‰0000000553213131373546015765 0ustar dpursehouse00000000000000# Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API ![Version](https://img.shields.io/pypi/v/pygerrit2.png) ![License](https://img.shields.io/pypi/l/pygerrit2.png) Pygerrit2 provides a simple interface for clients to interact with [Gerrit Code Review][gerrit] via the REST API. ## Prerequisites Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 is experimental. Pygerrit2 depends on the [requests library][requests]. ## Installation To install pygerrit2, simply: ```bash $ pip install pygerrit2 ``` ## Usage This simple example shows how to get the user's open changes. Authentication to Gerrit is done via HTTP Basic authentication, using an explicitly given username and password: ```python from requests.auth import HTTPBasicAuth from pygerrit2.rest import GerritRestAPI auth = HTTPBasicAuth('username', 'password') rest = GerritRestAPI(url='http://review.example.net', auth=auth) changes = rest.get("/changes/?q=owner:self%20status:open") ``` Note that is is not necessary to add the `/a/` prefix on the endpoint URLs. This is automatically added when the API is instantiated with an authentication object. If the user's HTTP username and password are defined in the `.netrc` file: ```bash machine review.example.net login MyUsername password MyPassword ``` then it is possible to authenticate with those credentials: ```python from pygerrit2.rest import GerritRestAPI from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc url = 'http://review.example.net' auth = HTTPBasicAuthFromNetrc(url=url) rest = GerritRestAPI(url=url, auth=auth) changes = rest.get("/changes/?q=owner:self%20status:open") ``` Note that the HTTP password is not the same as the SSH password. For instructions on how to obtain the HTTP password, refer to Gerrit's [HTTP upload settings documentation][settings]. Also note that in Gerrit version 2.14, support for HTTP Digest authentication was removed and only HTTP Basic authentication is supported. When using pygerrit2 against an earlier Gerrit version, it may be necessary to replace the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. Refer to the [example script][example] for a full working example. # Copyright and License Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. Copyright 2012 Sony Mobile Communications. All rights reserved. Copyright 2016 David Pursehouse. All rights reserved. Licensed under The MIT License. Please refer to the [LICENSE file][license] for full license details. [gerrit]: https://gerritcodereview.com/ [requests]: https://github.com/kennethreitz/requests [example]: https://github.com/dpursehouse/pygerrit2/blob/master/example.py [settings]: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http [license]: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE pygerrit2-2.0.4/setup.py0000755€][@€/";‰0000000263113214124673016215 0ustar dpursehouse00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # The MIT License # # Copyright 2012 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """Client library for interacting with Gerrit.""" import setuptools def _main(): setuptools.setup( packages=setuptools.find_packages(), setup_requires=['pbr'], pbr=True) if __name__ == "__main__": _main() pygerrit2-2.0.4/example.py0000755€][@€/";‰0000001122613214124673016510 0ustar dpursehouse00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # The MIT License # # Copyright 2013 Sony Mobile Communications. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """Example of using the Gerrit client REST API.""" import argparse import logging import sys from requests.auth import HTTPBasicAuth, HTTPDigestAuth from requests.exceptions import RequestException try: from requests_kerberos import HTTPKerberosAuth, OPTIONAL _KERBEROS_SUPPORT = True except ImportError: _KERBEROS_SUPPORT = False from pygerrit2.rest import GerritRestAPI from pygerrit2.rest.auth import HTTPDigestAuthFromNetrc, HTTPBasicAuthFromNetrc def _main(): descr = 'Send request using Gerrit HTTP API' parser = argparse.ArgumentParser( description=descr, formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('-g', '--gerrit-url', dest='gerrit_url', required=True, help='gerrit server url') parser.add_argument('-b', '--basic-auth', dest='basic_auth', action='store_true', help='(deprecated) use basic auth instead of digest') parser.add_argument('-d', '--digest-auth', dest='digest_auth', action='store_true', help='use digest auth instead of basic') if _KERBEROS_SUPPORT: parser.add_argument('-k', '--kerberos-auth', dest='kerberos_auth', action='store_true', help='use kerberos auth') parser.add_argument('-u', '--username', dest='username', help='username') parser.add_argument('-p', '--password', dest='password', help='password') parser.add_argument('-n', '--netrc', dest='netrc', action='store_true', help='Use credentials from netrc') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='enable verbose (debug) logging') options = parser.parse_args() level = logging.DEBUG if options.verbose else logging.INFO logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=level) if _KERBEROS_SUPPORT and options.kerberos_auth: if options.username or options.password \ or options.basic_auth or options.netrc: parser.error("--kerberos-auth may not be used together with " "--username, --password, --basic-auth or --netrc") auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL) elif options.username and options.password: if options.netrc: logging.warning("--netrc option ignored") if options.digest_auth: auth = HTTPDigestAuth(options.username, options.password) else: auth = HTTPBasicAuth(options.username, options.password) elif options.netrc: if options.digest_auth: auth = HTTPDigestAuthFromNetrc(url=options.gerrit_url) else: auth = HTTPBasicAuthFromNetrc(url=options.gerrit_url) else: auth = None rest = GerritRestAPI(url=options.gerrit_url, auth=auth) try: query = ["status:open"] if auth: query += ["owner:self"] else: query += ["limit:10"] changes = rest.get("/changes/?q=%s" % "%20".join(query)) logging.info("%d changes", len(changes)) for change in changes: logging.info(change['change_id']) except RequestException as err: logging.error("Error: %s", str(err)) if __name__ == "__main__": sys.exit(_main()) pygerrit2-2.0.4/tox.ini0000644€][@€/";‰0000000112213061672673016015 0ustar dpursehouse00000000000000[tox] minversion = 2.1.1 envlist = pep8, py35, py27, py26 [testenv] setenv = VIRTUAL_ENV={envdir} SUBUNIT_FORMATTER=tee testr_subunit_log OS_STDOUT_NOCAPTURE=False LANG=en_US.UTF-8 usedevelop = True install_command = pip install {opts} {packages} deps = -r{toxinidir}/test_requirements.txt commands = python unittests.py [testenv:pep8] commands = flake8 [testenv:pyflakes] deps = pyflakes commands = pyflakes pygerrit2 unittests.py example.py setup.py [flake8] max-line-length = 80 show-source = True exclude = .venv,.tox,dist,docs,build,*.egg,.test,pygerrit2env pygerrit2-2.0.4/setup.cfg0000644€][@€/";‰0000000116013215133522016307 0ustar dpursehouse00000000000000[metadata] name = pygerrit2 summary = Client library for interacting with Gerrit's REST API author = David Pursehouse author_email = david.pursehouse@gmail.com home-page = https://github.com/dpursehouse/pygerrit2 license = The MIT License description-file = README.rst keywords = gerrit rest http classifiers = Development Status :: 3 - Alpha Environment :: Console Intended Audience :: Developers License :: OSI Approved :: MIT License Natural Language :: English Programming Language :: Python Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 [egg_info] tag_build = tag_date = 0 pygerrit2-2.0.4/README.rst0000644€][@€/";‰0000000600213131373670016164 0ustar dpursehouse00000000000000Pygerrit2 - Client library for interacting with Gerrit Code Review's REST API ============================================================================= .. image:: https://img.shields.io/pypi/v/pygerrit2.png .. image:: https://img.shields.io/pypi/l/pygerrit2.png Pygerrit2 provides a simple interface for clients to interact with `Gerrit Code Review`_ via the REST API. Prerequisites ------------- Pygerrit2 is compatible with Python 2.6 and Python 2.7. Support for Python 3 is experimental. Pygerrit2 depends on the `requests`_ library. Installation ------------ To install pygerrit2, simply:: $ pip install pygerrit2 Usage ----- This simple example shows how to get the user's open changes. Authentication to Gerrit is done via HTTP Basic authentication, using an explicitly given username and password:: >>> from requests.auth import HTTPBasicAuth >>> from pygerrit2.rest import GerritRestAPI >>> auth = HTTPBasicAuth('username', 'password') >>> rest = GerritRestAPI(url='http://review.example.net', auth=auth) >>> changes = rest.get("/changes/?q=owner:self%20status:open") Note that is is not necessary to add the ``/a/`` prefix on the endpoint URLs. This is automatically added when the API is instantiated with an authentication object. If the user's HTTP username and password are defined in the ``.netrc`` file:: machine review.example.net login MyUsername password MyPassword then it is possible to authenticate with those credentials:: >>> from pygerrit2.rest import GerritRestAPI >>> from pygerrit2.rest.auth import HTTPBasicAuthFromNetrc >>> url = 'http://review.example.net' >>> auth = HTTPBasicAuthFromNetrc(url=url) >>> rest = GerritRestAPI(url=url, auth=auth) >>> changes = rest.get("/changes/?q=owner:self%20status:open") Note that the HTTP password is not the same as the SSH password. For instructions on how to obtain the HTTP password, refer to Gerrit's `HTTP upload settings`_ documentation. Also note that in Gerrit version 2.14, support for HTTP Digest authentication was removed and only HTTP Basic authentication is supported. When using pygerrit2 against an earlier Gerrit version, it may be necessary to replace the `HTTPBasic...` classes with the corresponding `HTTPDigest...` versions. Refer to the `example`_ script for a full working example. Copyright and License --------------------- Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. Copyright 2012 Sony Mobile Communications. All rights reserved. Copyright 2016 David Pursehouse. All rights reserved. Licensed under The MIT License. Please refer to the `LICENSE`_ file for full license details. .. _`Gerrit Code Review`: https://gerritcodereview.com/ .. _`requests`: https://github.com/kennethreitz/requests .. _example: https://github.com/dpursehouse/pygerrit2/blob/master/example.py .. _`HTTP upload settings`: https://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-upload.html#http .. _LICENSE: https://github.com/dpursehouse/pygerrit2/blob/master/LICENSE