summaryrefslogtreecommitdiffstats
path: root/google_appengine/google/appengine/api/capabilities
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2009-10-19 20:20:09 -0400
committerJason A. Donenfeld <Jason@zx2c4.com>2009-10-19 20:20:09 -0400
commit2d6dd2c5ade3f5fad3e2257dce52a6e188fe7535 (patch)
treeda9c93d2f87df6d2b688a455a31e69859117ba1e /google_appengine/google/appengine/api/capabilities
downloadFramedPrototype-2d6dd2c5ade3f5fad3e2257dce52a6e188fe7535.tar.xz
FramedPrototype-2d6dd2c5ade3f5fad3e2257dce52a6e188fe7535.zip
Initial import.
Diffstat (limited to 'google_appengine/google/appengine/api/capabilities')
-rwxr-xr-xgoogle_appengine/google/appengine/api/capabilities/__init__.py172
-rw-r--r--google_appengine/google/appengine/api/capabilities/__init__.pycbin0 -> 5952 bytes
-rw-r--r--google_appengine/google/appengine/api/capabilities/capability_service_pb.py366
-rw-r--r--google_appengine/google/appengine/api/capabilities/capability_service_pb.pycbin0 -> 18033 bytes
-rwxr-xr-xgoogle_appengine/google/appengine/api/capabilities/capability_stub.py53
-rw-r--r--google_appengine/google/appengine/api/capabilities/capability_stub.pycbin0 -> 1762 bytes
6 files changed, 591 insertions, 0 deletions
diff --git a/google_appengine/google/appengine/api/capabilities/__init__.py b/google_appengine/google/appengine/api/capabilities/__init__.py
new file mode 100755
index 0000000..f672cbb
--- /dev/null
+++ b/google_appengine/google/appengine/api/capabilities/__init__.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# 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.
+#
+
+"""Allows applications to identify API outages and scheduled downtime.
+
+Some examples:
+ def StoreUploadedProfileImage(self):
+ uploaded_image = self.request.get('img')
+ # If the images API is unavailable, we'll just skip the resize.
+ if CapabilitySet('images').is_enabled():
+ uploaded_image = images.resize(uploaded_image, 64, 64)
+ store(uploaded_image)
+
+ def RenderHTMLForm(self):
+ datastore_readonly = CapabilitySet('datastore_v3', capabilities=['write'])
+ if datastore_readonly.may_be_disabled_in(60):
+ # self.response.out('<p>Not accepting submissions right now: %s</p>' %
+ datastore_readonly.admin_message())
+ # ...render form with form elements disabled...
+ else:
+ # ...render form normally...
+
+ Individual API wrapper modules should expose CapabilitySet objects
+ for users rather than relying on users to create them. They may
+ also create convenience methods (e.g. db.IsReadOnly()) that delegate
+ to the relevant CapabilitySet.
+
+Classes defined here:
+ CapabilitySet: encapsulates one or more capabilities, allows introspection.
+ UnknownCapabilityError: thrown when an unknown capability is requested.
+"""
+
+
+
+
+
+from google.appengine.api.capabilities import capability_service_pb
+from google.appengine.base import capabilities_pb
+from google.appengine.api import apiproxy_stub_map
+
+
+IsEnabledRequest = capability_service_pb.IsEnabledRequest
+IsEnabledResponse = capability_service_pb.IsEnabledResponse
+CapabilityConfig = capabilities_pb.CapabilityConfig
+
+
+class UnknownCapabilityError(Exception):
+ """An unknown capability was requested."""
+
+
+class CapabilitySet(object):
+ """Encapsulates one or more capabilities.
+
+ Capabilities can either be named explicitly, or inferred from the
+ list of methods provided. If no capabilities or methods are
+ provided, this will check whether the entire package is enabled.
+ """
+ def __init__(self, package, capabilities=None, methods=None,
+ stub_map=apiproxy_stub_map):
+ """Constructor.
+
+ Args:
+ capabilities: list of strings
+ methods: list of strings
+ """
+ if capabilities is None:
+ capabilities = []
+ if methods is None:
+ methods = []
+ self._package = package
+ self._capabilities = ['*'] + capabilities
+ self._methods = methods
+ self._stub_map = stub_map
+
+ def is_enabled(self):
+ """Tests whether the capabilities is currently enabled.
+
+ Returns:
+ True if API calls that require these capabillities will succeed.
+
+ Raises:
+ UnknownCapabilityError, if a specified capability was not recognized.
+ """
+ config = self._get_status()
+ return config.summary_status() in (IsEnabledResponse.ENABLED,
+ IsEnabledResponse.SCHEDULED_FUTURE,
+ IsEnabledResponse.SCHEDULED_NOW)
+
+ def will_remain_enabled_for(self, time=60):
+ """Returns true if it will remain enabled for the specified amount of time.
+
+ Args:
+ time: Number of seconds in the future to look when checking for scheduled
+ downtime.
+
+ Returns:
+ True if there is no scheduled downtime for the specified capability
+ within the amount of time specified.
+
+ Raises:
+ UnknownCapabilityError, if a specified capability was not recognized.
+ """
+ config = self._get_status()
+
+ status = config.summary_status()
+ if status == IsEnabledResponse.ENABLED:
+ return True
+ elif status == IsEnabledResponse.SCHEDULED_NOW:
+ return False
+ elif status == IsEnabledResponse.SCHEDULED_FUTURE:
+ if config.has_time_until_scheduled():
+ return config.time_until_scheduled() >= time
+ else:
+ return True
+ elif status == IsEnabledResponse.DISABLED:
+ return False
+ else:
+ return False
+
+ def admin_message(self):
+ """Get any administrator notice messages for these capabilities.
+
+ Returns:
+ A string containing one or more admin messages, or an empty string.
+
+ Raises:
+ UnknownCapabilityError, if a specified capability was not recognized.
+ """
+ message_list = []
+ for config in self._get_status().config_list():
+ message = config.admin_message()
+ if message and message not in message_list:
+ message_list.append(message)
+ return ' '.join(message_list)
+
+ def _get_status(self):
+ """Get an IsEnabledResponse for the capabilities listed.
+
+ Returns:
+ IsEnabledResponse for the specified capabilities.
+
+ Raises:
+ UnknownCapabilityError: If an unknown capability was requested.
+ """
+ req = IsEnabledRequest()
+ req.set_package(self._package)
+ for capability in self._capabilities:
+ req.add_capability(capability)
+ for method in self._methods:
+ req.add_call(method)
+
+ resp = capability_service_pb.IsEnabledResponse()
+ self._stub_map.MakeSyncCall('capability_service', 'IsEnabled', req, resp)
+
+ if resp.summary_status() == IsEnabledResponse.UNKNOWN:
+ raise UnknownCapabilityError()
+
+ return resp
diff --git a/google_appengine/google/appengine/api/capabilities/__init__.pyc b/google_appengine/google/appengine/api/capabilities/__init__.pyc
new file mode 100644
index 0000000..c8ac026
--- /dev/null
+++ b/google_appengine/google/appengine/api/capabilities/__init__.pyc
Binary files differ
diff --git a/google_appengine/google/appengine/api/capabilities/capability_service_pb.py b/google_appengine/google/appengine/api/capabilities/capability_service_pb.py
new file mode 100644
index 0000000..9f9ba29
--- /dev/null
+++ b/google_appengine/google/appengine/api/capabilities/capability_service_pb.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# 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.
+#
+
+from google.net.proto import ProtocolBuffer
+import array
+import dummy_thread as thread
+
+__pychecker__ = """maxreturns=0 maxbranches=0 no-callinit
+ unusednames=printElemNumber,debug_strs no-special"""
+
+from google.appengine.base.capabilities_pb import CapabilityConfig
+class IsEnabledRequest(ProtocolBuffer.ProtocolMessage):
+ has_package_ = 0
+ package_ = ""
+
+ def __init__(self, contents=None):
+ self.capability_ = []
+ self.call_ = []
+ if contents is not None: self.MergeFromString(contents)
+
+ def package(self): return self.package_
+
+ def set_package(self, x):
+ self.has_package_ = 1
+ self.package_ = x
+
+ def clear_package(self):
+ if self.has_package_:
+ self.has_package_ = 0
+ self.package_ = ""
+
+ def has_package(self): return self.has_package_
+
+ def capability_size(self): return len(self.capability_)
+ def capability_list(self): return self.capability_
+
+ def capability(self, i):
+ return self.capability_[i]
+
+ def set_capability(self, i, x):
+ self.capability_[i] = x
+
+ def add_capability(self, x):
+ self.capability_.append(x)
+
+ def clear_capability(self):
+ self.capability_ = []
+
+ def call_size(self): return len(self.call_)
+ def call_list(self): return self.call_
+
+ def call(self, i):
+ return self.call_[i]
+
+ def set_call(self, i, x):
+ self.call_[i] = x
+
+ def add_call(self, x):
+ self.call_.append(x)
+
+ def clear_call(self):
+ self.call_ = []
+
+
+ def MergeFrom(self, x):
+ assert x is not self
+ if (x.has_package()): self.set_package(x.package())
+ for i in xrange(x.capability_size()): self.add_capability(x.capability(i))
+ for i in xrange(x.call_size()): self.add_call(x.call(i))
+
+ def Equals(self, x):
+ if x is self: return 1
+ if self.has_package_ != x.has_package_: return 0
+ if self.has_package_ and self.package_ != x.package_: return 0
+ if len(self.capability_) != len(x.capability_): return 0
+ for e1, e2 in zip(self.capability_, x.capability_):
+ if e1 != e2: return 0
+ if len(self.call_) != len(x.call_): return 0
+ for e1, e2 in zip(self.call_, x.call_):
+ if e1 != e2: return 0
+ return 1
+
+ def IsInitialized(self, debug_strs=None):
+ initialized = 1
+ if (not self.has_package_):
+ initialized = 0
+ if debug_strs is not None:
+ debug_strs.append('Required field: package not set.')
+ return initialized
+
+ def ByteSize(self):
+ n = 0
+ n += self.lengthString(len(self.package_))
+ n += 1 * len(self.capability_)
+ for i in xrange(len(self.capability_)): n += self.lengthString(len(self.capability_[i]))
+ n += 1 * len(self.call_)
+ for i in xrange(len(self.call_)): n += self.lengthString(len(self.call_[i]))
+ return n + 1
+
+ def Clear(self):
+ self.clear_package()
+ self.clear_capability()
+ self.clear_call()
+
+ def OutputUnchecked(self, out):
+ out.putVarInt32(10)
+ out.putPrefixedString(self.package_)
+ for i in xrange(len(self.capability_)):
+ out.putVarInt32(18)
+ out.putPrefixedString(self.capability_[i])
+ for i in xrange(len(self.call_)):
+ out.putVarInt32(26)
+ out.putPrefixedString(self.call_[i])
+
+ def TryMerge(self, d):
+ while d.avail() > 0:
+ tt = d.getVarInt32()
+ if tt == 10:
+ self.set_package(d.getPrefixedString())
+ continue
+ if tt == 18:
+ self.add_capability(d.getPrefixedString())
+ continue
+ if tt == 26:
+ self.add_call(d.getPrefixedString())
+ continue
+ if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
+ d.skipData(tt)
+
+
+ def __str__(self, prefix="", printElemNumber=0):
+ res=""
+ if self.has_package_: res+=prefix+("package: %s\n" % self.DebugFormatString(self.package_))
+ cnt=0
+ for e in self.capability_:
+ elm=""
+ if printElemNumber: elm="(%d)" % cnt
+ res+=prefix+("capability%s: %s\n" % (elm, self.DebugFormatString(e)))
+ cnt+=1
+ cnt=0
+ for e in self.call_:
+ elm=""
+ if printElemNumber: elm="(%d)" % cnt
+ res+=prefix+("call%s: %s\n" % (elm, self.DebugFormatString(e)))
+ cnt+=1
+ return res
+
+
+ def _BuildTagLookupTable(sparse, maxtag, default=None):
+ return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
+
+ kpackage = 1
+ kcapability = 2
+ kcall = 3
+
+ _TEXT = _BuildTagLookupTable({
+ 0: "ErrorCode",
+ 1: "package",
+ 2: "capability",
+ 3: "call",
+ }, 3)
+
+ _TYPES = _BuildTagLookupTable({
+ 0: ProtocolBuffer.Encoder.NUMERIC,
+ 1: ProtocolBuffer.Encoder.STRING,
+ 2: ProtocolBuffer.Encoder.STRING,
+ 3: ProtocolBuffer.Encoder.STRING,
+ }, 3, ProtocolBuffer.Encoder.MAX_TYPE)
+
+ _STYLE = """"""
+ _STYLE_CONTENT_TYPE = """"""
+class IsEnabledResponse(ProtocolBuffer.ProtocolMessage):
+
+ ENABLED = 1
+ SCHEDULED_FUTURE = 2
+ SCHEDULED_NOW = 3
+ DISABLED = 4
+ UNKNOWN = 5
+
+ _SummaryStatus_NAMES = {
+ 1: "ENABLED",
+ 2: "SCHEDULED_FUTURE",
+ 3: "SCHEDULED_NOW",
+ 4: "DISABLED",
+ 5: "UNKNOWN",
+ }
+
+ def SummaryStatus_Name(cls, x): return cls._SummaryStatus_NAMES.get(x, "")
+ SummaryStatus_Name = classmethod(SummaryStatus_Name)
+
+ has_summary_status_ = 0
+ summary_status_ = 0
+ has_time_until_scheduled_ = 0
+ time_until_scheduled_ = 0
+
+ def __init__(self, contents=None):
+ self.config_ = []
+ if contents is not None: self.MergeFromString(contents)
+
+ def summary_status(self): return self.summary_status_
+
+ def set_summary_status(self, x):
+ self.has_summary_status_ = 1
+ self.summary_status_ = x
+
+ def clear_summary_status(self):
+ if self.has_summary_status_:
+ self.has_summary_status_ = 0
+ self.summary_status_ = 0
+
+ def has_summary_status(self): return self.has_summary_status_
+
+ def time_until_scheduled(self): return self.time_until_scheduled_
+
+ def set_time_until_scheduled(self, x):
+ self.has_time_until_scheduled_ = 1
+ self.time_until_scheduled_ = x
+
+ def clear_time_until_scheduled(self):
+ if self.has_time_until_scheduled_:
+ self.has_time_until_scheduled_ = 0
+ self.time_until_scheduled_ = 0
+
+ def has_time_until_scheduled(self): return self.has_time_until_scheduled_
+
+ def config_size(self): return len(self.config_)
+ def config_list(self): return self.config_
+
+ def config(self, i):
+ return self.config_[i]
+
+ def mutable_config(self, i):
+ return self.config_[i]
+
+ def add_config(self):
+ x = CapabilityConfig()
+ self.config_.append(x)
+ return x
+
+ def clear_config(self):
+ self.config_ = []
+
+ def MergeFrom(self, x):
+ assert x is not self
+ if (x.has_summary_status()): self.set_summary_status(x.summary_status())
+ if (x.has_time_until_scheduled()): self.set_time_until_scheduled(x.time_until_scheduled())
+ for i in xrange(x.config_size()): self.add_config().CopyFrom(x.config(i))
+
+ def Equals(self, x):
+ if x is self: return 1
+ if self.has_summary_status_ != x.has_summary_status_: return 0
+ if self.has_summary_status_ and self.summary_status_ != x.summary_status_: return 0
+ if self.has_time_until_scheduled_ != x.has_time_until_scheduled_: return 0
+ if self.has_time_until_scheduled_ and self.time_until_scheduled_ != x.time_until_scheduled_: return 0
+ if len(self.config_) != len(x.config_): return 0
+ for e1, e2 in zip(self.config_, x.config_):
+ if e1 != e2: return 0
+ return 1
+
+ def IsInitialized(self, debug_strs=None):
+ initialized = 1
+ if (not self.has_summary_status_):
+ initialized = 0
+ if debug_strs is not None:
+ debug_strs.append('Required field: summary_status not set.')
+ for p in self.config_:
+ if not p.IsInitialized(debug_strs): initialized=0
+ return initialized
+
+ def ByteSize(self):
+ n = 0
+ n += self.lengthVarInt64(self.summary_status_)
+ if (self.has_time_until_scheduled_): n += 1 + self.lengthVarInt64(self.time_until_scheduled_)
+ n += 1 * len(self.config_)
+ for i in xrange(len(self.config_)): n += self.lengthString(self.config_[i].ByteSize())
+ return n + 1
+
+ def Clear(self):
+ self.clear_summary_status()
+ self.clear_time_until_scheduled()
+ self.clear_config()
+
+ def OutputUnchecked(self, out):
+ out.putVarInt32(8)
+ out.putVarInt32(self.summary_status_)
+ if (self.has_time_until_scheduled_):
+ out.putVarInt32(16)
+ out.putVarInt64(self.time_until_scheduled_)
+ for i in xrange(len(self.config_)):
+ out.putVarInt32(26)
+ out.putVarInt32(self.config_[i].ByteSize())
+ self.config_[i].OutputUnchecked(out)
+
+ def TryMerge(self, d):
+ while d.avail() > 0:
+ tt = d.getVarInt32()
+ if tt == 8:
+ self.set_summary_status(d.getVarInt32())
+ continue
+ if tt == 16:
+ self.set_time_until_scheduled(d.getVarInt64())
+ continue
+ if tt == 26:
+ length = d.getVarInt32()
+ tmp = ProtocolBuffer.Decoder(d.buffer(), d.pos(), d.pos() + length)
+ d.skip(length)
+ self.add_config().TryMerge(tmp)
+ continue
+ if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
+ d.skipData(tt)
+
+
+ def __str__(self, prefix="", printElemNumber=0):
+ res=""
+ if self.has_summary_status_: res+=prefix+("summary_status: %s\n" % self.DebugFormatInt32(self.summary_status_))
+ if self.has_time_until_scheduled_: res+=prefix+("time_until_scheduled: %s\n" % self.DebugFormatInt64(self.time_until_scheduled_))
+ cnt=0
+ for e in self.config_:
+ elm=""
+ if printElemNumber: elm="(%d)" % cnt
+ res+=prefix+("config%s <\n" % elm)
+ res+=e.__str__(prefix + " ", printElemNumber)
+ res+=prefix+">\n"
+ cnt+=1
+ return res
+
+
+ def _BuildTagLookupTable(sparse, maxtag, default=None):
+ return tuple([sparse.get(i, default) for i in xrange(0, 1+maxtag)])
+
+ ksummary_status = 1
+ ktime_until_scheduled = 2
+ kconfig = 3
+
+ _TEXT = _BuildTagLookupTable({
+ 0: "ErrorCode",
+ 1: "summary_status",
+ 2: "time_until_scheduled",
+ 3: "config",
+ }, 3)
+
+ _TYPES = _BuildTagLookupTable({
+ 0: ProtocolBuffer.Encoder.NUMERIC,
+ 1: ProtocolBuffer.Encoder.NUMERIC,
+ 2: ProtocolBuffer.Encoder.NUMERIC,
+ 3: ProtocolBuffer.Encoder.STRING,
+ }, 3, ProtocolBuffer.Encoder.MAX_TYPE)
+
+ _STYLE = """"""
+ _STYLE_CONTENT_TYPE = """"""
+
+__all__ = ['IsEnabledRequest','IsEnabledResponse']
diff --git a/google_appengine/google/appengine/api/capabilities/capability_service_pb.pyc b/google_appengine/google/appengine/api/capabilities/capability_service_pb.pyc
new file mode 100644
index 0000000..d1a68c2
--- /dev/null
+++ b/google_appengine/google/appengine/api/capabilities/capability_service_pb.pyc
Binary files differ
diff --git a/google_appengine/google/appengine/api/capabilities/capability_stub.py b/google_appengine/google/appengine/api/capabilities/capability_stub.py
new file mode 100755
index 0000000..6d33d7e
--- /dev/null
+++ b/google_appengine/google/appengine/api/capabilities/capability_stub.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# 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.
+#
+
+"""Stub version of the capability service API, everything is always enabled."""
+
+
+
+from google.appengine.api import apiproxy_stub
+from google.appengine.api import capabilities
+
+IsEnabledRequest = capabilities.IsEnabledRequest
+IsEnabledResponse = capabilities.IsEnabledResponse
+CapabilityConfig = capabilities.CapabilityConfig
+
+class CapabilityServiceStub(apiproxy_stub.APIProxyStub):
+ """Python only capability service stub."""
+
+ def __init__(self, service_name='capability_service'):
+ """Constructor.
+
+ Args:
+ service_name: Service name expected for all calls.
+ """
+ super(CapabilityServiceStub, self).__init__(service_name)
+
+
+ def _Dynamic_IsEnabled(self, request, response):
+ """Implementation of CapabilityService::IsEnabled().
+
+ Args:
+ request: An IsEnabledRequest.
+ response: An IsEnabledResponse.
+ """
+ response.set_summary_status(IsEnabledResponse.ENABLED)
+
+ default_config = response.add_config()
+ default_config.set_package('')
+ default_config.set_capability('')
+ default_config.set_status(CapabilityConfig.ENABLED)
diff --git a/google_appengine/google/appengine/api/capabilities/capability_stub.pyc b/google_appengine/google/appengine/api/capabilities/capability_stub.pyc
new file mode 100644
index 0000000..6336e60
--- /dev/null
+++ b/google_appengine/google/appengine/api/capabilities/capability_stub.pyc
Binary files differ