Skip to content

Commit

Permalink
Implement 'pubsub.client.Client'.
Browse files Browse the repository at this point in the history
Wraps:

- 'pubsub.api' functions
- 'pubsub.topic.Topic' (via a factory / proxy)
- 'pubsub.subscription.Subscription' (via a factory / proxy)

See:
#861 (comment)
  • Loading branch information
tseaver committed May 26, 2015
1 parent 2bf11ca commit 42889a5
Show file tree
Hide file tree
Showing 5 changed files with 564 additions and 0 deletions.
85 changes: 85 additions & 0 deletions docs/pubsub-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,88 @@ Fetch messages for a pull subscription without blocking (none pending):
>>> messages = [recv[1] for recv in received]
>>> [message.id for message in messages]
[]

Using Clients
-------------

A :class:`gcloud.pubsub.client.Client` instance explicitly manages a
:class:`gcloud.pubsub.connection.Connection` and an associated project
ID. Applications can then use the APIs which might otherwise take a
``connection`` or ``project`` parameter, knowing that the values configured
in the client will be passed.

Create a client using the defaults from the environment:

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> client = Client()

Create a client using an explicit connection, but the default project:

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> from gcloud.pubsub.connection import Connection
>>> connection = Connection.from_service_account_json('/path/to/creds.json')
>>> client = Client(connection=connection)

Create a client using an explicit project ID, but the connetion inferred
from the environment:

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> client = Client(project='your-project-id')

Listing topics using a client (note that the client's connection
is used to make the request, and its project is passed as a parameter):

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> client = Client(project='your-project-id')
>>> topics, next_page_token = client.list_topics() # API request

Listing subscriptions using a client (note that the client's connection
is used to make the request, and its project is passed as a parameter):

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> client = Client(project='your-project-id')
>>> topics, next_page_token = client.list_topics() # API request
>>> subscription, next_page_tokens = list_subscriptions() # API request

Instantiate a topic using a client (note that the client's project is passed
through to the topic constructor, and that the returned object is a proxy
which ensures that an API requests made via the topic use the client's
connection):

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> client = Client(project='your-project-id')
>>> topic = client.topic('topic-name')
>>> topic.exists() # API request
False
>>> topic.create() # API request
>>> topic.exists() # API request
True

Instantiate a subscription using a client (note that the client's project is
passed through to the subscription constructor, and that the returned object
is a proxy which ensures that an API requests made via the subscription use
the client's connection):

.. doctest::

>>> from gcloud.pubsub.client import Client
>>> client = Client(project='your-project-id')
>>> topic = client.topic('topic-name')
>>> subscription = topic.subscription('subscription-name')
>>> subscription.exists() # API request
False
>>> subscription.create() # API request
>>> subscription.exists() # API request
True
34 changes: 34 additions & 0 deletions gcloud/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
This module is not part of the public API surface of `gcloud`.
"""

import functools
import inspect
import os
import socket

Expand Down Expand Up @@ -238,3 +241,34 @@ def __init__(self, project=None, implicit=False):


_DEFAULTS = _DefaultsContainer(implicit=True)


class _ClientProxy(object):
"""Proxy for :class:`gcloud.pubsub.topic.Topic`.
:param wrapped: Domain instance being proxied.
:param client: Client used to pass connection / project as needed to
methods of ``wrapped``.
"""
def __init__(self, wrapped, client):
self._wrapped = wrapped
self._client = client

def __getattr__(self, name):
"""Proxy to wrapped object.
Pass 'connection' and 'project' from our client to methods which take
either / both.
"""
found = getattr(self._wrapped, name)
if inspect.ismethod(found):
args, _, _ = inspect.getargs(found.__code__)
curried = {}
if 'connection' in args:
curried['connection'] = self._client.connection
if 'project' in args:
curried['project'] = self._client.project
if curried:
found = functools.partial(found, **curried)
return found
130 changes: 130 additions & 0 deletions gcloud/pubsub/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Convenience proxies
Define wrappers for ``api`` functions, :class:`gcloud.pubsub.topic.Topic`, and
:class:`gcloud.pubsub.subscription.Subscription`, passing the memoized
connection / project as needed.
"""

from gcloud._helpers import get_default_project
from gcloud._helpers import _ClientProxy
from gcloud.pubsub._implicit_environ import _require_connection
from gcloud.pubsub import api
from gcloud.pubsub.subscription import Subscription
from gcloud.pubsub.topic import Topic


class Client(object):
"""Wrap :mod:`gcloud.pubsub` API objects.
:type connection: :class:`gcloud.pubsub.connection.Connection` or None
:param connection: The configured connection. Defaults to one inferred
from the environment.
:type project: str or None
:param connection: The configured project. Defaults to the value inferred
from the environment.
"""

def __init__(self, connection=None, project=None):
self.connection = _require_connection(connection)
if project is None:
project = get_default_project()
self.project = project

def topic(self, name):
"""Proxy for :class:`gcloud.pubsub.topic.Topic`.
:type name: string
:param name: the name of the topic
:rtype: :class:`_Topic`
:returns: a proxy for a newly created Topic, using the passed name
and the client's project.
"""
topic = Topic(name, self.project)
return _Topic(topic, self)

def list_topics(self, page_size=None, page_token=None):
"""Proxy for :func:`gcloud.pubsub.api.list_topics`.
Passes configured connection and project.
"""
topics, next_page_token = api.list_topics(
page_size=page_size,
page_token=page_token,
connection=self.connection,
project=self.project)
proxies = [_Topic(topic, self) for topic in topics]
return proxies, next_page_token

def list_subscriptions(self, page_size=None, page_token=None,
topic_name=None):
"""Proxy for :func:`gcloud.pubsub.api.list_subscriptions`.
Passes configured connection and project.
"""
subscriptions, next_page_token = api.list_subscriptions(
page_size=page_size,
page_token=page_token,
topic_name=topic_name,
connection=self.connection,
project=self.project)
topics = dict([(sub.topic.name, _Topic(sub.topic, self))
for sub in subscriptions])
proxies = [
_Subscription(sub, self, topics[sub.topic.name])
for sub in subscriptions]
return proxies, next_page_token


class _Topic(_ClientProxy):
"""Proxy for :class:`gcloud.pubsub.topic.Topic`.
:type wrapped: :class:`gcloud.pubsub.topic.Topic`
:param wrapped: Topic being proxied.
:type client: :class:`gcloud.pubsub.client.Client`
:param client: Client used to pass connection / project.
"""
def subscription(self, name, ack_deadline=None, push_endpoint=None):
""" Proxy through to :class:`gcloud.pubsub.subscription.Subscription`.
:rtype: :class:`_Subscription`
"""
subscription = Subscription(
name,
self._wrapped,
ack_deadline=ack_deadline,
push_endpoint=push_endpoint)
return _Subscription(subscription, self._client, self)


class _Subscription(_ClientProxy):
"""Proxy for :class:`gcloud.pubsub.subscription.Subscription`.
:type wrapped: :class:`gcloud.pubsub.topic.Subscription`
:param wrapped: Subscription being proxied.
:type client: :class:`gcloud.pubsub.client.Client`
:param client: Client used to pass connection / project.
:type topic: :class:`gcloud.pubsub.client._Topic`
:param topic: proxy for the wrapped subscription's topic.
"""
def __init__(self, wrapped, client, topic):
super(_Subscription, self).__init__(wrapped, client)
self.topic = topic
Loading

0 comments on commit 42889a5

Please sign in to comment.