Source code for google.appengine.api.dispatchinfo

#!/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
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.

"""Dispatch configuration tools.

Library for parsing dispatch.yaml files and working with these in memory.

import re

from google.appengine.api import appinfo
from google.appengine.api import validation
from google.appengine.api import yaml_builder
from google.appengine.api import yaml_listener
from google.appengine.api import yaml_object

_URL_SPLITTER_RE = re.compile(r'^([^/]+)(/.*)$')

_URL_HOST_EXACT_PATTERN_RE = re.compile(r"""
# 0 or more . terminated hostname segments (may not start or end in -).
# followed by a host name segment.
([a-z0-9]([a-z0-9\-]*[a-z0-9])*)$""", re.VERBOSE)

_URL_IP_V4_ADDR_RE = re.compile(r"""
#4 1-3 digit numbers separated by .
^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$""", re.VERBOSE)

_URL_HOST_SUFFIX_PATTERN_RE = re.compile(r"""
# Single star or
# Host prefix with no .,  Ex '*-a' or
# Host prefix with ., Ex '*-a.b-c.d'
""", re.VERBOSE)

APPLICATION = 'application'
DISPATCH = 'dispatch'
URL = 'url'
MODULE = 'module'
SERVICE = 'service'

[docs]class Error(Exception): """Base class for errors in this module."""
[docs]class MalformedDispatchConfigurationError(Error): """Configuration file for dispatch is malformed."""
[docs]class DispatchEntryURLValidator(validation.Validator): """Validater for URL patterns."""
[docs] def Validate(self, value, unused_key=None): """Validates an URL pattern.""" if value is None: raise validation.MissingAttribute('url must be specified') if not isinstance(value, basestring): raise validation.ValidationError('url must be a string, not \'%r\'' % type(value)) url_holder = ParsedURL(value) if url_holder.host_exact: _ValidateMatch(_URL_HOST_EXACT_PATTERN_RE,, 'invalid host_pattern \'%s\'' % _ValidateNotIpV4Address( else: _ValidateMatch(_URL_HOST_SUFFIX_PATTERN_RE, url_holder.host_pattern, 'invalid host_pattern \'%s\'' % url_holder.host_pattern) return value
[docs]class ParsedURL(object): """Dispath Entry URL holder class. Attributes: host_pattern: The host pattern component of the URL pattern. host_exact: True iff the host pattern does not start with a *. host: host_pattern with any leading * removed. path_pattern: The path pattern component of the URL pattern. path_exact: True iff path_pattern does not end with a *. path: path_pattern with any trailing * removed. """ def __init__(self, url_pattern): """Initializes this ParsedURL with an URL pattern value. Args: url_pattern: An URL pattern that conforms to the regular expression '^([^/]+)(/.*)$'. Raises: validation.ValidationError: When url_pattern does not match the required regular expression. """ split_matcher = _ValidateMatch(_URL_SPLITTER_RE, url_pattern, 'invalid url \'%s\'' % url_pattern) self.host_pattern, self.path_pattern = split_matcher.groups() if self.host_pattern.startswith('*'): self.host_exact = False = self.host_pattern[1:] else: self.host_exact = True = self.host_pattern if self.path_pattern.endswith('*'): self.path_exact = False self.path = self.path_pattern[:-1] else: self.path_exact = True self.path = self.path_pattern
def _ValidateMatch(regex, value, message): """Validate value matches regex.""" matcher = regex.match(value) if not matcher: raise validation.ValidationError(message) return matcher def _ValidateNotIpV4Address(host): """Validate host is not an IPV4 address.""" matcher = _URL_IP_V4_ADDR_RE.match(host) if matcher and sum(1 for x in matcher.groups() if int(x) <= 255) == 4: raise validation.ValidationError('Host may not match an ipv4 address \'%s\'' % host) return matcher
[docs]class DispatchEntry(validation.Validated): """A Dispatch entry describes a mapping from a URL pattern to a module.""" ATTRIBUTES = { URL: DispatchEntryURLValidator(), MODULE: validation.Optional(appinfo.MODULE_ID_RE_STRING), SERVICE: validation.Optional(appinfo.MODULE_ID_RE_STRING) }
[docs]class DispatchInfoExternal(validation.Validated): """Describes the format of a dispatch.yaml file.""" ATTRIBUTES = { APPLICATION: validation.Optional(appinfo.APPLICATION_RE_STRING), DISPATCH: validation.Optional(validation.Repeated(DispatchEntry)), }
[docs]def LoadSingleDispatch(dispatch_info, open_fn=None): """Load a dispatch.yaml file or string and return a DispatchInfoExternal. Args: dispatch_info: The contents of a dispatch.yaml file as a string, or an open file object. open_fn: Function for opening files. Unused here, needed to provide a polymorphic API used by yaml parsing. Returns: A DispatchInfoExternal instance which represents the contents of the parsed yaml file. Raises: MalformedDispatchConfigurationError: The yaml file contains multiple dispatch sections or is missing a required value. yaml_errors.EventError: An error occured while parsing the yaml file. """ builder = yaml_object.ObjectBuilder(DispatchInfoExternal) handler = yaml_builder.BuilderHandler(builder) listener = yaml_listener.EventListener(handler) listener.Parse(dispatch_info) parsed_yaml = handler.GetResults() if not parsed_yaml: return DispatchInfoExternal() if len(parsed_yaml) > 1: raise MalformedDispatchConfigurationError('Multiple dispatch: sections ' 'in configuration.') dispatch_info_external = parsed_yaml[0] for dispatch in getattr(dispatch_info_external, DISPATCH) or []: if dispatch.module and dispatch.service: raise MalformedDispatchConfigurationError( 'Both module: and service: in dispatch entry. Please use only one.') if not (dispatch.module or dispatch.service): raise MalformedDispatchConfigurationError( "Missing required value 'service'.") dispatch.module = dispatch.module or dispatch.service dispatch.service = None return dispatch_info_external

Send feedback about...

App Engine standard environment for Python