Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Implement 'pubsub.client.Client'.
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
commit 42889a5f3cded2e000f2e585fcfc5684a17a8255
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):

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

"""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):

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

self.connection = _require_connection(connection)

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

if project is None:
project = get_default_project()
self.project = project

def topic(self, name):

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

"""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

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.


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