# 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.
""" Gerrit client interface. """
from json import JSONDecoder
from Queue import Queue, Empty, Full
from . import escape_string
from .error import GerritError
from .events import GerritEventFactory
from .models import Change
from .ssh import GerritSSHClient
from .stream import GerritStream
[docs]class GerritClient(object):
""" Gerrit client interface.
:arg str host: The hostname.
:arg str username: (optional) The username to use when connecting.
:arg str port: (optional) The port number to connect to.
:arg int keepalive: (optional) Keepalive interval in seconds.
"""
def __init__(self, host, username=None, port=None, keepalive=None):
self._factory = GerritEventFactory()
self._events = Queue()
self._stream = None
self.keepalive = keepalive
self._ssh_client = GerritSSHClient(host,
username=username,
port=port,
keepalive=keepalive)
[docs] def gerrit_version(self):
""" Get the Gerrit version.
:Returns: The version of Gerrit that is connected to, as a string.
"""
return self._ssh_client.get_remote_version()
[docs] def gerrit_info(self):
""" Get connection information.
:Returns: A tuple of the username, and version of Gerrit that is
connected to.
"""
return self._ssh_client.get_remote_info()
[docs] def run_command(self, command):
""" Run a command.
:arg str command: The command to run.
:Return: The result as a string.
:Raises: `ValueError` if `command` is not a string.
"""
if not isinstance(command, basestring):
raise ValueError("command must be a string")
return self._ssh_client.run_gerrit_command(command)
[docs] def query(self, term):
""" Run a query.
:arg str term: The query term to run.
:Returns: A list of results as :class:`pygerrit.models.Change` objects.
:Raises: `ValueError` if `term` is not a string.
"""
results = []
command = ["query", "--current-patch-set", "--all-approvals",
"--format JSON", "--commit-message"]
if not isinstance(term, basestring):
raise ValueError("term must be a string")
command.append(escape_string(term))
result = self._ssh_client.run_gerrit_command(" ".join(command))
decoder = JSONDecoder()
for line in result.stdout.read().splitlines():
# Gerrit's response to the query command contains one or more
# lines of JSON-encoded strings. The last one is a status
# dictionary containing the key "type" whose value indicates
# whether or not the operation was successful.
# According to http://goo.gl/h13HD it should be safe to use the
# presence of the "type" key to determine whether the dictionary
# represents a change or if it's the query status indicator.
try:
data = decoder.decode(line)
except ValueError as err:
raise GerritError("Query returned invalid data: %s", err)
if "type" in data and data["type"] == "error":
raise GerritError("Query error: %s" % data["message"])
elif "project" in data:
results.append(Change(data))
return results
[docs] def start_event_stream(self):
""" Start streaming events from `gerrit stream-events`. """
if not self._stream:
self._stream = GerritStream(self, ssh_client=self._ssh_client)
self._stream.start()
[docs] def stop_event_stream(self):
""" Stop streaming events from `gerrit stream-events`."""
if self._stream:
self._stream.stop()
self._stream.join()
self._stream = None
with self._events.mutex:
self._events.queue.clear()
[docs] def get_event(self, block=True, timeout=None):
""" Get the next event from the queue.
:arg boolean block: Set to True to block if no event is available.
:arg seconds timeout: Timeout to wait if no event is available.
:Returns: The next event as a :class:`pygerrit.events.GerritEvent`
instance, or `None` if:
- `block` is False and there is no event available in the queue, or
- `block` is True and no event is available within the time
specified by `timeout`.
"""
try:
return self._events.get(block, timeout)
except Empty:
return None
[docs] def put_event(self, data):
""" Create event from `data` and add it to the queue.
:arg json data: The JSON data from which to create the event.
:Raises: :class:`pygerrit.error.GerritError` if the queue is full, or
the factory could not create the event.
"""
try:
event = self._factory.create(data)
self._events.put(event)
except Full:
raise GerritError("Unable to add event: queue is full")