App Engine Python SDK version 1.8.6
git-svn-id: http://googleappengine.googlecode.com/svn/trunk/python@390 80f5ef21-4148-0410-bacc-cfb02402ada8
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 6bfb08d..c428873 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -3,6 +3,34 @@
App Engine SDK - Release Notes
+Version 1.8.6
+
+All
+==============================
+- A memcache size chart has been added to admin console's dashboard. Access it
+ via the drop-down above the graph. The chart graphs memcache size over time
+ enabling customers to determine when cache flush events occurred. This is a
+ preview feature.
+- Fixed an issue with the dev_appserver that allows an invalid Datastore query
+ combination of group by and filter properties.
+- Fixed an issue with the bulkloader that causes uploads to the dev_appserver
+ to fail.
+- Fixed an issue affecting validation of the size of Datastore property names.
+- Fixed an issue with Datastore query validation for strings with exactly 500
+ characters.
+ https://code.google.com/p/googleappengine/issues/detail?id=10019
+
+Python
+==============================
+- Django 1.5.4 is now available in the Runtime. This is a Preview feature.
+- app_identity.get_access_token in the App Identity API is now a GA feature.
+- Geo is now a supported field type in the Search API for the dev_appserver.
+ http://code.google.com/p/googleappengine/issues/detail?id=7486
+
+PHP
+==============================
+- php://memory and php://temp I/O streams are now supported.
+
Version 1.8.5
All
diff --git a/VERSION b/VERSION
index 82756fd..e8d37c1 100644
--- a/VERSION
+++ b/VERSION
@@ -1,5 +1,5 @@
-release: "1.8.5"
-timestamp: 1378244055
+release: "1.8.6"
+timestamp: 1380132098
api_versions: ['1']
supported_api_versions:
python:
diff --git a/_php_runtime.py b/_php_runtime.py
index b615636..566da19 100644
--- a/_php_runtime.py
+++ b/_php_runtime.py
@@ -109,7 +109,7 @@
os.path.join(_DIR_PATH, 'lib', 'django-1.4'),
os.path.join(_DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(_DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(_DIR_PATH, 'lib', 'PyAMF-0.6.1'),
os.path.join(_DIR_PATH, 'lib', 'markupsafe-0.15'),
os.path.join(_DIR_PATH, 'lib', 'webob-1.2.3'),
@@ -140,7 +140,7 @@
os.path.join(_DIR_PATH, 'lib', 'concurrent'),
os.path.join(_DIR_PATH, 'lib', 'cherrypy'),
os.path.join(_DIR_PATH, 'lib', 'fancy_urllib'),
- os.path.join(_DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(_DIR_PATH, 'lib', 'yaml-3.10'),
]
diff --git a/_python_runtime.py b/_python_runtime.py
index b615636..566da19 100644
--- a/_python_runtime.py
+++ b/_python_runtime.py
@@ -109,7 +109,7 @@
os.path.join(_DIR_PATH, 'lib', 'django-1.4'),
os.path.join(_DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(_DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(_DIR_PATH, 'lib', 'PyAMF-0.6.1'),
os.path.join(_DIR_PATH, 'lib', 'markupsafe-0.15'),
os.path.join(_DIR_PATH, 'lib', 'webob-1.2.3'),
@@ -140,7 +140,7 @@
os.path.join(_DIR_PATH, 'lib', 'concurrent'),
os.path.join(_DIR_PATH, 'lib', 'cherrypy'),
os.path.join(_DIR_PATH, 'lib', 'fancy_urllib'),
- os.path.join(_DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(_DIR_PATH, 'lib', 'yaml-3.10'),
]
diff --git a/api_server.py b/api_server.py
index fa956c5..b15365d 100644
--- a/api_server.py
+++ b/api_server.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/appcfg.py b/appcfg.py
index fa956c5..b15365d 100644
--- a/appcfg.py
+++ b/appcfg.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/backends_conversion.py b/backends_conversion.py
index fa956c5..b15365d 100644
--- a/backends_conversion.py
+++ b/backends_conversion.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/bulkload_client.py b/bulkload_client.py
index fa956c5..b15365d 100644
--- a/bulkload_client.py
+++ b/bulkload_client.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/bulkloader.py b/bulkloader.py
index fa956c5..b15365d 100644
--- a/bulkloader.py
+++ b/bulkloader.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/demos/php/minishell/shell.php b/demos/php/minishell/shell.php
index 4e3fedb..db95291 100644
--- a/demos/php/minishell/shell.php
+++ b/demos/php/minishell/shell.php
@@ -89,7 +89,8 @@
/** Replace the constants with the serialized stored ones. */
function loadConstants() {
$constants = unserialize($this->constants);
- foreach(array_diff($constants, get_defined_constants()) as $constant=>$value) {
+ foreach(array_diff_key($constants, get_defined_constants()) as
+ $constant=>$value) {
define($constant, $value);
}
}
diff --git a/dev_appserver.py b/dev_appserver.py
index b615636..566da19 100644
--- a/dev_appserver.py
+++ b/dev_appserver.py
@@ -109,7 +109,7 @@
os.path.join(_DIR_PATH, 'lib', 'django-1.4'),
os.path.join(_DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(_DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(_DIR_PATH, 'lib', 'PyAMF-0.6.1'),
os.path.join(_DIR_PATH, 'lib', 'markupsafe-0.15'),
os.path.join(_DIR_PATH, 'lib', 'webob-1.2.3'),
@@ -140,7 +140,7 @@
os.path.join(_DIR_PATH, 'lib', 'concurrent'),
os.path.join(_DIR_PATH, 'lib', 'cherrypy'),
os.path.join(_DIR_PATH, 'lib', 'fancy_urllib'),
- os.path.join(_DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(_DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(_DIR_PATH, 'lib', 'yaml-3.10'),
]
diff --git a/download_appstats.py b/download_appstats.py
index fa956c5..b15365d 100644
--- a/download_appstats.py
+++ b/download_appstats.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/endpointscfg.py b/endpointscfg.py
index fa956c5..b15365d 100644
--- a/endpointscfg.py
+++ b/endpointscfg.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/gen_protorpc.py b/gen_protorpc.py
index fa956c5..b15365d 100644
--- a/gen_protorpc.py
+++ b/gen_protorpc.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/google/appengine/api/appinfo.py b/google/appengine/api/appinfo.py
index 96ca58c..0b075c8 100644
--- a/google/appengine/api/appinfo.py
+++ b/google/appengine/api/appinfo.py
@@ -369,6 +369,14 @@
'A library for creating and transforming images.',
['1.1.7']),
_VersionedLibrary(
+ 'protorpc',
+ 'https://code.google.com/p/google-protorpc/',
+ 'A framework for implementing HTTP-based remote procedure call (RPC) '
+ 'services.',
+ ['1.0'],
+ default_version='1.0',
+ ),
+ _VersionedLibrary(
'PyAMF',
'http://www.pyamf.org/',
'A library that provides (AMF) Action Message Format functionality.',
diff --git a/google/appengine/api/datastore_file_stub.py b/google/appengine/api/datastore_file_stub.py
index a6a3d57..d15559a 100644
--- a/google/appengine/api/datastore_file_stub.py
+++ b/google/appengine/api/datastore_file_stub.py
@@ -694,7 +694,7 @@
if count >= self._IdCounter(id_space):
self._SetIdCounter(id_space, count + 1)
- def _AllocateIds(self, reference, size=1, max_id=None):
+ def _AllocateSequentialIds(self, reference, size=1, max_id=None):
datastore_stub_util.Check(not (size and max_id),
'Both size and max cannot be set.')
@@ -715,7 +715,7 @@
return (start, end)
- def _AllocateScatteredIds(self, keys):
+ def _AllocateIds(self, keys):
self.__id_lock.acquire()
full_keys = []
try:
diff --git a/google/appengine/api/files/file_service_stub.py b/google/appengine/api/files/file_service_stub.py
index d0e4826..07334bb 100644
--- a/google/appengine/api/files/file_service_stub.py
+++ b/google/appengine/api/files/file_service_stub.py
@@ -125,7 +125,7 @@
{'content-type': upload.content_type})
assert blobkey == self.get_blobkey(upload.gs_filename)
self.gs_stub.put_continue_creation(
- blobkey, content, (0, len(content) - 1), True)
+ blobkey, content, (0, len(content) - 1), len(content))
del self.sequence_keys[filename]
diff --git a/google/appengine/api/mail.py b/google/appengine/api/mail.py
index e5b7395..d07aae1 100644
--- a/google/appengine/api/mail.py
+++ b/google/appengine/api/mail.py
@@ -612,7 +612,7 @@
class EncodedPayload(object):
"""Wrapper for a payload that contains encoding information.
- When an email is recieved, it is usually encoded using a certain
+ When an email is received, it is usually encoded using a certain
character set, and then possibly further encoded using a transfer
encoding in that character set. Most of the times, it is possible
to decode the encoded payload as is, however, in the case where it
diff --git a/google/appengine/api/mail_service_pb.py b/google/appengine/api/mail_service_pb.py
index c833407..54b09b7 100644
--- a/google/appengine/api/mail_service_pb.py
+++ b/google/appengine/api/mail_service_pb.py
@@ -42,6 +42,7 @@
UNAUTHORIZED_SENDER = 3
INVALID_ATTACHMENT_TYPE = 4
INVALID_HEADER_NAME = 5
+ EMPTY_CONTENT_ID = 6
_ErrorCode_NAMES = {
0: "OK",
@@ -50,6 +51,7 @@
3: "UNAUTHORIZED_SENDER",
4: "INVALID_ATTACHMENT_TYPE",
5: "INVALID_HEADER_NAME",
+ 6: "EMPTY_CONTENT_ID",
}
def ErrorCode_Name(cls, x): return cls._ErrorCode_NAMES.get(x, "")
@@ -124,6 +126,8 @@
filename_ = ""
has_data_ = 0
data_ = ""
+ has_contentid_ = 0
+ contentid_ = ""
def __init__(self, contents=None):
if contents is not None: self.MergeFromString(contents)
@@ -154,11 +158,25 @@
def has_data(self): return self.has_data_
+ def contentid(self): return self.contentid_
+
+ def set_contentid(self, x):
+ self.has_contentid_ = 1
+ self.contentid_ = x
+
+ def clear_contentid(self):
+ if self.has_contentid_:
+ self.has_contentid_ = 0
+ self.contentid_ = ""
+
+ def has_contentid(self): return self.has_contentid_
+
def MergeFrom(self, x):
assert x is not self
if (x.has_filename()): self.set_filename(x.filename())
if (x.has_data()): self.set_data(x.data())
+ if (x.has_contentid()): self.set_contentid(x.contentid())
def Equals(self, x):
if x is self: return 1
@@ -166,6 +184,8 @@
if self.has_filename_ and self.filename_ != x.filename_: return 0
if self.has_data_ != x.has_data_: return 0
if self.has_data_ and self.data_ != x.data_: return 0
+ if self.has_contentid_ != x.has_contentid_: return 0
+ if self.has_contentid_ and self.contentid_ != x.contentid_: return 0
return 1
def IsInitialized(self, debug_strs=None):
@@ -184,6 +204,7 @@
n = 0
n += self.lengthString(len(self.filename_))
n += self.lengthString(len(self.data_))
+ if (self.has_contentid_): n += 1 + self.lengthString(len(self.contentid_))
return n + 2
def ByteSizePartial(self):
@@ -194,17 +215,22 @@
if (self.has_data_):
n += 1
n += self.lengthString(len(self.data_))
+ if (self.has_contentid_): n += 1 + self.lengthString(len(self.contentid_))
return n
def Clear(self):
self.clear_filename()
self.clear_data()
+ self.clear_contentid()
def OutputUnchecked(self, out):
out.putVarInt32(10)
out.putPrefixedString(self.filename_)
out.putVarInt32(18)
out.putPrefixedString(self.data_)
+ if (self.has_contentid_):
+ out.putVarInt32(26)
+ out.putPrefixedString(self.contentid_)
def OutputPartial(self, out):
if (self.has_filename_):
@@ -213,6 +239,9 @@
if (self.has_data_):
out.putVarInt32(18)
out.putPrefixedString(self.data_)
+ if (self.has_contentid_):
+ out.putVarInt32(26)
+ out.putPrefixedString(self.contentid_)
def TryMerge(self, d):
while d.avail() > 0:
@@ -223,6 +252,9 @@
if tt == 18:
self.set_data(d.getPrefixedString())
continue
+ if tt == 26:
+ self.set_contentid(d.getPrefixedString())
+ continue
if (tt == 0): raise ProtocolBuffer.ProtocolBufferDecodeError
@@ -233,6 +265,7 @@
res=""
if self.has_filename_: res+=prefix+("FileName: %s\n" % self.DebugFormatString(self.filename_))
if self.has_data_: res+=prefix+("Data: %s\n" % self.DebugFormatString(self.data_))
+ if self.has_contentid_: res+=prefix+("ContentID: %s\n" % self.DebugFormatString(self.contentid_))
return res
@@ -241,18 +274,21 @@
kFileName = 1
kData = 2
+ kContentID = 3
_TEXT = _BuildTagLookupTable({
0: "ErrorCode",
1: "FileName",
2: "Data",
- }, 2)
+ 3: "ContentID",
+ }, 3)
_TYPES = _BuildTagLookupTable({
0: ProtocolBuffer.Encoder.NUMERIC,
1: ProtocolBuffer.Encoder.STRING,
2: ProtocolBuffer.Encoder.STRING,
- }, 2, ProtocolBuffer.Encoder.MAX_TYPE)
+ 3: ProtocolBuffer.Encoder.STRING,
+ }, 3, ProtocolBuffer.Encoder.MAX_TYPE)
_STYLE = """"""
diff --git a/google/appengine/api/search/stub/document_matcher.py b/google/appengine/api/search/stub/document_matcher.py
index a1b03ff..9459ed6 100644
--- a/google/appengine/api/search/stub/document_matcher.py
+++ b/google/appengine/api/search/stub/document_matcher.py
@@ -23,6 +23,7 @@
"""
import logging
+import math
from google.appengine.datastore import document_pb
@@ -43,6 +44,93 @@
Exception.__init__(self, msg)
+class LatLng(object):
+ """A class representing a Latitude/Longitude pair."""
+
+ _EARTH_RADIUS_METERS = 6371010
+
+ def __init__(self, latitude, longitude):
+ """Initializer.
+
+ Args:
+ latitude: The latitude in degrees.
+ longitude: The longitude in degrees.
+
+ Raises:
+ TypeError: If a non-numeric latitude or longitude is passed.
+ """
+ self._lat = math.radians(latitude)
+ self._lng = math.radians(longitude)
+
+ @property
+ def latitude(self):
+ """Returns the latitude in degrees."""
+ return math.degrees(self._lat)
+
+ @property
+ def longitude(self):
+ """Returns the longitude in degrees."""
+ return math.degrees(self._lng)
+
+ def __sub__(self, other):
+ """Returns the great circle distance between two LatLng objects as computed
+ by the Haversine formula."""
+
+ assert isinstance(other, LatLng)
+ dlat = self._lat - other._lat
+ dlng = self._lng - other._lng
+ a1 = math.sin(dlat / 2)**2
+ a2 = math.cos(self._lat) * math.cos(other._lat) * math.sin(dlng / 2)**2
+ return 2 * self._EARTH_RADIUS_METERS * math.asin(math.sqrt(a1 + a2))
+
+
+class DistanceMatcher(object):
+ """A class to match on geo distance."""
+ def __init__(self, geopoint, distance):
+ self._geopoint = geopoint
+ self._distance = distance
+
+ def _CheckOp(self, op):
+ if op == QueryParser.EQ:
+ raise ExpressionTreeException('Equality comparison not available for Geo type')
+ if op == QueryParser.NE:
+ raise ExpressionTreeException('!= comparison operator is not available')
+ if op not in (QueryParser.GT, QueryParser.GE, QueryParser.LESSTHAN, QueryParser.LE):
+ raise search_util.UnsupportedOnDevError(
+ 'Operator %s not supported for distance matches on development server.'
+ % str(op))
+
+ def _IsDistanceMatch(self, geopoint, op):
+ distance = geopoint - self._geopoint
+ if op == QueryParser.GT or op == QueryParser.GE:
+ return distance >= self._distance
+ if op == QueryParser.LESSTHAN or op == QueryParser.LE:
+ return distance <= self._distance
+ else:
+ raise AssertionError, 'unexpected op %s' % str(op)
+
+ def IsMatch(self, field_values, op):
+ self._CheckOp(op)
+
+
+
+ for field_value in field_values:
+ geo_pb = field_value.geo()
+ geopoint = LatLng(geo_pb.lat(), geo_pb.lng())
+ if self._IsDistanceMatch(geopoint, op):
+ return True
+
+
+
+ if field_values:
+ return False
+
+
+
+
+ return op == QueryParser.GT or op == QueryParser.GE
+
+
class DocumentMatcher(object):
"""A class to match documents with a query."""
@@ -173,6 +261,19 @@
"""Check if a numeric field matches a query tree node."""
return self._MatchComparableField(field, match, float, operator, document)
+ def _MatchGeoField(self, field, matcher, operator, document):
+ """Check if a geo field matches a query tree node."""
+
+ if not isinstance(matcher, DistanceMatcher):
+ return False
+
+ if isinstance(field, tree.CommonTree):
+ field = query_parser.GetQueryNodeText(field)
+ values = [ field.value() for field in
+ search_util.GetAllFieldInDocument(document, field) if
+ field.value().type() == document_pb.FieldValue.GEO ]
+ return matcher.IsMatch(values, operator)
+
def _MatchComparableField(
self, field, match, cast_to_type, op, document):
@@ -223,24 +324,33 @@
'Operator %s not supported for numerical fields on development server.'
% match.getText())
+ def _MatchAnyField(self, field, match, operator, document):
+ """Check if a field matches a query tree.
+
+ Args:
+ field: the name of the field, or a query node containing the field.
+ match: A query node to match the field with.
+ operator: The query node type corresponding to the type of match to
+ perform (eg QueryParser.EQ, QueryParser.GT, etc).
+ document: The document to match.
+ """
+
+ if isinstance(field, tree.CommonTree):
+ field = query_parser.GetQueryNodeText(field)
+ fields = search_util.GetAllFieldInDocument(document, field)
+ return any(self._MatchField(f, match, operator, document) for f in fields)
+
def _MatchField(self, field, match, operator, document):
"""Check if a field matches a query tree.
Args:
- field_query_node: Either a string containing the name of a field, a query
- node whose text is the name of the field, or a document_pb.Field.
+ field: a document_pb.Field instance to match.
match: A query node to match the field with.
operator: The a query node type corresponding to the type of match to
perform (eg QueryParser.EQ, QueryParser.GT, etc).
document: The document to match.
"""
- if isinstance(field, (basestring, tree.CommonTree)):
- if isinstance(field, tree.CommonTree):
- field = query_parser.GetQueryNodeText(field)
- fields = search_util.GetAllFieldInDocument(document, field)
- return any(self._MatchField(f, match, operator, document) for f in fields)
-
if field.value().type() in search_util.TEXT_DOCUMENT_FIELD_TYPES:
if operator != QueryParser.EQ:
return False
@@ -252,6 +362,13 @@
if field.value().type() == document_pb.FieldValue.DATE:
return self._MatchDateField(field, match, operator, document)
+
+
+
+
+ if field.value().type() == document_pb.FieldValue.GEO:
+ return False
+
type_name = document_pb.FieldValue.ContentType_Name(
field.value().type()).lower()
raise search_util.UnsupportedOnDevError(
@@ -261,7 +378,7 @@
def _MatchGlobal(self, match, document):
for field in document.field_list():
try:
- if self._MatchField(field.name(), match, QueryParser.EQ, document):
+ if self._MatchAnyField(field.name(), match, QueryParser.EQ, document):
return True
except search_util.UnsupportedOnDevError:
@@ -270,6 +387,29 @@
pass
return False
+ def _ResolveDistanceArg(self, node):
+ if node.getType() == QueryParser.VALUE:
+ return query_parser.GetQueryNodeText(node)
+ if node.getType() == QueryParser.FUNCTION:
+ name, args = node.children
+ if name.getText() == 'geopoint':
+ lat, lng = (float(query_parser.GetQueryNodeText(v)) for v in args.children)
+ return LatLng(lat, lng)
+ return None
+
+ def _MatchFunction(self, node, match, operator, document):
+ name, args = node.children
+ if name.getText() == 'distance':
+ x, y = args.children
+ x, y = self._ResolveDistanceArg(x), self._ResolveDistanceArg(y)
+ if isinstance(x, LatLng) and isinstance(y, basestring):
+ x, y = y, x
+ if isinstance(x, basestring) and isinstance(y, LatLng):
+ distance = float(query_parser.GetQueryNodeText(match))
+ matcher = DistanceMatcher(y, distance)
+ return self._MatchGeoField(x, matcher, operator, document)
+ return False
+
def _CheckMatch(self, node, document):
"""Check if a document matches a query tree."""
@@ -283,19 +423,17 @@
return not self._CheckMatch(node.children[0], document)
if node.getType() in query_parser.COMPARISON_TYPES:
- field, match = node.children
- if field.getType() == QueryParser.GLOBAL:
+ lhs, match = node.children
+ if lhs.getType() == QueryParser.GLOBAL:
return self._MatchGlobal(match, document)
- return self._MatchField(field, match, node.getType(), document)
+ elif lhs.getType() == QueryParser.FUNCTION:
+ return self._MatchFunction(lhs, match, node.getType(), document)
+ return self._MatchAnyField(lhs, match, node.getType(), document)
return False
def Matches(self, document):
- try:
- return self._CheckMatch(self._query, document)
- except search_util.UnsupportedOnDevError, e:
- logging.warning(str(e))
- return False
+ return self._CheckMatch(self._query, document)
def FilterDocuments(self, documents):
return (doc for doc in documents if self.Matches(doc))
diff --git a/google/appengine/datastore/datastore_index.py b/google/appengine/datastore/datastore_index.py
index eee17a1..acb9844 100644
--- a/google/appengine/datastore/datastore_index.py
+++ b/google/appengine/datastore/datastore_index.py
@@ -58,6 +58,7 @@
import itertools
+from google.appengine.api import appinfo
from google.appengine.api import datastore_types
from google.appengine.api import validation
from google.appengine.api import yaml_errors
@@ -108,6 +109,7 @@
"""
ATTRIBUTES = {
+ appinfo.APPLICATION: validation.Optional(appinfo.APPLICATION_RE_STRING),
'indexes': validation.Optional(validation.Repeated(Index)),
}
diff --git a/google/appengine/datastore/datastore_pbs.py b/google/appengine/datastore/datastore_pbs.py
index 4dfb755..2a21042 100644
--- a/google/appengine/datastore/datastore_pbs.py
+++ b/google/appengine/datastore/datastore_pbs.py
@@ -40,56 +40,80 @@
from google.appengine.datastore import entity_pb
-from google.appengine.datastore import datastore_pb
-from google.appengine.datastore import datastore_v4_pb
from google.appengine.datastore import entity_v4_pb
-_MEANING_ATOM_CATEGORY = 1
-_MEANING_URL = 2
-_MEANING_ATOM_TITLE = 3
-_MEANING_ATOM_CONTENT = 4
-_MEANING_ATOM_SUMMARY = 5
-_MEANING_ATOM_AUTHOR = 6
-_MEANING_GD_EMAIL = 8
-_MEANING_GEORSS_POINT = 9
-_MEANING_GD_IM = 10
-_MEANING_GD_PHONENUMBER = 11
-_MEANING_GD_POSTALADDRESS = 12
-_MEANING_PERCENT = 13
-_MEANING_TEXT = 15
-_MEANING_BYTESTRING = 16
-_MEANING_INDEX_ONLY = 18
-_MEANING_PREDEFINED_ENTITY_USER = 20
-_MEANING_PREDEFINED_ENTITY_POINT = 21
-_MEANING_ZLIB = 22
+MEANING_ATOM_CATEGORY = 1
+MEANING_URL = 2
+MEANING_ATOM_TITLE = 3
+MEANING_ATOM_CONTENT = 4
+MEANING_ATOM_SUMMARY = 5
+MEANING_ATOM_AUTHOR = 6
+MEANING_GD_EMAIL = 8
+MEANING_GEORSS_POINT = 9
+MEANING_GD_IM = 10
+MEANING_GD_PHONENUMBER = 11
+MEANING_GD_POSTALADDRESS = 12
+MEANING_PERCENT = 13
+MEANING_TEXT = 15
+MEANING_BYTESTRING = 16
+MEANING_INDEX_ONLY = 18
+MEANING_PREDEFINED_ENTITY_USER = 20
+MEANING_PREDEFINED_ENTITY_POINT = 21
+MEANING_ZLIB = 22
-_URI_MEANING_ZLIB = 'ZLIB'
+URI_MEANING_ZLIB = 'ZLIB'
-_MAX_INDEXED_BLOB_BYTES = 500
+MAX_INDEXED_BLOB_BYTES = 500
-_PROPERTY_NAME_X = 'x'
-_PROPERTY_NAME_Y = 'y'
+PROPERTY_NAME_X = 'x'
+PROPERTY_NAME_Y = 'y'
-_PROPERTY_NAME_EMAIL = 'email'
-_PROPERTY_NAME_AUTH_DOMAIN = 'auth_domain'
-_PROPERTY_NAME_USER_ID = 'user_id'
-_PROPERTY_NAME_INTERNAL_ID = 'internal_id'
-_PROPERTY_NAME_FEDERATED_IDENTITY = 'federated_identity'
-_PROPERTY_NAME_FEDERATED_PROVIDER = 'federated_provider'
+PROPERTY_NAME_EMAIL = 'email'
+PROPERTY_NAME_AUTH_DOMAIN = 'auth_domain'
+PROPERTY_NAME_USER_ID = 'user_id'
+PROPERTY_NAME_INTERNAL_ID = 'internal_id'
+PROPERTY_NAME_FEDERATED_IDENTITY = 'federated_identity'
+PROPERTY_NAME_FEDERATED_PROVIDER = 'federated_provider'
-_PROPERTY_NAME_KEY = '__key__'
+PROPERTY_NAME_KEY = '__key__'
-_DEFAULT_GAIA_ID = 0
+DEFAULT_GAIA_ID = 0
-def _is_valid_utf8(s):
+def v4_key_to_string(v4_key):
+ """Generates a string representing a key's path.
+
+ The output makes no effort to qualify special characters in strings.
+
+ The key need not be valid, but if any of the key path elements have
+ both a name and an ID the name is ignored.
+
+ Args:
+ v4_key: a datastore_v4_pb.Key
+
+ Returns:
+ a string representing the key's path
+ """
+ path_element_strings = []
+ for path_element in v4_key.path_element_list():
+ if path_element.has_id():
+ id_or_name = str(path_element.id())
+ elif path_element.has_name():
+ id_or_name = path_element.name()
+ else:
+ id_or_name = ''
+ path_element_strings.append('%s: %s' % (path_element.kind(), id_or_name))
+ return '[%s]' % ', '.join(path_element_strings)
+
+
+def is_valid_utf8(s):
try:
s.decode('utf-8')
return True
@@ -97,7 +121,7 @@
return False
-def _check_conversion(condition, message):
+def check_conversion(condition, message):
"""Asserts a conversion condition and raises an error if it's not met.
Args:
@@ -141,6 +165,22 @@
if v4_element.has_name():
v3_element.set_name(v4_element.name())
+ def v4_to_v3_references(self, v4_keys):
+ """Converts a list of v4 Keys to a list of v3 References.
+
+ Args:
+ v4_keys: a list of entity_v4_pb.Key objects
+
+ Returns:
+ a list of entity_pb.Reference objects
+ """
+ v3_refs = []
+ for v4_key in v4_keys:
+ v3_ref = entity_pb.Reference()
+ self.v4_to_v3_reference(v4_key, v3_ref)
+ v3_refs.append(v3_ref)
+ return v3_refs
+
def v3_to_v4_key(self, v3_ref, v4_key):
"""Converts a v3 Reference to a v4 Key.
@@ -162,6 +202,22 @@
if v3_element.has_name():
v4_element.set_name(v3_element.name())
+ def v3_to_v4_keys(self, v3_refs):
+ """Converts a list of v3 References to a list of v4 Keys.
+
+ Args:
+ v3_refs: a list of entity_pb.Reference objects
+
+ Returns:
+ a list of entity_v4_pb.Key objects
+ """
+ v4_keys = []
+ for v3_ref in v3_refs:
+ v4_key = entity_v4_pb.Key()
+ self.v3_to_v4_key(v3_ref, v4_key)
+ v4_keys.append(v4_key)
+ return v4_keys
+
def v4_to_v3_entity(self, v4_entity, v3_entity):
"""Converts a v4 Entity to a v3 EntityProto.
@@ -189,7 +245,7 @@
v3_ref = v3_entity.key()
if (self.__v3_reference_has_id_or_name(v3_ref)
or v3_ref.path().element_size() > 1):
- self._v3_reference_to_group(v3_ref, v3_entity.mutable_entity_group())
+ self.v3_reference_to_group(v3_ref, v3_entity.mutable_entity_group())
else:
@@ -238,7 +294,7 @@
elif v4_value.has_key_value():
v3_ref = entity_pb.Reference()
self.v4_to_v3_reference(v4_value.key_value(), v3_ref)
- self._v3_reference_to_v3_property_value(v3_ref, v3_value)
+ self.v3_reference_to_v3_property_value(v3_ref, v3_value)
elif v4_value.has_blob_key_value():
v3_value.set_stringvalue(v4_value.blob_key_value())
elif v4_value.has_string_value():
@@ -248,11 +304,11 @@
elif v4_value.has_entity_value():
v4_entity_value = v4_value.entity_value()
v4_meaning = v4_value.meaning()
- if (v4_meaning == _MEANING_GEORSS_POINT
- or v4_meaning == _MEANING_PREDEFINED_ENTITY_POINT):
+ if (v4_meaning == MEANING_GEORSS_POINT
+ or v4_meaning == MEANING_PREDEFINED_ENTITY_POINT):
self.__v4_to_v3_point_value(v4_entity_value,
v3_value.mutable_pointvalue())
- elif v4_meaning == _MEANING_PREDEFINED_ENTITY_USER:
+ elif v4_meaning == MEANING_PREDEFINED_ENTITY_USER:
self.__v4_to_v3_user_value(v4_entity_value,
v3_value.mutable_uservalue())
else:
@@ -292,7 +348,7 @@
is_zlib_value = False
if v3_uri_meaning:
- if v3_uri_meaning == _URI_MEANING_ZLIB:
+ if v3_uri_meaning == URI_MEANING_ZLIB:
if v3_property_value.has_stringvalue():
is_zlib_value = True
if v3_meaning != entity_pb.Property.BLOB:
@@ -337,7 +393,7 @@
v3_meaning = None
else:
string_value = v3_property_value.stringvalue()
- if _is_valid_utf8(string_value):
+ if is_valid_utf8(string_value):
if v3_meaning == entity_pb.Property.BLOBKEY:
v4_value.set_blob_key_value(string_value)
v3_meaning = None
@@ -355,17 +411,17 @@
self.__v3_to_v4_point_entity(v3_property_value.pointvalue(),
v4_value.mutable_entity_value())
if v3_meaning != entity_pb.Property.GEORSS_POINT:
- v4_value.set_meaning(_MEANING_PREDEFINED_ENTITY_POINT)
+ v4_value.set_meaning(MEANING_PREDEFINED_ENTITY_POINT)
v3_meaning = None
elif v3_property_value.has_uservalue():
self.__v3_to_v4_user_entity(v3_property_value.uservalue(),
v4_value.mutable_entity_value())
- v4_value.set_meaning(_MEANING_PREDEFINED_ENTITY_USER)
+ v4_value.set_meaning(MEANING_PREDEFINED_ENTITY_USER)
else:
pass
if is_zlib_value:
- v4_value.set_meaning(_MEANING_ZLIB)
+ v4_value.set_meaning(MEANING_ZLIB)
elif v3_meaning:
v4_value.set_meaning(v3_meaning)
@@ -399,8 +455,8 @@
elif v4_value.has_blob_key_value():
v3_property.set_meaning(entity_pb.Property.BLOBKEY)
elif v4_value.has_blob_value():
- if v4_meaning == _MEANING_ZLIB:
- v3_property.set_meaning_uri(_URI_MEANING_ZLIB)
+ if v4_meaning == MEANING_ZLIB:
+ v3_property.set_meaning_uri(URI_MEANING_ZLIB)
if v4_meaning == entity_pb.Property.BYTESTRING:
if v4_value.indexed():
pass
@@ -413,9 +469,9 @@
v3_property.set_meaning(entity_pb.Property.BLOB)
v4_meaning = None
elif v4_value.has_entity_value():
- if v4_meaning != _MEANING_GEORSS_POINT:
- if (v4_meaning != _MEANING_PREDEFINED_ENTITY_POINT
- and v4_meaning != _MEANING_PREDEFINED_ENTITY_USER):
+ if v4_meaning != MEANING_GEORSS_POINT:
+ if (v4_meaning != MEANING_PREDEFINED_ENTITY_POINT
+ and v4_meaning != MEANING_PREDEFINED_ENTITY_USER):
v3_property.set_meaning(entity_pb.Property.ENTITY_PROTO)
v4_meaning = None
else:
@@ -607,9 +663,9 @@
"""
v4_entity.Clear()
v4_entity.property_list().append(
- self.__v4_double_property(_PROPERTY_NAME_X, v3_point_value.x(), False))
+ self.__v4_double_property(PROPERTY_NAME_X, v3_point_value.x(), False))
v4_entity.property_list().append(
- self.__v4_double_property(_PROPERTY_NAME_Y, v3_point_value.y(), False))
+ self.__v4_double_property(PROPERTY_NAME_Y, v3_point_value.y(), False))
def __v4_to_v3_user_value(self, v4_user_entity, v3_user_value):
"""Converts a v4 user Entity to a v3 UserValue.
@@ -622,27 +678,27 @@
name_to_v4_property = self.__build_name_to_v4_property_map(v4_user_entity)
v3_user_value.set_email(self.__get_single_v4_string_value(
- name_to_v4_property[_PROPERTY_NAME_EMAIL]))
+ name_to_v4_property[PROPERTY_NAME_EMAIL]))
v3_user_value.set_auth_domain(self.__get_single_v4_string_value(
- name_to_v4_property[_PROPERTY_NAME_AUTH_DOMAIN]))
- if _PROPERTY_NAME_USER_ID in name_to_v4_property:
+ name_to_v4_property[PROPERTY_NAME_AUTH_DOMAIN]))
+ if PROPERTY_NAME_USER_ID in name_to_v4_property:
v3_user_value.set_obfuscated_gaiaid(
self.__get_single_v4_string_value(
- name_to_v4_property[_PROPERTY_NAME_USER_ID]))
- if _PROPERTY_NAME_INTERNAL_ID in name_to_v4_property:
+ name_to_v4_property[PROPERTY_NAME_USER_ID]))
+ if PROPERTY_NAME_INTERNAL_ID in name_to_v4_property:
v3_user_value.set_gaiaid(self.__get_single_v4_integer_value(
- name_to_v4_property[_PROPERTY_NAME_INTERNAL_ID]))
+ name_to_v4_property[PROPERTY_NAME_INTERNAL_ID]))
else:
v3_user_value.set_gaiaid(0)
- if _PROPERTY_NAME_FEDERATED_IDENTITY in name_to_v4_property:
+ if PROPERTY_NAME_FEDERATED_IDENTITY in name_to_v4_property:
v3_user_value.set_federated_identity(
self.__get_single_v4_string_value(name_to_v4_property[
- _PROPERTY_NAME_FEDERATED_IDENTITY]))
- if _PROPERTY_NAME_FEDERATED_PROVIDER in name_to_v4_property:
+ PROPERTY_NAME_FEDERATED_IDENTITY]))
+ if PROPERTY_NAME_FEDERATED_PROVIDER in name_to_v4_property:
v3_user_value.set_federated_provider(
self.__get_single_v4_string_value(name_to_v4_property[
- _PROPERTY_NAME_FEDERATED_PROVIDER]))
+ PROPERTY_NAME_FEDERATED_PROVIDER]))
def __v3_to_v4_user_entity(self, v3_user_value, v4_entity):
"""Converts a v3 UserValue to a v4 user Entity.
@@ -653,30 +709,30 @@
"""
v4_entity.Clear()
v4_entity.property_list().append(
- self.__v4_string_property(_PROPERTY_NAME_EMAIL, v3_user_value.email(),
+ self.__v4_string_property(PROPERTY_NAME_EMAIL, v3_user_value.email(),
False))
v4_entity.property_list().append(self.__v4_string_property(
- _PROPERTY_NAME_AUTH_DOMAIN,
+ PROPERTY_NAME_AUTH_DOMAIN,
v3_user_value.auth_domain(), False))
if v3_user_value.gaiaid() != 0:
v4_entity.property_list().append(self.__v4_integer_property(
- _PROPERTY_NAME_INTERNAL_ID,
+ PROPERTY_NAME_INTERNAL_ID,
v3_user_value.gaiaid(),
False))
if v3_user_value.has_obfuscated_gaiaid():
v4_entity.property_list().append(self.__v4_string_property(
- _PROPERTY_NAME_USER_ID,
+ PROPERTY_NAME_USER_ID,
v3_user_value.obfuscated_gaiaid(),
False))
if v3_user_value.has_federated_identity():
v4_entity.property_list().append(self.__v4_string_property(
- _PROPERTY_NAME_FEDERATED_IDENTITY,
+ PROPERTY_NAME_FEDERATED_IDENTITY,
v3_user_value.federated_identity(),
False))
if v3_user_value.has_federated_provider():
v4_entity.property_list().append(self.__v4_string_property(
- _PROPERTY_NAME_FEDERATED_PROVIDER,
+ PROPERTY_NAME_FEDERATED_PROVIDER,
v3_user_value.federated_provider(),
False))
@@ -750,7 +806,7 @@
last_element = path.element(path.element_size() - 1)
return last_element.has_id() or last_element.has_name()
- def _v3_reference_to_group(self, v3_ref, group):
+ def v3_reference_to_group(self, v3_ref, group):
"""Converts a v3 Reference to a v3 Path representing the entity group.
The entity group is represented as an entity_pb.Path containing only the
@@ -765,7 +821,7 @@
assert path.element_size() >= 1
group.add_element().CopyFrom(path.element(0))
- def _v3_reference_to_v3_property_value(self, v3_ref, v3_property_value):
+ def v3_reference_to_v3_property_value(self, v3_ref, v3_property_value):
"""Converts a v3 Reference to a v3 PropertyValue.
Args:
@@ -816,788 +872,3 @@
def get_entity_converter():
"""Returns a converter for v3 and v4 entities and keys."""
return __entity_converter
-
-
-class _BaseQueryConverter(object):
- """Base converter for queries."""
-
- def __init__(self, entity_converter):
- self._entity_converter = entity_converter
-
- def v4_to_v3_compiled_cursor(self, v4_cursor, v3_compiled_cursor):
- """Converts a v4 cursor string to a v3 CompiledCursor.
-
- Args:
- v4_cursor: a string representing a v4 query cursor
- v3_compiled_cursor: a datastore_pb.CompiledCursor to populate
- """
- raise NotImplementedError
-
- def v3_to_v4_compiled_cursor(self, v3_compiled_cursor):
- """Converts a v3 CompiledCursor to a v4 cursor string.
-
- Args:
- v3_compiled_cursor: a datastore_pb.CompiledCursor
-
- Returns:
- a string representing a v4 query cursor
- """
- raise NotImplementedError
-
- def v4_to_v3_query(self, v4_partition_id, v4_query, v3_query):
- """Converts a v4 Query to a v3 Query.
-
- Args:
- v4_partition_id: a datastore_v4_pb.PartitionId
- v4_query: a datastore_v4_pb.Query
- v3_query: a datastore_pb.Query to populate
-
- Raises:
- InvalidConversionError if the query cannot be converted
- """
- v3_query.Clear()
-
- if v4_partition_id.dataset_id():
- v3_query.set_app(v4_partition_id.dataset_id())
- if v4_partition_id.has_namespace():
- v3_query.set_name_space(v4_partition_id.namespace())
-
- v3_query.set_persist_offset(True)
- v3_query.set_require_perfect_plan(True)
- v3_query.set_compile(True)
-
-
- if v4_query.has_limit():
- v3_query.set_limit(v4_query.limit())
- if v4_query.offset():
- v3_query.set_offset(v4_query.offset())
- if v4_query.has_start_cursor():
- self.v4_to_v3_compiled_cursor(v4_query.start_cursor(),
- v3_query.mutable_compiled_cursor())
- if v4_query.has_end_cursor():
- self.v4_to_v3_compiled_cursor(v4_query.end_cursor(),
- v3_query.mutable_end_compiled_cursor())
-
-
- if v4_query.kind_list():
- _check_conversion(len(v4_query.kind_list()) == 1,
- 'multiple kinds not supported')
- v3_query.set_kind(v4_query.kind(0).name())
-
-
- has_key_projection = False
- for prop in v4_query.projection_list():
- if prop.property().name() == _PROPERTY_NAME_KEY:
- has_key_projection = True
- else:
- v3_query.add_property_name(prop.property().name())
- if has_key_projection and not v3_query.property_name_list():
- v3_query.set_keys_only(True)
-
-
- for prop in v4_query.group_by_list():
- v3_query.add_group_by_property_name(prop.name())
-
-
- self.__populate_v3_filters(v4_query.filter(), v3_query)
-
-
- for v4_order in v4_query.order_list():
- v3_order = v3_query.add_order()
- v3_order.set_property(v4_order.property().name())
- if v4_order.has_direction():
- v3_order.set_direction(v4_order.direction())
-
- def v3_to_v4_query(self, v3_query, v4_query):
- """Converts a v3 Query to a v4 Query.
-
- Args:
- v3_query: a datastore_pb.Query
- v4_query: a datastore_v4_pb.Query to populate
-
- Raises:
- InvalidConversionError if the query cannot be converted
- """
- v4_query.Clear()
-
- _check_conversion(not v3_query.has_distinct(),
- 'distinct option not supported')
- _check_conversion(v3_query.require_perfect_plan(),
- 'non-perfect plans not supported')
-
-
-
- if v3_query.has_limit():
- v4_query.set_limit(v3_query.limit())
- if v3_query.offset():
- v4_query.set_offset(v3_query.offset())
- if v3_query.has_compiled_cursor():
- v4_query.set_start_cursor(
- self.v3_to_v4_compiled_cursor(v3_query.compiled_cursor()))
- if v3_query.has_end_compiled_cursor():
- v4_query.set_end_cursor(
- self.v3_to_v4_compiled_cursor(v3_query.end_compiled_cursor()))
-
-
- if v3_query.has_kind():
- v4_query.add_kind().set_name(v3_query.kind())
-
-
- for name in v3_query.property_name_list():
- v4_query.add_projection().mutable_property().set_name(name)
- if v3_query.keys_only():
- v4_query.add_projection().mutable_property().set_name(_PROPERTY_NAME_KEY)
-
-
- for name in v3_query.group_by_property_name_list():
- v4_query.add_group_by().set_name(name)
-
-
- num_v4_filters = len(v3_query.filter_list())
- if v3_query.has_ancestor():
- num_v4_filters += 1
-
- if num_v4_filters == 1:
- get_property_filter = self.__get_property_filter
- elif num_v4_filters >= 1:
- v4_query.mutable_filter().mutable_composite_filter().set_operator(
- datastore_v4_pb.CompositeFilter.AND)
- get_property_filter = self.__add_property_filter
-
- if v3_query.has_ancestor():
- self.__v3_query_to_v4_ancestor_filter(v3_query,
- get_property_filter(v4_query))
- for v3_filter in v3_query.filter_list():
- self.__v3_filter_to_v4_property_filter(v3_filter,
- get_property_filter(v4_query))
-
-
- for v3_order in v3_query.order_list():
- v4_order = v4_query.add_order()
- v4_order.mutable_property().set_name(v3_order.property())
- if v3_order.has_direction():
- v4_order.set_direction(v3_order.direction())
-
- def __get_property_filter(self, v4_query):
- """Returns the PropertyFilter from the query's top-level filter."""
- return v4_query.mutable_filter().mutable_property_filter()
-
- def __add_property_filter(self, v4_query):
- """Adds and returns a PropertyFilter from the query's composite filter."""
- v4_comp_filter = v4_query.mutable_filter().mutable_composite_filter()
- return v4_comp_filter.add_filter().mutable_property_filter()
-
- def __populate_v3_filters(self, v4_filter, v3_query):
- """Populates a filters for a v3 Query.
-
- Args:
- v4_filter: a datastore_v4_pb.Filter
- v3_query: a datastore_pb.Query to populate with filters
- """
- if v4_filter.has_property_filter():
- v4_property_filter = v4_filter.property_filter()
- if (v4_property_filter.operator()
- == datastore_v4_pb.PropertyFilter.HAS_ANCESTOR):
- _check_conversion(v4_property_filter.value().has_key_value(),
- 'HAS_ANCESTOR requires a reference value')
- _check_conversion((v4_property_filter.property().name()
- == _PROPERTY_NAME_KEY),
- 'unsupported property')
- _check_conversion(not v3_query.has_ancestor(),
- 'duplicate ancestor constraint')
- self._entity_converter.v4_to_v3_reference(
- v4_property_filter.value().key_value(),
- v3_query.mutable_ancestor())
- else:
- v3_filter = v3_query.add_filter()
- property_name = v4_property_filter.property().name()
- v3_filter.set_op(v4_property_filter.operator())
- _check_conversion(not v4_property_filter.value().list_value_list(),
- ('unsupported value type, %s, in property filter'
- ' on "%s"' % ('list_value', property_name)))
- prop = v3_filter.add_property()
- prop.set_multiple(False)
- prop.set_name(property_name)
- self._entity_converter.v4_value_to_v3_property_value(
- v4_property_filter.value(), prop.mutable_value())
- elif v4_filter.has_composite_filter():
- _check_conversion((v4_filter.composite_filter().operator()
- == datastore_v4_pb.CompositeFilter.AND),
- 'unsupported composite property operator')
- for v4_sub_filter in v4_filter.composite_filter().filter_list():
- self.__populate_v3_filters(v4_sub_filter, v3_query)
-
- def __v3_filter_to_v4_property_filter(self, v3_filter, v4_property_filter):
- """Converts a v3 Filter to a v4 PropertyFilter.
-
- Args:
- v3_filter: a datastore_pb.Filter
- v4_property_filter: a datastore_v4_pb.PropertyFilter to populate
-
- Raises:
- InvalidConversionError if the filter cannot be converted
- """
- _check_conversion(v3_filter.property_size() == 1, 'invalid filter')
- _check_conversion(v3_filter.op() <= 5,
- 'unsupported filter op: %d' % v3_filter.op())
- v4_property_filter.Clear()
- v4_property_filter.set_operator(v3_filter.op())
- v4_property_filter.mutable_property().set_name(v3_filter.property(0).name())
- self._entity_converter.v3_property_to_v4_value(
- v3_filter.property(0), True, v4_property_filter.mutable_value())
-
- def __v3_query_to_v4_ancestor_filter(self, v3_query, v4_property_filter):
- """Converts a v3 Query to a v4 ancestor PropertyFilter.
-
- Args:
- v3_query: a datastore_pb.Query
- v4_property_filter: a datastore_v4_pb.PropertyFilter to populate
- """
- v4_property_filter.Clear()
- v4_property_filter.set_operator(
- datastore_v4_pb.PropertyFilter.HAS_ANCESTOR)
- prop = v4_property_filter.mutable_property()
- prop.set_name(_PROPERTY_NAME_KEY)
- self._entity_converter.v3_to_v4_key(
- v3_query.ancestor(),
- v4_property_filter.mutable_value().mutable_key_value())
-
-
-class _StubQueryConverter(_BaseQueryConverter):
- """A query converter suitable for use in stubs."""
-
- def v4_to_v3_compiled_cursor(self, v4_cursor, v3_compiled_cursor):
- v3_compiled_cursor.Clear()
- v3_compiled_cursor.ParseFromString(v4_cursor)
-
- def v3_to_v4_compiled_cursor(self, v3_compiled_cursor):
- return v3_compiled_cursor.SerializeToString()
-
-
-
-__stub_query_converter = _StubQueryConverter(__entity_converter)
-
-
-def get_stub_query_converter():
- """Returns a converter for v3 and v4 queries (not suitable for production).
-
- This converter is suitable for use in stubs but not for production.
-
- Returns:
- a _StubQueryConverter
- """
- return __stub_query_converter
-
-
-class _BaseServiceConverter(object):
- """Base converter for v3 and v4 request/response protos."""
-
- def __init__(self, entity_converter, query_converter):
- self._entity_converter = entity_converter
- self._query_converter = query_converter
-
- def v4_to_v3_cursor(self, v4_query_handle, v3_cursor):
- """Converts a v4 cursor string to a v3 Cursor.
-
- Args:
- v4_query_handle: a string representing a v4 query handle
- v3_cursor: a datastore_pb.Cursor to populate
- """
- raise NotImplementedError
-
- def _v3_to_v4_query_handle(self, v3_cursor):
- """Converts a v3 Cursor to a v4 query handle string.
-
- Args:
- v3_cursor: a datastore_pb.Cursor
-
- Returns:
- a string representing a v4 cursor
- """
- raise NotImplementedError
-
- def v4_to_v3_txn(self, v4_txn, v3_txn):
- """Converts a v4 transaction string to a v3 Transaction.
-
- Args:
- v4_txn: a string representing a v4 transaction
- v3_txn: a datastore_pb.Transaction to populate
- """
- raise NotImplementedError
-
- def _v3_to_v4_txn(self, v3_txn):
- """Converts a v3 Transaction to a v4 transaction string.
-
- Args:
- v3_txn: a datastore_pb.Transaction
-
- Returns:
- a string representing a v4 transaction
- """
- raise NotImplementedError
-
-
-
-
- def v4_to_v3_begin_transaction_req(self, app_id, v4_req):
- """Converts a v4 BeginTransactionRequest to a v3 BeginTransactionRequest.
-
- Args:
- app_id: app id
- v4_req: a datastore_v4_pb.BeginTransactionRequest
-
- Returns:
- a datastore_pb.BeginTransactionRequest
- """
- v3_req = datastore_pb.BeginTransactionRequest()
- v3_req.set_app(app_id)
- v3_req.set_allow_multiple_eg(v4_req.cross_group())
- return v3_req
-
- def v3_to_v4_begin_transaction_req(self, v3_req):
- """Converts a v3 BeginTransactionRequest to a v4 BeginTransactionRequest.
-
- Args:
- v3_req: a datastore_pb.BeginTransactionRequest
-
- Returns:
- a datastore_v4_pb.BeginTransactionRequest
- """
- v4_req = datastore_v4_pb.BeginTransactionRequest()
-
- if v3_req.has_allow_multiple_eg():
- v4_req.set_cross_group(v3_req.allow_multiple_eg())
-
- return v4_req
-
- def v4_begin_transaction_resp_to_v3_txn(self, v4_resp):
- """Converts a v4 BeginTransactionResponse to a v3 Transaction.
-
- Args:
- v4_resp: datastore_v4_pb.BeginTransactionResponse
-
- Returns:
- a a datastore_pb.Transaction
- """
- v3_txn = datastore_pb.Transaction()
- self.v4_to_v3_txn(v4_resp.transaction(), v3_txn)
- return v3_txn
-
- def v3_to_v4_begin_transaction_resp(self, v3_resp):
- """Converts a v3 Transaction to a v4 BeginTransactionResponse.
-
- Args:
- v3_resp: a datastore_pb.Transaction
-
- Returns:
- a datastore_v4_pb.BeginTransactionResponse
- """
- v4_resp = datastore_v4_pb.BeginTransactionResponse()
- v4_resp.set_transaction(self._v3_to_v4_txn(v3_resp))
- return v4_resp
-
-
-
-
- def v4_rollback_req_to_v3_txn(self, v4_req):
- """Converts a v4 RollbackRequest to a v3 Transaction.
-
- Args:
- v4_req: a datastore_v4_pb.RollbackRequest
-
- Returns:
- a datastore_pb.Transaction
- """
- v3_txn = datastore_pb.Transaction()
- self.v4_to_v3_txn(v4_req.transaction(), v3_txn)
- return v3_txn
-
- def v3_to_v4_rollback_req(self, v3_req):
- """Converts a v3 Transaction to a v4 RollbackRequest.
-
- Args:
- v3_req: datastore_pb.Transaction
-
- Returns:
- a a datastore_v4_pb.RollbackRequest
- """
- v4_req = datastore_v4_pb.RollbackRequest()
- v4_req.set_transaction(self._v3_to_v4_txn(v3_req))
- return v4_req
-
-
-
-
- def v4_commit_req_to_v3_txn(self, v4_req):
- """Converts a v4 CommitRequest to a v3 Transaction.
-
- Args:
- v4_req: a datastore_v4_pb.CommitRequest
-
- Returns:
- a datastore_pb.Transaction
- """
- v3_txn = datastore_pb.Transaction()
- self.v4_to_v3_txn(v4_req.transaction(), v3_txn)
- return v3_txn
-
-
-
-
- def v4_run_query_req_to_v3_query(self, v4_req):
- """Converts a v4 RunQueryRequest to a v3 Query.
-
- GQL is not supported.
-
- Args:
- v4_req: a datastore_v4_pb.RunQueryRequest
-
- Returns:
- a datastore_pb.Query
- """
-
- _check_conversion(not v4_req.has_gql_query(), 'GQL not supported')
- v3_query = datastore_pb.Query()
- self._query_converter.v4_to_v3_query(v4_req.partition_id(), v4_req.query(),
- v3_query)
-
-
- if v4_req.has_suggested_batch_size():
- v3_query.set_count(v4_req.suggested_batch_size())
-
-
- read_options = v4_req.read_options()
- if read_options.has_transaction():
- self.v4_to_v3_txn(read_options.transaction(),
- v3_query.mutable_transaction())
- elif (read_options.read_consistency()
- == datastore_v4_pb.ReadOptions.EVENTUAL):
- v3_query.set_strong(False)
- v3_query.set_failover_ms(-1)
- elif read_options.read_consistency() == datastore_v4_pb.ReadOptions.STRONG:
- v3_query.set_strong(True)
-
- if v4_req.has_min_safe_time_seconds():
- v3_query.set_min_safe_time_seconds(v4_req.min_safe_time_seconds())
-
- return v3_query
-
- def v3_to_v4_run_query_req(self, v3_req):
- """Converts a v3 Query to a v4 RunQueryRequest.
-
- Args:
- v3_req: a datastore_pb.Query
-
- Returns:
- a datastore_v4_pb.RunQueryRequest
- """
- v4_req = datastore_v4_pb.RunQueryRequest()
-
-
- v4_partition_id = v4_req.mutable_partition_id()
- v4_partition_id.set_dataset_id(v3_req.app())
- if v3_req.name_space():
- v4_partition_id.set_namespace(v3_req.name_space())
-
-
- if v3_req.has_count():
- v4_req.set_suggested_batch_size(v3_req.count())
-
-
- if v3_req.has_transaction():
- v4_req.mutable_read_options().set_transaction(
- self._v3_to_v4_txn(v3_req.transaction()))
- elif v3_req.strong():
- v4_req.mutable_read_options().set_read_consistency(
- datastore_v4_pb.ReadOptions.STRONG)
- elif v3_req.has_failover_ms():
- v4_req.mutable_read_options().set_read_consistency(
- datastore_v4_pb.ReadOptions.EVENTUAL)
- if v3_req.has_min_safe_time_seconds():
- v4_req.set_min_safe_time_seconds(v3_req.min_safe_time_seconds())
-
- self._query_converter.v3_to_v4_query(v3_req, v4_req.mutable_query())
-
- return v4_req
-
- def v4_run_query_resp_to_v3_query_result(self, v4_resp):
- """Converts a V4 RunQueryResponse to a v3 QueryResult.
-
- Args:
- v4_resp: a datastore_v4_pb.QueryResult
-
- Returns:
- a datastore_pb.QueryResult
- """
- v3_resp = self.v4_to_v3_query_result(v4_resp.batch())
-
-
- if v4_resp.has_query_handle():
- self.v4_to_v3_cursor(v4_resp.query_handle(), v3_resp.mutable_cursor())
-
- return v3_resp
-
- def v3_to_v4_run_query_resp(self, v3_resp):
- """Converts a v3 QueryResult to a V4 RunQueryResponse.
-
- Args:
- v3_resp: a datastore_pb.QueryResult
-
- Returns:
- a datastore_v4_pb.RunQueryResponse
- """
- v4_resp = datastore_v4_pb.RunQueryResponse()
- self.v3_to_v4_query_result_batch(v3_resp, v4_resp.mutable_batch())
-
- if v3_resp.has_cursor():
- v4_resp.set_query_handle(
- self._query_converter.v3_to_v4_compiled_cursor(v3_resp.cursor()))
-
- return v4_resp
-
-
-
-
- def v4_to_v3_next_req(self, v4_req):
- """Converts a v4 ContinueQueryRequest to a v3 NextRequest.
-
- Args:
- v4_req: a datastore_v4_pb.ContinueQueryRequest
-
- Returns:
- a datastore_pb.NextRequest
- """
- v3_req = datastore_pb.NextRequest()
- v3_req.set_compile(True)
- self.v4_to_v3_cursor(v4_req.query_handle(), v3_req.mutable_cursor())
- return v3_req
-
- def v3_to_v4_continue_query_resp(self, v3_resp):
- """Converts a v3 QueryResult to a v4 ContinueQueryResponse.
-
- Args:
- v3_resp: a datstore_pb.QueryResult
-
- Returns:
- a datastore_v4_pb.ContinueQueryResponse
- """
- v4_resp = datastore_v4_pb.ContinueQueryResponse()
- self.v3_to_v4_query_result_batch(v3_resp, v4_resp.mutable_batch())
- return v4_resp
-
-
-
-
- def v4_to_v3_get_req(self, v4_req):
- """Converts a v4 LookupRequest to a v3 GetRequest.
-
- Args:
- v4_req: a datastore_v4_pb.LookupRequest
-
- Returns:
- a datastore_pb.GetRequest
- """
- v3_req = datastore_pb.GetRequest()
- v3_req.set_allow_deferred(True)
-
-
- if v4_req.read_options().has_transaction():
- self.v4_to_v3_txn(v4_req.read_options().transaction(),
- v3_req.mutable_transaction())
- elif (v4_req.read_options().read_consistency()
- == datastore_v4_pb.ReadOptions.EVENTUAL):
- v3_req.set_strong(False)
- v3_req.set_failover_ms(-1)
- elif (v4_req.read_options().read_consistency()
- == datastore_v4_pb.ReadOptions.STRONG):
- v3_req.set_strong(True)
-
- for v4_key in v4_req.key_list():
- self._entity_converter.v4_to_v3_reference(v4_key, v3_req.add_key())
-
- return v3_req
-
- def v3_to_v4_lookup_req(self, v3_req):
- """Converts a v3 GetRequest to a v4 LookupRequest.
-
- Args:
- v3_req: a datastore_pb.GetRequest
-
- Returns:
- a datastore_v4_pb.LookupRequest
- """
- v4_req = datastore_v4_pb.LookupRequest()
- _check_conversion(v3_req.allow_deferred(), 'allow_deferred must be true')
-
-
- if v3_req.has_transaction():
- v4_req.mutable_read_options().set_transaction(
- self._v3_to_v4_txn(v3_req.transaction()))
- elif v3_req.strong():
- v4_req.mutable_read_options().set_read_consistency(
- datastore_v4_pb.ReadOptions.STRONG)
- elif v3_req.has_failover_ms():
- v4_req.mutable_read_options().set_read_consistency(
- datastore_v4_pb.ReadOptions.EVENTUAL)
-
- for v3_ref in v3_req.key_list():
- self._entity_converter.v3_to_v4_key(v3_ref, v4_req.add_key())
-
- return v4_req
-
- def v4_to_v3_get_resp(self, v4_resp):
- """Converts a v4 LookupResponse to a v3 GetResponse.
-
- Args:
- v4_resp: a datastore_v4_pb.LookupResponse
-
- Returns:
- a datastore_pb.GetResponse
- """
- v3_resp = datastore_pb.GetResponse()
-
- for v4_key in v4_resp.deferred_list():
- self._entity_converter.v4_to_v3_reference(v4_key, v3_resp.add_deferred())
- for v4_found in v4_resp.found_list():
- self._entity_converter.v4_to_v3_entity(
- v4_found.entity(), v3_resp.add_entity().mutable_entity())
- for v4_missing in v4_resp.missing_list():
- self._entity_converter.v4_to_v3_reference(
- v4_missing.entity().key(),
- v3_resp.add_entity().mutable_key())
-
- return v3_resp
-
- def v3_to_v4_lookup_resp(self, v3_resp):
- """Converts a v3 GetResponse to a v4 LookupResponse.
-
- Args:
- v3_resp: a datastore_pb.GetResponse
-
- Returns:
- a datastore_v4_pb.LookupResponse
- """
- v4_resp = datastore_v4_pb.LookupResponse()
-
- for v3_ref in v3_resp.deferred_list():
- self._entity_converter.v3_to_v4_key(v3_ref, v4_resp.add_deferred())
- for v3_entity in v3_resp.entity_list():
- if v3_entity.has_entity():
- self._entity_converter.v3_to_v4_entity(
- v3_entity.entity(),
- v4_resp.add_found().mutable_entity())
- if v3_entity.has_key():
- self._entity_converter.v3_to_v4_key(
- v3_entity.key(),
- v4_resp.add_missing().mutable_entity().mutable_key())
-
- return v4_resp
-
- def v4_to_v3_query_result(self, v4_batch):
- """Converts a v4 QueryResultBatch to a v3 QueryResult.
-
- Args:
- v4_batch: a datastore_v4_pb.QueryResultBatch
-
- Returns:
- a datastore_pb.QueryResult
- """
- v3_result = datastore_pb.QueryResult()
-
-
- v3_result.set_more_results(
- (v4_batch.more_results()
- == datastore_v4_pb.QueryResultBatch.NOT_FINISHED))
- if v4_batch.has_end_cursor():
- self._query_converter.v4_to_v3_compiled_cursor(
- v4_batch.end_cursor(), v3_result.mutable_compiled_cursor())
-
-
- if v4_batch.entity_result_type() == datastore_v4_pb.EntityResult.PROJECTION:
- v3_result.set_index_only(True)
- elif v4_batch.entity_result_type() == datastore_v4_pb.EntityResult.KEY_ONLY:
- v3_result.set_keys_only(True)
-
-
- if v4_batch.has_skipped_results():
- v3_result.set_skipped_results(v4_batch.skipped_results())
- for v4_entity in v4_batch.entity_result_list():
- v3_entity = v3_result.add_result()
- self._entity_converter.v4_to_v3_entity(v4_entity.entity(), v3_entity)
- if v4_batch.entity_result_type() != datastore_v4_pb.EntityResult.FULL:
-
-
- v3_entity.clear_entity_group()
-
- return v3_result
-
- def v3_to_v4_query_result_batch(self, v3_result, v4_batch):
- """Converts a v3 QueryResult to a v4 QueryResultBatch.
-
- Args:
- v3_result: a datastore_pb.QueryResult
- v4_batch: a datastore_v4_pb.QueryResultBatch to populate
- """
- v4_batch.Clear()
-
-
- if v3_result.more_results():
- v4_batch.set_more_results(datastore_v4_pb.QueryResultBatch.NOT_FINISHED)
- else:
- v4_batch.set_more_results(
- datastore_v4_pb.QueryResultBatch.MORE_RESULTS_AFTER_LIMIT)
- if v3_result.has_compiled_cursor():
- v4_batch.set_end_cursor(
- self._query_converter.v3_to_v4_compiled_cursor(
- v3_result.compiled_cursor()))
-
-
- if v3_result.keys_only():
- v4_batch.set_entity_result_type(datastore_v4_pb.EntityResult.KEY_ONLY)
- elif v3_result.index_only():
- v4_batch.set_entity_result_type(datastore_v4_pb.EntityResult.PROJECTION)
- else:
- v4_batch.set_entity_result_type(datastore_v4_pb.EntityResult.FULL)
-
-
- if v3_result.has_skipped_results():
- v4_batch.set_skipped_results(v3_result.skipped_results())
- for v3_entity in v3_result.result_list():
- v4_entity_result = datastore_v4_pb.EntityResult()
- self._entity_converter.v3_to_v4_entity(v3_entity,
- v4_entity_result.mutable_entity())
- v4_batch.entity_result_list().append(v4_entity_result)
-
-
-class _StubServiceConverter(_BaseServiceConverter):
- """Converter for request/response protos suitable for use in stubs."""
-
- def v4_to_v3_cursor(self, v4_query_handle, v3_cursor):
- v3_cursor.ParseFromString(v4_query_handle)
- return v3_cursor
-
- def _v3_to_v4_query_handle(self, v3_cursor):
- return v3_cursor.SerializeToString()
-
- def v4_to_v3_txn(self, v4_txn, v3_txn):
- v3_txn.ParseFromString(v4_txn)
- return v3_txn
-
- def _v3_to_v4_txn(self, v3_txn):
- return v3_txn.SerializeToString()
-
-
-
-__stub_service_converter = _StubServiceConverter(__entity_converter,
- __stub_query_converter)
-
-
-def get_stub_service_converter():
- """Returns a converter for v3 and v4 service request/response protos.
-
- This converter is suitable for use in stubs but not for production.
-
- Returns:
- a _StubServiceConverter
- """
- return __stub_service_converter
diff --git a/google/appengine/datastore/datastore_sqlite_stub.py b/google/appengine/datastore/datastore_sqlite_stub.py
index 3b10bf6..d73a317 100644
--- a/google/appengine/datastore/datastore_sqlite_stub.py
+++ b/google/appengine/datastore/datastore_sqlite_stub.py
@@ -1386,7 +1386,7 @@
end = max(max_id, start - 1)
return start, end
- def _AllocateIds(self, reference, size=1, max_id=None):
+ def _AllocateSequentialIds(self, reference, size=1, max_id=None):
conn = self._GetConnection()
try:
datastore_stub_util.CheckAppId(self._trusted, self._app_id,
@@ -1408,11 +1408,11 @@
finally:
self._ReleaseConnection(conn)
- def _AllocateScatteredIds(self, keys):
+ def _AllocateIds(self, references):
conn = self._GetConnection()
try:
full_keys = []
- for key in keys:
+ for key in references:
datastore_stub_util.CheckAppId(self._trusted, self._app_id, key.app())
prefix = self._GetTablePrefix(key)
last_element = key.path().element_list()[-1]
diff --git a/google/appengine/datastore/datastore_stub_util.py b/google/appengine/datastore/datastore_stub_util.py
index 05d903f..8520e22 100644
--- a/google/appengine/datastore/datastore_stub_util.py
+++ b/google/appengine/datastore/datastore_stub_util.py
@@ -18,7 +18,13 @@
-"""Utility functions shared between the file and sqlite datastore stubs."""
+"""Utility functions shared between the file and sqlite datastore stubs.
+
+This module is internal and should not be used by client applications.
+"""
+
+
+
@@ -51,7 +57,9 @@
from google.appengine.datastore import datastore_index
from google.appengine.datastore import datastore_stub_index
from google.appengine.datastore import datastore_pb
+from google.appengine.datastore import datastore_pbs
from google.appengine.datastore import datastore_query
+from google.appengine.datastore import datastore_v4_pb
from google.appengine.runtime import apiproxy_errors
from google.appengine.datastore import entity_pb
@@ -615,6 +623,15 @@
'Only one inequality filter per query is supported. '
'Encountered both %s and %s' % (ineq_prop_name, prop_name))
+ if (ineq_prop_name is not None
+ and query.group_by_property_name_size() > 0
+ and not orders):
+
+ Check(ineq_prop_name in group_by_set,
+ 'Inequality filter on %s must also be a group by '
+ 'property when group by properties are set.'
+ % (ineq_prop_name))
+
if ineq_prop_name is not None and orders:
first_order_prop = orders[0].property().decode('utf-8')
@@ -2581,9 +2598,9 @@
if self._auto_id_policy == SEQUENTIAL:
- last_element.set_id(self._AllocateIds(entity.key())[0])
+ last_element.set_id(self._AllocateSequentialIds(entity.key())[0])
else:
- full_key = self._AllocateScatteredIds([entity.key()])[0]
+ full_key = self._AllocateIds([entity.key()])[0]
last_element.set_id(full_key.path().element_list()[-1].id())
else:
insert = False
@@ -2790,11 +2807,11 @@
"""
raise NotImplementedError
- def _AllocateIds(self, reference, size=1, max_id=None):
- """Allocate ids for given reference.
+ def _AllocateSequentialIds(self, reference, size=1, max_id=None):
+ """Allocate sequential ids for given reference.
Args:
- reference: A entity_pb.Reference to allocate an id for.
+ reference: An entity_pb.Reference to allocate an id for.
size: The size of the range to allocate
max_id: The upper bound of the range to allocate
@@ -2803,6 +2820,20 @@
"""
raise NotImplementedError
+ def _AllocateIds(self, references):
+ """Allocate or reserves IDs for the v4 datastore API.
+
+ Incomplete keys are allocated scattered IDs. Complete keys are reserved
+ in the appropriate ID space.
+
+ Args:
+ references: a list of entity_pb.Reference objects to allocate or reserve
+
+ Returns:
+ a list of (complete) allocated entity_pb.Reference objects
+ """
+ raise NotImplementedError
+
def _NeedsIndexes(func):
"""A decorator for DatastoreStub methods that require or affect indexes.
@@ -3123,9 +3154,8 @@
reference = allocate_ids_request.model_key()
- (start, end) = self._datastore._AllocateIds(reference,
- allocate_ids_request.size(),
- allocate_ids_request.max())
+ (start, end) = self._datastore._AllocateSequentialIds(
+ reference, allocate_ids_request.size(), allocate_ids_request.max())
allocate_ids_response.set_start(start)
allocate_ids_response.set_end(end)
@@ -3206,6 +3236,771 @@
self._index_yaml_updater.UpdateIndexYaml()
+class StubQueryConverter(object):
+ """Converter for v3 and v4 queries suitable for use in stubs."""
+
+ def __init__(self, entity_converter):
+ self._entity_converter = entity_converter
+
+ def v4_to_v3_compiled_cursor(self, v4_cursor, v3_compiled_cursor):
+ """Converts a v4 cursor string to a v3 CompiledCursor.
+
+ Args:
+ v4_cursor: a string representing a v4 query cursor
+ v3_compiled_cursor: a datastore_pb.CompiledCursor to populate
+ """
+ v3_compiled_cursor.Clear()
+ v3_compiled_cursor.ParseFromString(v4_cursor)
+
+ def v3_to_v4_compiled_cursor(self, v3_compiled_cursor):
+ """Converts a v3 CompiledCursor to a v4 cursor string.
+
+ Args:
+ v3_compiled_cursor: a datastore_pb.CompiledCursor
+
+ Returns:
+ a string representing a v4 query cursor
+ """
+ return v3_compiled_cursor.SerializeToString()
+
+ def v4_to_v3_query(self, v4_partition_id, v4_query, v3_query):
+ """Converts a v4 Query to a v3 Query.
+
+ Args:
+ v4_partition_id: a datastore_v4_pb.PartitionId
+ v4_query: a datastore_v4_pb.Query
+ v3_query: a datastore_pb.Query to populate
+
+ Raises:
+ InvalidConversionError if the query cannot be converted
+ """
+ v3_query.Clear()
+
+ if v4_partition_id.dataset_id():
+ v3_query.set_app(v4_partition_id.dataset_id())
+ if v4_partition_id.has_namespace():
+ v3_query.set_name_space(v4_partition_id.namespace())
+
+ v3_query.set_persist_offset(True)
+ v3_query.set_require_perfect_plan(True)
+ v3_query.set_compile(True)
+
+
+ if v4_query.has_limit():
+ v3_query.set_limit(v4_query.limit())
+ if v4_query.offset():
+ v3_query.set_offset(v4_query.offset())
+ if v4_query.has_start_cursor():
+ self.v4_to_v3_compiled_cursor(v4_query.start_cursor(),
+ v3_query.mutable_compiled_cursor())
+ if v4_query.has_end_cursor():
+ self.v4_to_v3_compiled_cursor(v4_query.end_cursor(),
+ v3_query.mutable_end_compiled_cursor())
+
+
+ if v4_query.kind_list():
+ datastore_pbs.check_conversion(len(v4_query.kind_list()) == 1,
+ 'multiple kinds not supported')
+ v3_query.set_kind(v4_query.kind(0).name())
+
+
+ has_key_projection = False
+ for prop in v4_query.projection_list():
+ if prop.property().name() == datastore_pbs.PROPERTY_NAME_KEY:
+ has_key_projection = True
+ else:
+ v3_query.add_property_name(prop.property().name())
+ if has_key_projection and not v3_query.property_name_list():
+ v3_query.set_keys_only(True)
+
+
+ for prop in v4_query.group_by_list():
+ v3_query.add_group_by_property_name(prop.name())
+
+
+ self.__populate_v3_filters(v4_query.filter(), v3_query)
+
+
+ for v4_order in v4_query.order_list():
+ v3_order = v3_query.add_order()
+ v3_order.set_property(v4_order.property().name())
+ if v4_order.has_direction():
+ v3_order.set_direction(v4_order.direction())
+
+ def v3_to_v4_query(self, v3_query, v4_query):
+ """Converts a v3 Query to a v4 Query.
+
+ Args:
+ v3_query: a datastore_pb.Query
+ v4_query: a datastore_v4_pb.Query to populate
+
+ Raises:
+ InvalidConversionError if the query cannot be converted
+ """
+ v4_query.Clear()
+
+ datastore_pbs.check_conversion(not v3_query.has_distinct(),
+ 'distinct option not supported')
+ datastore_pbs.check_conversion(v3_query.require_perfect_plan(),
+ 'non-perfect plans not supported')
+
+
+
+ if v3_query.has_limit():
+ v4_query.set_limit(v3_query.limit())
+ if v3_query.offset():
+ v4_query.set_offset(v3_query.offset())
+ if v3_query.has_compiled_cursor():
+ v4_query.set_start_cursor(
+ self.v3_to_v4_compiled_cursor(v3_query.compiled_cursor()))
+ if v3_query.has_end_compiled_cursor():
+ v4_query.set_end_cursor(
+ self.v3_to_v4_compiled_cursor(v3_query.end_compiled_cursor()))
+
+
+ if v3_query.has_kind():
+ v4_query.add_kind().set_name(v3_query.kind())
+
+
+ for name in v3_query.property_name_list():
+ v4_query.add_projection().mutable_property().set_name(name)
+ if v3_query.keys_only():
+ v4_query.add_projection().mutable_property().set_name(
+ datastore_pbs.PROPERTY_NAME_KEY)
+
+
+ for name in v3_query.group_by_property_name_list():
+ v4_query.add_group_by().set_name(name)
+
+
+ num_v4_filters = len(v3_query.filter_list())
+ if v3_query.has_ancestor():
+ num_v4_filters += 1
+
+ if num_v4_filters == 1:
+ get_property_filter = self.__get_property_filter
+ elif num_v4_filters >= 1:
+ v4_query.mutable_filter().mutable_composite_filter().set_operator(
+ datastore_v4_pb.CompositeFilter.AND)
+ get_property_filter = self.__add_property_filter
+
+ if v3_query.has_ancestor():
+ self.__v3_query_to_v4_ancestor_filter(v3_query,
+ get_property_filter(v4_query))
+ for v3_filter in v3_query.filter_list():
+ self.__v3_filter_to_v4_property_filter(v3_filter,
+ get_property_filter(v4_query))
+
+
+ for v3_order in v3_query.order_list():
+ v4_order = v4_query.add_order()
+ v4_order.mutable_property().set_name(v3_order.property())
+ if v3_order.has_direction():
+ v4_order.set_direction(v3_order.direction())
+
+ def __get_property_filter(self, v4_query):
+ """Returns the PropertyFilter from the query's top-level filter."""
+ return v4_query.mutable_filter().mutable_property_filter()
+
+ def __add_property_filter(self, v4_query):
+ """Adds and returns a PropertyFilter from the query's composite filter."""
+ v4_comp_filter = v4_query.mutable_filter().mutable_composite_filter()
+ return v4_comp_filter.add_filter().mutable_property_filter()
+
+ def __populate_v3_filters(self, v4_filter, v3_query):
+ """Populates a filters for a v3 Query.
+
+ Args:
+ v4_filter: a datastore_v4_pb.Filter
+ v3_query: a datastore_pb.Query to populate with filters
+ """
+ if v4_filter.has_property_filter():
+ v4_property_filter = v4_filter.property_filter()
+ if (v4_property_filter.operator()
+ == datastore_v4_pb.PropertyFilter.HAS_ANCESTOR):
+ datastore_pbs.check_conversion(
+ v4_property_filter.value().has_key_value(),
+ 'HAS_ANCESTOR requires a reference value')
+ datastore_pbs.check_conversion((v4_property_filter.property().name()
+ == datastore_pbs.PROPERTY_NAME_KEY),
+ 'unsupported property')
+ datastore_pbs.check_conversion(not v3_query.has_ancestor(),
+ 'duplicate ancestor constraint')
+ self._entity_converter.v4_to_v3_reference(
+ v4_property_filter.value().key_value(),
+ v3_query.mutable_ancestor())
+ else:
+ v3_filter = v3_query.add_filter()
+ property_name = v4_property_filter.property().name()
+ v3_filter.set_op(v4_property_filter.operator())
+ datastore_pbs.check_conversion(
+ not v4_property_filter.value().list_value_list(),
+ ('unsupported value type, %s, in property filter'
+ ' on "%s"' % ('list_value', property_name)))
+ prop = v3_filter.add_property()
+ prop.set_multiple(False)
+ prop.set_name(property_name)
+ self._entity_converter.v4_value_to_v3_property_value(
+ v4_property_filter.value(), prop.mutable_value())
+ elif v4_filter.has_composite_filter():
+ datastore_pbs.check_conversion((v4_filter.composite_filter().operator()
+ == datastore_v4_pb.CompositeFilter.AND),
+ 'unsupported composite property operator')
+ for v4_sub_filter in v4_filter.composite_filter().filter_list():
+ self.__populate_v3_filters(v4_sub_filter, v3_query)
+
+ def __v3_filter_to_v4_property_filter(self, v3_filter, v4_property_filter):
+ """Converts a v3 Filter to a v4 PropertyFilter.
+
+ Args:
+ v3_filter: a datastore_pb.Filter
+ v4_property_filter: a datastore_v4_pb.PropertyFilter to populate
+
+ Raises:
+ InvalidConversionError if the filter cannot be converted
+ """
+ datastore_pbs.check_conversion(v3_filter.property_size() == 1,
+ 'invalid filter')
+ datastore_pbs.check_conversion(v3_filter.op() <= 5,
+ 'unsupported filter op: %d' % v3_filter.op())
+ v4_property_filter.Clear()
+ v4_property_filter.set_operator(v3_filter.op())
+ v4_property_filter.mutable_property().set_name(v3_filter.property(0).name())
+ self._entity_converter.v3_property_to_v4_value(
+ v3_filter.property(0), True, v4_property_filter.mutable_value())
+
+ def __v3_query_to_v4_ancestor_filter(self, v3_query, v4_property_filter):
+ """Converts a v3 Query to a v4 ancestor PropertyFilter.
+
+ Args:
+ v3_query: a datastore_pb.Query
+ v4_property_filter: a datastore_v4_pb.PropertyFilter to populate
+ """
+ v4_property_filter.Clear()
+ v4_property_filter.set_operator(
+ datastore_v4_pb.PropertyFilter.HAS_ANCESTOR)
+ prop = v4_property_filter.mutable_property()
+ prop.set_name(datastore_pbs.PROPERTY_NAME_KEY)
+ self._entity_converter.v3_to_v4_key(
+ v3_query.ancestor(),
+ v4_property_filter.mutable_value().mutable_key_value())
+
+
+
+__query_converter = StubQueryConverter(datastore_pbs.get_entity_converter())
+
+
+def get_query_converter():
+ """Returns a converter for v3 and v4 queries (not suitable for production).
+
+ This converter is suitable for use in stubs but not for production.
+
+ Returns:
+ a StubQueryConverter
+ """
+ return __query_converter
+
+
+class StubServiceConverter(object):
+ """Converter for v3/v4 request/response protos suitable for use in stubs."""
+
+ def __init__(self, entity_converter, query_converter):
+ self._entity_converter = entity_converter
+ self._query_converter = query_converter
+
+ def v4_to_v3_cursor(self, v4_query_handle, v3_cursor):
+ """Converts a v4 cursor string to a v3 Cursor.
+
+ Args:
+ v4_query_handle: a string representing a v4 query handle
+ v3_cursor: a datastore_pb.Cursor to populate
+ """
+ v3_cursor.ParseFromString(v4_query_handle)
+ return v3_cursor
+
+ def _v3_to_v4_query_handle(self, v3_cursor):
+ """Converts a v3 Cursor to a v4 query handle string.
+
+ Args:
+ v3_cursor: a datastore_pb.Cursor
+
+ Returns:
+ a string representing a v4 cursor
+ """
+ return v3_cursor.SerializeToString()
+
+ def v4_to_v3_txn(self, v4_txn, v3_txn):
+ """Converts a v4 transaction string to a v3 Transaction.
+
+ Args:
+ v4_txn: a string representing a v4 transaction
+ v3_txn: a datastore_pb.Transaction to populate
+ """
+ v3_txn.ParseFromString(v4_txn)
+ return v3_txn
+
+ def _v3_to_v4_txn(self, v3_txn):
+ """Converts a v3 Transaction to a v4 transaction string.
+
+ Args:
+ v3_txn: a datastore_pb.Transaction
+
+ Returns:
+ a string representing a v4 transaction
+ """
+ return v3_txn.SerializeToString()
+
+
+
+
+ def v4_to_v3_begin_transaction_req(self, app_id, v4_req):
+ """Converts a v4 BeginTransactionRequest to a v3 BeginTransactionRequest.
+
+ Args:
+ app_id: app id
+ v4_req: a datastore_v4_pb.BeginTransactionRequest
+
+ Returns:
+ a datastore_pb.BeginTransactionRequest
+ """
+ v3_req = datastore_pb.BeginTransactionRequest()
+ v3_req.set_app(app_id)
+ v3_req.set_allow_multiple_eg(v4_req.cross_group())
+ return v3_req
+
+ def v3_to_v4_begin_transaction_req(self, v3_req):
+ """Converts a v3 BeginTransactionRequest to a v4 BeginTransactionRequest.
+
+ Args:
+ v3_req: a datastore_pb.BeginTransactionRequest
+
+ Returns:
+ a datastore_v4_pb.BeginTransactionRequest
+ """
+ v4_req = datastore_v4_pb.BeginTransactionRequest()
+
+ if v3_req.has_allow_multiple_eg():
+ v4_req.set_cross_group(v3_req.allow_multiple_eg())
+
+ return v4_req
+
+ def v4_begin_transaction_resp_to_v3_txn(self, v4_resp):
+ """Converts a v4 BeginTransactionResponse to a v3 Transaction.
+
+ Args:
+ v4_resp: datastore_v4_pb.BeginTransactionResponse
+
+ Returns:
+ a a datastore_pb.Transaction
+ """
+ v3_txn = datastore_pb.Transaction()
+ self.v4_to_v3_txn(v4_resp.transaction(), v3_txn)
+ return v3_txn
+
+ def v3_to_v4_begin_transaction_resp(self, v3_resp):
+ """Converts a v3 Transaction to a v4 BeginTransactionResponse.
+
+ Args:
+ v3_resp: a datastore_pb.Transaction
+
+ Returns:
+ a datastore_v4_pb.BeginTransactionResponse
+ """
+ v4_resp = datastore_v4_pb.BeginTransactionResponse()
+ v4_resp.set_transaction(self._v3_to_v4_txn(v3_resp))
+ return v4_resp
+
+
+
+
+ def v4_rollback_req_to_v3_txn(self, v4_req):
+ """Converts a v4 RollbackRequest to a v3 Transaction.
+
+ Args:
+ v4_req: a datastore_v4_pb.RollbackRequest
+
+ Returns:
+ a datastore_pb.Transaction
+ """
+ v3_txn = datastore_pb.Transaction()
+ self.v4_to_v3_txn(v4_req.transaction(), v3_txn)
+ return v3_txn
+
+ def v3_to_v4_rollback_req(self, v3_req):
+ """Converts a v3 Transaction to a v4 RollbackRequest.
+
+ Args:
+ v3_req: datastore_pb.Transaction
+
+ Returns:
+ a a datastore_v4_pb.RollbackRequest
+ """
+ v4_req = datastore_v4_pb.RollbackRequest()
+ v4_req.set_transaction(self._v3_to_v4_txn(v3_req))
+ return v4_req
+
+
+
+
+ def v4_commit_req_to_v3_txn(self, v4_req):
+ """Converts a v4 CommitRequest to a v3 Transaction.
+
+ Args:
+ v4_req: a datastore_v4_pb.CommitRequest
+
+ Returns:
+ a datastore_pb.Transaction
+ """
+ v3_txn = datastore_pb.Transaction()
+ self.v4_to_v3_txn(v4_req.transaction(), v3_txn)
+ return v3_txn
+
+
+
+
+ def v4_run_query_req_to_v3_query(self, v4_req):
+ """Converts a v4 RunQueryRequest to a v3 Query.
+
+ GQL is not supported.
+
+ Args:
+ v4_req: a datastore_v4_pb.RunQueryRequest
+
+ Returns:
+ a datastore_pb.Query
+ """
+
+ datastore_pbs.check_conversion(not v4_req.has_gql_query(),
+ 'GQL not supported')
+ v3_query = datastore_pb.Query()
+ self._query_converter.v4_to_v3_query(v4_req.partition_id(), v4_req.query(),
+ v3_query)
+
+
+ if v4_req.has_suggested_batch_size():
+ v3_query.set_count(v4_req.suggested_batch_size())
+
+
+ read_options = v4_req.read_options()
+ if read_options.has_transaction():
+ self.v4_to_v3_txn(read_options.transaction(),
+ v3_query.mutable_transaction())
+ elif (read_options.read_consistency()
+ == datastore_v4_pb.ReadOptions.EVENTUAL):
+ v3_query.set_strong(False)
+ v3_query.set_failover_ms(-1)
+ elif read_options.read_consistency() == datastore_v4_pb.ReadOptions.STRONG:
+ v3_query.set_strong(True)
+
+ if v4_req.has_min_safe_time_seconds():
+ v3_query.set_min_safe_time_seconds(v4_req.min_safe_time_seconds())
+
+ return v3_query
+
+ def v3_to_v4_run_query_req(self, v3_req):
+ """Converts a v3 Query to a v4 RunQueryRequest.
+
+ Args:
+ v3_req: a datastore_pb.Query
+
+ Returns:
+ a datastore_v4_pb.RunQueryRequest
+ """
+ v4_req = datastore_v4_pb.RunQueryRequest()
+
+
+ v4_partition_id = v4_req.mutable_partition_id()
+ v4_partition_id.set_dataset_id(v3_req.app())
+ if v3_req.name_space():
+ v4_partition_id.set_namespace(v3_req.name_space())
+
+
+ if v3_req.has_count():
+ v4_req.set_suggested_batch_size(v3_req.count())
+
+
+ if v3_req.has_transaction():
+ v4_req.mutable_read_options().set_transaction(
+ self._v3_to_v4_txn(v3_req.transaction()))
+ elif v3_req.strong():
+ v4_req.mutable_read_options().set_read_consistency(
+ datastore_v4_pb.ReadOptions.STRONG)
+ elif v3_req.has_failover_ms():
+ v4_req.mutable_read_options().set_read_consistency(
+ datastore_v4_pb.ReadOptions.EVENTUAL)
+ if v3_req.has_min_safe_time_seconds():
+ v4_req.set_min_safe_time_seconds(v3_req.min_safe_time_seconds())
+
+ self._query_converter.v3_to_v4_query(v3_req, v4_req.mutable_query())
+
+ return v4_req
+
+ def v4_run_query_resp_to_v3_query_result(self, v4_resp):
+ """Converts a V4 RunQueryResponse to a v3 QueryResult.
+
+ Args:
+ v4_resp: a datastore_v4_pb.QueryResult
+
+ Returns:
+ a datastore_pb.QueryResult
+ """
+ v3_resp = self.v4_to_v3_query_result(v4_resp.batch())
+
+
+ if v4_resp.has_query_handle():
+ self.v4_to_v3_cursor(v4_resp.query_handle(), v3_resp.mutable_cursor())
+
+ return v3_resp
+
+ def v3_to_v4_run_query_resp(self, v3_resp):
+ """Converts a v3 QueryResult to a V4 RunQueryResponse.
+
+ Args:
+ v3_resp: a datastore_pb.QueryResult
+
+ Returns:
+ a datastore_v4_pb.RunQueryResponse
+ """
+ v4_resp = datastore_v4_pb.RunQueryResponse()
+ self.v3_to_v4_query_result_batch(v3_resp, v4_resp.mutable_batch())
+
+ if v3_resp.has_cursor():
+ v4_resp.set_query_handle(
+ self._query_converter.v3_to_v4_compiled_cursor(v3_resp.cursor()))
+
+ return v4_resp
+
+
+
+
+ def v4_to_v3_next_req(self, v4_req):
+ """Converts a v4 ContinueQueryRequest to a v3 NextRequest.
+
+ Args:
+ v4_req: a datastore_v4_pb.ContinueQueryRequest
+
+ Returns:
+ a datastore_pb.NextRequest
+ """
+ v3_req = datastore_pb.NextRequest()
+ v3_req.set_compile(True)
+ self.v4_to_v3_cursor(v4_req.query_handle(), v3_req.mutable_cursor())
+ return v3_req
+
+ def v3_to_v4_continue_query_resp(self, v3_resp):
+ """Converts a v3 QueryResult to a v4 ContinueQueryResponse.
+
+ Args:
+ v3_resp: a datstore_pb.QueryResult
+
+ Returns:
+ a datastore_v4_pb.ContinueQueryResponse
+ """
+ v4_resp = datastore_v4_pb.ContinueQueryResponse()
+ self.v3_to_v4_query_result_batch(v3_resp, v4_resp.mutable_batch())
+ return v4_resp
+
+
+
+
+ def v4_to_v3_get_req(self, v4_req):
+ """Converts a v4 LookupRequest to a v3 GetRequest.
+
+ Args:
+ v4_req: a datastore_v4_pb.LookupRequest
+
+ Returns:
+ a datastore_pb.GetRequest
+ """
+ v3_req = datastore_pb.GetRequest()
+ v3_req.set_allow_deferred(True)
+
+
+ if v4_req.read_options().has_transaction():
+ self.v4_to_v3_txn(v4_req.read_options().transaction(),
+ v3_req.mutable_transaction())
+ elif (v4_req.read_options().read_consistency()
+ == datastore_v4_pb.ReadOptions.EVENTUAL):
+ v3_req.set_strong(False)
+ v3_req.set_failover_ms(-1)
+ elif (v4_req.read_options().read_consistency()
+ == datastore_v4_pb.ReadOptions.STRONG):
+ v3_req.set_strong(True)
+
+ for v4_key in v4_req.key_list():
+ self._entity_converter.v4_to_v3_reference(v4_key, v3_req.add_key())
+
+ return v3_req
+
+ def v3_to_v4_lookup_req(self, v3_req):
+ """Converts a v3 GetRequest to a v4 LookupRequest.
+
+ Args:
+ v3_req: a datastore_pb.GetRequest
+
+ Returns:
+ a datastore_v4_pb.LookupRequest
+ """
+ v4_req = datastore_v4_pb.LookupRequest()
+ datastore_pbs.check_conversion(v3_req.allow_deferred(),
+ 'allow_deferred must be true')
+
+
+ if v3_req.has_transaction():
+ v4_req.mutable_read_options().set_transaction(
+ self._v3_to_v4_txn(v3_req.transaction()))
+ elif v3_req.strong():
+ v4_req.mutable_read_options().set_read_consistency(
+ datastore_v4_pb.ReadOptions.STRONG)
+ elif v3_req.has_failover_ms():
+ v4_req.mutable_read_options().set_read_consistency(
+ datastore_v4_pb.ReadOptions.EVENTUAL)
+
+ for v3_ref in v3_req.key_list():
+ self._entity_converter.v3_to_v4_key(v3_ref, v4_req.add_key())
+
+ return v4_req
+
+ def v4_to_v3_get_resp(self, v4_resp):
+ """Converts a v4 LookupResponse to a v3 GetResponse.
+
+ Args:
+ v4_resp: a datastore_v4_pb.LookupResponse
+
+ Returns:
+ a datastore_pb.GetResponse
+ """
+ v3_resp = datastore_pb.GetResponse()
+
+ for v4_key in v4_resp.deferred_list():
+ self._entity_converter.v4_to_v3_reference(v4_key, v3_resp.add_deferred())
+ for v4_found in v4_resp.found_list():
+ self._entity_converter.v4_to_v3_entity(
+ v4_found.entity(), v3_resp.add_entity().mutable_entity())
+ for v4_missing in v4_resp.missing_list():
+ self._entity_converter.v4_to_v3_reference(
+ v4_missing.entity().key(),
+ v3_resp.add_entity().mutable_key())
+
+ return v3_resp
+
+ def v3_to_v4_lookup_resp(self, v3_resp):
+ """Converts a v3 GetResponse to a v4 LookupResponse.
+
+ Args:
+ v3_resp: a datastore_pb.GetResponse
+
+ Returns:
+ a datastore_v4_pb.LookupResponse
+ """
+ v4_resp = datastore_v4_pb.LookupResponse()
+
+ for v3_ref in v3_resp.deferred_list():
+ self._entity_converter.v3_to_v4_key(v3_ref, v4_resp.add_deferred())
+ for v3_entity in v3_resp.entity_list():
+ if v3_entity.has_entity():
+ self._entity_converter.v3_to_v4_entity(
+ v3_entity.entity(),
+ v4_resp.add_found().mutable_entity())
+ if v3_entity.has_key():
+ self._entity_converter.v3_to_v4_key(
+ v3_entity.key(),
+ v4_resp.add_missing().mutable_entity().mutable_key())
+
+ return v4_resp
+
+ def v4_to_v3_query_result(self, v4_batch):
+ """Converts a v4 QueryResultBatch to a v3 QueryResult.
+
+ Args:
+ v4_batch: a datastore_v4_pb.QueryResultBatch
+
+ Returns:
+ a datastore_pb.QueryResult
+ """
+ v3_result = datastore_pb.QueryResult()
+
+
+ v3_result.set_more_results(
+ (v4_batch.more_results()
+ == datastore_v4_pb.QueryResultBatch.NOT_FINISHED))
+ if v4_batch.has_end_cursor():
+ self._query_converter.v4_to_v3_compiled_cursor(
+ v4_batch.end_cursor(), v3_result.mutable_compiled_cursor())
+
+
+ if v4_batch.entity_result_type() == datastore_v4_pb.EntityResult.PROJECTION:
+ v3_result.set_index_only(True)
+ elif v4_batch.entity_result_type() == datastore_v4_pb.EntityResult.KEY_ONLY:
+ v3_result.set_keys_only(True)
+
+
+ if v4_batch.has_skipped_results():
+ v3_result.set_skipped_results(v4_batch.skipped_results())
+ for v4_entity in v4_batch.entity_result_list():
+ v3_entity = v3_result.add_result()
+ self._entity_converter.v4_to_v3_entity(v4_entity.entity(), v3_entity)
+ if v4_batch.entity_result_type() != datastore_v4_pb.EntityResult.FULL:
+
+
+ v3_entity.clear_entity_group()
+
+ return v3_result
+
+ def v3_to_v4_query_result_batch(self, v3_result, v4_batch):
+ """Converts a v3 QueryResult to a v4 QueryResultBatch.
+
+ Args:
+ v3_result: a datastore_pb.QueryResult
+ v4_batch: a datastore_v4_pb.QueryResultBatch to populate
+ """
+ v4_batch.Clear()
+
+
+ if v3_result.more_results():
+ v4_batch.set_more_results(datastore_v4_pb.QueryResultBatch.NOT_FINISHED)
+ else:
+ v4_batch.set_more_results(
+ datastore_v4_pb.QueryResultBatch.MORE_RESULTS_AFTER_LIMIT)
+ if v3_result.has_compiled_cursor():
+ v4_batch.set_end_cursor(
+ self._query_converter.v3_to_v4_compiled_cursor(
+ v3_result.compiled_cursor()))
+
+
+ if v3_result.keys_only():
+ v4_batch.set_entity_result_type(datastore_v4_pb.EntityResult.KEY_ONLY)
+ elif v3_result.index_only():
+ v4_batch.set_entity_result_type(datastore_v4_pb.EntityResult.PROJECTION)
+ else:
+ v4_batch.set_entity_result_type(datastore_v4_pb.EntityResult.FULL)
+
+
+ if v3_result.has_skipped_results():
+ v4_batch.set_skipped_results(v3_result.skipped_results())
+ for v3_entity in v3_result.result_list():
+ v4_entity_result = datastore_v4_pb.EntityResult()
+ self._entity_converter.v3_to_v4_entity(v3_entity,
+ v4_entity_result.mutable_entity())
+ v4_batch.entity_result_list().append(v4_entity_result)
+
+
+
+__service_converter = StubServiceConverter(
+ datastore_pbs.get_entity_converter(), __query_converter)
+
+
+def get_service_converter():
+ """Returns a converter for v3 and v4 service request/response protos.
+
+ This converter is suitable for use in stubs but not for production.
+
+ Returns:
+ a StubServiceConverter
+ """
+ return __service_converter
+
+
def ReverseBitsInt64(v):
"""Reverse the bits of a 64-bit integer.
diff --git a/google/appengine/datastore/datastore_v4_stub.py b/google/appengine/datastore/datastore_v4_stub.py
new file mode 100644
index 0000000..b983633
--- /dev/null
+++ b/google/appengine/datastore/datastore_v4_stub.py
@@ -0,0 +1,84 @@
+#!/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.
+#
+
+
+
+
+"""Implementation of the datastore_v4 API that forwards to the v3 service."""
+
+
+
+
+
+
+
+
+
+
+
+
+
+from google.appengine.api import apiproxy_stub
+from google.appengine.api import apiproxy_stub_map
+from google.appengine.datastore import datastore_pbs
+from google.appengine.datastore import datastore_v4_pb
+from google.appengine.datastore import datastore_v4_validator
+from google.appengine.runtime import apiproxy_errors
+
+
+SERVICE_NAME = 'datastore_v4'
+V3_SERVICE_NAME = 'datastore_v3'
+
+
+class DatastoreV4Stub(apiproxy_stub.APIProxyStub):
+ """Implementation of the datastore_v4 API that forwards to the v3 service."""
+
+
+ THREADSAFE = False
+
+ def __init__(self, app_id):
+ apiproxy_stub.APIProxyStub.__init__(self, SERVICE_NAME)
+ self.__app_id = app_id
+ self.__entity_converter = datastore_pbs.get_entity_converter()
+ self.__service_validator = datastore_v4_validator.get_service_validator()
+
+ def _Dynamic_AllocateIds(self, req, resp):
+ v3_stub = apiproxy_stub_map.apiproxy.GetStub(V3_SERVICE_NAME)
+ try:
+ self.__service_validator.validate_allocate_ids_req(req)
+
+ if req.allocate_list():
+ v3_refs = self.__entity_converter.v4_to_v3_references(
+ req.allocate_list())
+
+ v3_full_refs = v3_stub._AllocateIds(v3_refs)
+ resp.allocated_list().extend(
+ self.__entity_converter.v3_to_v4_keys(v3_full_refs))
+ elif req.reserve_list():
+ v3_refs = self.__entity_converter.v4_to_v3_references(
+ req.reserve_list())
+
+ v3_stub._AllocateIds(v3_refs)
+ except datastore_pbs.InvalidConversionError, e:
+ raise apiproxy_errors.ApplicationError(
+ datastore_v4_pb.Error.BAD_REQUEST, str(e))
+ except datastore_v4_validator.ValidationError, e:
+ raise apiproxy_errors.ApplicationError(
+ datastore_v4_pb.Error.BAD_REQUEST, str(e))
+
+ def __make_v3_call(self, method, v3_req, v3_resp):
+ apiproxy_stub_map.MakeSyncCall(V3_SERVICE_NAME, method, v3_req, v3_resp)
diff --git a/google/appengine/datastore/datastore_v4_validator.py b/google/appengine/datastore/datastore_v4_validator.py
new file mode 100644
index 0000000..c8c9290
--- /dev/null
+++ b/google/appengine/datastore/datastore_v4_validator.py
@@ -0,0 +1,322 @@
+#!/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.
+#
+
+
+
+
+"""Validators for v4 datastore protocol buffers.
+
+This module is internal and should not be used by client applications.
+"""
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+import re
+
+from google.appengine.datastore import datastore_pbs
+
+
+RESERVED_NAME = re.compile('^__(.*)__$')
+
+
+
+class ValidationError(Exception):
+ """Raised when validation fails."""
+ pass
+
+
+def _assert_condition(condition, message):
+ """Asserts a validation condition and raises an error if it's not met.
+
+ Args:
+ condition: (boolean) condition to enforce
+ message: error message
+
+ Raises:
+ ValidationError: if condition is not met
+ """
+ if not condition:
+ raise ValidationError(message)
+
+
+def _assert_initialized(pb):
+ """Asserts that a protocol buffer is initialized.
+
+ Args:
+ pb: a protocol buffer
+
+ Raises:
+ ValidationError: if protocol buffer is not initialized
+ """
+ errors = []
+ if not pb.IsInitialized(errors):
+ _assert_condition(False, 'not initialized: %s' % '\n\t'.join(errors))
+
+
+def _assert_valid_utf8(string, desc):
+ """Asserts that a string is valid UTF8.
+
+ Args:
+ string: string to check
+ desc: description of the string (used in error message)
+
+ Raises:
+ ValidationError: if the string is not valid UTF8
+ """
+ _assert_condition(datastore_pbs.is_valid_utf8(string),
+ 'The %s is not valid UTF-8.' % desc)
+
+
+def _assert_string_not_empty(string, desc):
+ """Asserts that a string is not empty.
+
+ Args:
+ string: string to check
+ desc: description of the string (used in error message)
+
+ Raises:
+ ValidationError: if the string is empty
+ """
+ _assert_condition(string, 'The %s is the empty string.' % desc)
+
+
+def _assert_string_not_reserved(string, desc):
+ """Asserts that a string is not a reserved name.
+
+ Args:
+ string: string to check
+ desc: description of the string (used in error message)
+
+ Raises:
+ ValidationError: if the string is a reserved name
+ """
+ _assert_condition(not RESERVED_NAME.match(string),
+ 'The %s "%s" is reserved.' % (desc, string))
+
+
+class _ValidationConstraint(object):
+ """Container for a set of validation constraints."""
+
+ def __init__(self, incomplete_key_path_allowed=True,
+ complete_key_path_allowed=False,
+ reserved_key_allowed=False):
+ self.__incomplete_key_path_allowed = incomplete_key_path_allowed
+ self.__complete_key_path_allowed = complete_key_path_allowed
+ self.__reserved_key_allowed = reserved_key_allowed
+
+ @property
+ def incomplete_key_path_allowed(self):
+ """Allow key paths to be incomplete."""
+ return self.__incomplete_key_path_allowed
+
+ @property
+ def complete_key_path_allowed(self):
+ """Allow key paths to be complete."""
+ return self.__complete_key_path_allowed
+
+ @property
+ def reserved_key_allowed(self):
+ """Allow reserved keys and reserved partition ids."""
+ return self.__reserved_key_allowed
+
+
+
+CONSTRAINT_WRITE = _ValidationConstraint(
+ incomplete_key_path_allowed=False,
+ complete_key_path_allowed=True,
+ reserved_key_allowed=False)
+
+
+
+CONSTRAINT_ALLOCATE_KEY_ID = _ValidationConstraint(
+ incomplete_key_path_allowed=True,
+ complete_key_path_allowed=False,
+ reserved_key_allowed=False)
+
+
+
+class _EntityValidator(object):
+ """Validator for v4 entities and keys."""
+
+ def validate_keys(self, constraint, keys):
+ """Validates a list of keys.
+
+ Args:
+ constraint: a _ValidationConstraint to apply
+ keys: a list of datastore_v4_pb.Key objects
+
+ Raises:
+ ValidationError: if any of the keys is invalid
+ """
+ for key in keys:
+ self.validate_key(constraint, key)
+
+ def validate_key(self, constraint, key):
+ """Validates a key.
+
+ Args:
+ constraint: a _ValidationConstraint to apply
+ key: a datastore_v4_pb.Key
+
+ Raises:
+ ValidationError: if the key is invalid
+ """
+ _assert_condition(key.has_partition_id(), 'Key is missing partition id.')
+ self.validate_partition_id(constraint, key.partition_id())
+ num_key_path_elements = len(key.path_element_list())
+ _assert_condition(num_key_path_elements, 'Key path is empty.')
+ num_incomplete_elements = 0
+ for path_element in key.path_element_list():
+ _assert_valid_utf8(path_element.kind(), 'key path kind')
+ kind = path_element.kind()
+ self.validate_kind(constraint, kind)
+ has_name = path_element.has_name()
+ if path_element.has_id():
+ _assert_condition(not has_name,
+ 'Key path element has both id (%d) and name ("%s").'
+ % (path_element.id(), path_element.name()))
+ else:
+ if has_name:
+ _assert_valid_utf8(path_element.name(), 'key path name')
+ name = path_element.name()
+ _assert_string_not_empty(name, 'key path name')
+ if not constraint.reserved_key_allowed:
+ _assert_string_not_reserved(name, 'key path name')
+ else:
+ num_incomplete_elements += 1
+ final_element = key.path_element(num_key_path_elements - 1)
+ final_element_complete = final_element.has_id() or final_element.has_name()
+ if not constraint.complete_key_path_allowed:
+ _assert_condition(not final_element_complete,
+ 'Key path is complete: %s.'
+ % datastore_pbs.v4_key_to_string(key))
+ if not constraint.incomplete_key_path_allowed:
+ _assert_condition(final_element_complete,
+ 'Key path is incomplete: %s.'
+ % datastore_pbs.v4_key_to_string(key))
+ if final_element_complete:
+ num_expected_incomplete = 0
+ else:
+ num_expected_incomplete = 1
+ if num_incomplete_elements != num_expected_incomplete:
+
+ _assert_condition(False, 'Key path element is incomplete: %s.'
+ % datastore_pbs.v4_key_to_string(key))
+
+ def validate_partition_id(self, constraint, partition_id):
+ """Validates a partition ID.
+
+ Args:
+ constraint: a _ValidationConstraint to apply
+ partition_id: a datastore_v4_pb.PartitionId
+
+ Raises:
+ ValidationError: if the partition ID is invalid
+ """
+ _assert_condition(partition_id.has_dataset_id(),
+ 'Partition id is missing dataset id.')
+ if partition_id.has_dataset_id():
+ self.validate_partition_id_dimension(constraint,
+ partition_id.dataset_id(),
+ 'dataset id')
+ if partition_id.has_namespace():
+ self.validate_partition_id_dimension(constraint, partition_id.namespace(),
+ 'namespace')
+
+ def validate_partition_id_dimension(self, constraint, partition_dimension,
+ desc):
+ """Validates a dimension (namespace or dataset) or a partition ID.
+
+ Args:
+ constraint: a _ValidationConstraint to apply
+ partition_dimension: string representing one dimension of a partition ID
+ desc: description of the dimension (used in error messages)
+
+ Raises:
+ ValidationError: if the partition ID dimension is invalid
+ """
+ _assert_valid_utf8(partition_dimension, desc)
+ _assert_string_not_empty(partition_dimension, desc)
+ if not constraint.reserved_key_allowed:
+ _assert_string_not_reserved(partition_dimension, desc)
+ _assert_condition('!' not in partition_dimension,
+ 'The %s "%s" contains invalid character \'!\'.'
+ % (desc, partition_dimension))
+
+ def validate_kind(self, constraint, kind):
+ """Validates a kind.
+
+ Args:
+ constraint: a _ValidationConstraint to apply
+ kind: kind string
+
+ Raises:
+ ValidationError: if the kind is invalid
+ """
+ _assert_string_not_empty(kind, 'kind')
+ if not constraint.reserved_key_allowed:
+ _assert_string_not_reserved(kind, 'kind')
+
+
+__entity_validator = _EntityValidator()
+
+
+def get_entity_validator():
+ """Validator for entities and keys."""
+ return __entity_validator
+
+
+class _ServiceValidator(object):
+ """Validator for request/response protos."""
+
+ def __init__(self, entity_validator):
+ self.__entity_validator = entity_validator
+
+ def validate_allocate_ids_req(self, req):
+ _assert_initialized(req)
+ _assert_condition(not req.allocate_list() or not req.reserve_list(),
+ 'Cannot reserve and allocate ids in the same request.')
+ self.__entity_validator.validate_keys(CONSTRAINT_ALLOCATE_KEY_ID,
+ req.allocate_list())
+ self.__entity_validator.validate_keys(CONSTRAINT_WRITE,
+ req.reserve_list())
+
+
+
+__service_validator = _ServiceValidator(__entity_validator)
+
+
+def get_service_validator():
+ """Returns a validator for v4 service request/response protos.
+
+ Returns:
+ a _ServiceValidator
+ """
+ return __service_validator
diff --git a/google/appengine/ext/analytics/static/analytics_js.js b/google/appengine/ext/analytics/static/analytics_js.js
index 596ec3b..91b5f91 100644
--- a/google/appengine/ext/analytics/static/analytics_js.js
+++ b/google/appengine/ext/analytics/static/analytics_js.js
@@ -6,13 +6,13 @@
var la=L,ma={},P=function(a){var b;if(!(b=ma[a])){b=0;for(var c=String(la).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),d=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",k=d[f]||"",w=RegExp("(\\d*)(\\D*)","g"),p=RegExp("(\\d*)(\\D*)","g");do{var l=w.exec(g)||["","",""],s=p.exec(k)||["","",""];if(0==l[0].length&&0==s[0].length)break;b=((0==l[1].length?0:parseInt(l[1],10))<(0==s[1].length?0:parseInt(s[1],10))?-1:(0==l[1].length?
0:parseInt(l[1],10))>(0==s[1].length?0:parseInt(s[1],10))?1:0)||((0==l[2].length)<(0==s[2].length)?-1:(0==l[2].length)>(0==s[2].length)?1:0)||(l[2]<s[2]?-1:l[2]>s[2]?1:0)}while(0==b)}b=ma[a]=0<=b}return b},na=m.document,oa=na&&I?ia()||("CSS1Compat"==na.compatMode?parseInt(la,10):5):void 0;!J&&!I||I&&I&&9<=oa||J&&P("1.9.1");I&&P("9");var pa=function(a){a=a.className;return q(a)&&a.match(/\S+/g)||[]},qa=function(a,b){for(var c=pa(a),d=ea(arguments,1),e=c,f=0;f<d.length;f++)0<=B(e,d[f])||e.push(d[f]);c=c.join(" ");a.className=c},sa=function(a,b){var c=pa(a),d=ea(arguments,1),c=ra(c,d).join(" ");a.className=c},ra=function(a,b){return da(a,function(a){return!(0<=B(b,a))})};var Q=function(a,b,c){var d=document;c=c||d;a=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(a||b))return c.querySelectorAll(a+(b?"."+b:""));if(b&&c.getElementsByClassName){c=c.getElementsByClassName(b);if(a){for(var d={},e=0,f=0,g;g=c[f];f++)a==g.nodeName&&(d[e++]=g);d.length=e;return d}return c}c=c.getElementsByTagName(a||"*");if(b){d={};for(f=e=0;g=c[f];f++)a=g.className,"function"==typeof a.split&&0<=B(a.split(/\s+/),b)&&(d[e++]=g);d.length=e;return d}return c};var R=function(a){R[" "](a);return a};R[" "]=function(){};var ta=!I||I&&9<=oa,ua=I&&!P("9");!K||P("528");J&&P("1.9b")||I&&P("8")||ha&&P("9.5")||K&&P("528");J&&!P("8")||I&&P("9");var S=function(a,b){this.type=a;this.currentTarget=this.target=b};S.prototype.i=!1;S.prototype.defaultPrevented=!1;S.prototype.preventDefault=function(){this.defaultPrevented=!0};var T=function(a,b){if(a){var c=this.type=a.type;S.call(this,c);this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;if(d){if(J){var e;n:{try{R(d.nodeName);e=!0;break n}catch(f){}e=!1}e||(d=null)}}else"mouseover"==c?d=a.fromElement:"mouseout"==c&&(d=a.toElement);this.relatedTarget=d;this.offsetX=K||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=K||void 0!==a.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:
a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.state=a.state;this.j=a;a.defaultPrevented&&this.preventDefault();delete this.i}};u(T,S);h=T.prototype;h.target=null;h.relatedTarget=null;h.offsetX=0;h.offsetY=0;h.clientX=0;h.clientY=0;h.screenX=0;h.screenY=0;h.button=0;h.keyCode=0;
-h.charCode=0;h.ctrlKey=!1;h.altKey=!1;h.shiftKey=!1;h.metaKey=!1;h.j=null;h.preventDefault=function(){T.o.preventDefault.call(this);var a=this.j;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ua)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var va="closure_listenable_"+(1E6*Math.random()|0),wa=function(a){try{return!(!a||!a[va])}catch(b){return!1}},xa=0;var ya=function(a,b,c,d,e){this.c=a;this.e=null;this.src=b;this.type=c;this.capture=!!d;this.f=e;this.key=++xa;this.d=this.g=!1},za=function(a){a.d=!0;a.c=null;a.e=null;a.src=null;a.f=null};var U=function(a){this.src=a;this.b={};this.h=0};U.prototype.add=function(a,b,c,d,e){var f=this.b[a];f||(f=this.b[a]=[],this.h++);var g;n:{for(g=0;g<f.length;++g){var k=f[g];if(!k.d&&k.c==b&&k.capture==!!d&&k.f==e)break n}g=-1}-1<g?(a=f[g],c||(a.g=!1)):(a=new ya(b,this.src,a,!!d,e),a.g=c,f.push(a));return a};var Aa=function(a,b){var c=b.type;if(c in a.b){var d=a.b[c],e=B(d,b),f;if(f=0<=e)z(null!=d.length),A.splice.call(d,e,1);f&&(za(b),0==a.b[c].length&&(delete a.b[c],a.h--))}};var V="closure_lm_"+(1E6*Math.random()|0),W={},Ba=0,Da=function(){var a=Ca,b=ta?function(c){return a.call(b.src,b.c,c)}:function(c){c=a.call(b.src,b.c,c);if(!c)return c};return b},Ea=function(a,b,c,d,e){if("array"==n(b))for(var f=0;f<b.length;f++)Ea(a,b[f],c,d,e);else if(c=Fa(c),wa(a))a.k.add(b,c,!0,d,e);else{if(!b)throw Error("Invalid event type");var f=!!d,g=X(a);g||(a[V]=g=new U(a));c=g.add(b,c,!0,d,e);c.e||(d=Da(),c.e=d,d.src=a,d.c=c,a.addEventListener?a.addEventListener(b,d,f):a.attachEvent(b in
+h.charCode=0;h.ctrlKey=!1;h.altKey=!1;h.shiftKey=!1;h.metaKey=!1;h.j=null;h.preventDefault=function(){T.o.preventDefault.call(this);var a=this.j;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,ua)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var va="closure_listenable_"+(1E6*Math.random()|0),wa=function(a){try{return!(!a||!a[va])}catch(b){return!1}},xa=0;var ya=function(a,b,c,d,e){this.c=a;this.e=null;this.src=b;this.type=c;this.capture=!!d;this.f=e;this.key=++xa;this.d=this.g=!1},za=function(a){a.d=!0;a.c=null;a.e=null;a.src=null;a.f=null};var U=function(a){this.src=a;this.b={};this.h=0};U.prototype.add=function(a,b,c,d,e){var f=this.b[a];f||(f=this.b[a]=[],this.h++);var g;n:{for(g=0;g<f.length;++g){var k=f[g];if(!k.d&&k.c==b&&k.capture==!!d&&k.f==e)break n}g=-1}-1<g?(a=f[g],c||(a.g=!1)):(a=new ya(b,this.src,a,!!d,e),a.g=c,f.push(a));return a};var Aa=function(a,b){var c=b.type;if(c in a.b){var d=a.b[c],e=B(d,b),f;if(f=0<=e)z(null!=d.length),A.splice.call(d,e,1);f&&(za(b),0==a.b[c].length&&(delete a.b[c],a.h--))}};var V="closure_lm_"+(1E6*Math.random()|0),W={},Ba=0,Da=function(){var a=Ca,b=ta?function(c){return a.call(b.src,b.c,c)}:function(c){c=a.call(b.src,b.c,c);if(!c)return c};return b},Ea=function(a,b,c,d,e){if("array"==n(b))for(var f=0;f<b.length;f++)Ea(a,b[f],c,d,e);else if(c=Fa(c),wa(a))a.k.add(String(b),c,!0,d,e);else{if(!b)throw Error("Invalid event type");var f=!!d,g=X(a);g||(a[V]=g=new U(a));c=g.add(b,c,!0,d,e);c.e||(d=Da(),c.e=d,d.src=a,d.c=c,a.addEventListener?a.addEventListener(b,d,f):a.attachEvent(b in
W?W[b]:W[b]="on"+b,d),Ba++)}},Ha=function(a,b,c,d){var e=1;if(a=X(a))if(b=a.b[b])for(b=C(b),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.d&&(e&=!1!==Ga(f,d))}return Boolean(e)},Ga=function(a,b){var c=a.c,d=a.f||a.src;if(a.g&&"number"!=typeof a&&a&&!a.d){var e=a.src;if(wa(e))Aa(e.k,a);else{var f=a.type,g=a.e;e.removeEventListener?e.removeEventListener(f,g,a.capture):e.detachEvent&&e.detachEvent(f in W?W[f]:W[f]="on"+f,g);Ba--;(f=X(e))?(Aa(f,a),0==f.h&&(f.src=null,e[V]=null)):za(a)}}return c.call(d,
b)},Ca=function(a,b){if(a.d)return!0;if(!ta){var c;if(!(c=b))n:{c=["window","event"];for(var d=m,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break n}c=d}e=c;c=new T(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){n:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break n}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,k=e.length-1;!c.i&&0<=k;k--)c.currentTarget=e[k],d&=Ha(e[k],f,!0,c);for(k=0;!c.i&&k<e.length;k++)c.currentTarget=
e[k],d&=Ha(e[k],f,!1,c)}return d}return Ga(a,new T(b,this))},X=function(a){a=a[V];return a instanceof U?a:null},Ia="__closure_events_fn_"+(1E9*Math.random()>>>0),Fa=function(a){z(a,"Listener can not be null.");if("function"==n(a))return a;z(a.handleEvent,"An object listener must have handleEvent method.");return a[Ia]||(a[Ia]=function(b){return a.handleEvent(b)})};var Z=function(a,b,c){"number"==typeof a?(this.a=new Date(a,b||0,c||1),Y(this,c||1)):(b=typeof a,"object"==b&&null!=a||"function"==b?(this.a=new Date(a.getFullYear(),a.getMonth(),a.getDate()),Y(this,a.getDate())):(this.a=new Date(aa()),this.a.setHours(0),this.a.setMinutes(0),this.a.setSeconds(0),this.a.setMilliseconds(0)))};h=Z.prototype;h.getFullYear=function(){return this.a.getFullYear()};h.getYear=function(){return this.getFullYear()};h.getMonth=function(){return this.a.getMonth()};h.getDate=function(){return this.a.getDate()};
h.getTime=function(){return this.a.getTime()};h.getUTCHours=function(){return this.a.getUTCHours()};h.setFullYear=function(a){this.a.setFullYear(a)};h.setMonth=function(a){this.a.setMonth(a)};h.setDate=function(a){this.a.setDate(a)};
h.add=function(a){if(a.n||a.m){var b=this.getMonth()+a.m+12*a.n,c=this.getYear()+Math.floor(b/12),b=b%12;0>b&&(b+=12);var d;n:{switch(b){case 1:d=0!=c%4||0==c%100&&0!=c%400?28:29;break n;case 5:case 8:case 10:case 3:d=30;break n}d=31}d=Math.min(d,this.getDate());this.setDate(1);this.setFullYear(c);this.setMonth(b);this.setDate(d)}a.l&&(b=new Date(this.getYear(),this.getMonth(),this.getDate(),12),a=new Date(b.getTime()+864E5*a.l),this.setDate(1),this.setFullYear(a.getFullYear()),this.setMonth(a.getMonth()),
-this.setDate(a.getDate()),Y(this,a.getDate()))};h.toString=function(){return[this.getFullYear(),x(this.getMonth()+1),x(this.getDate())].join("")+""};var Y=function(a,b){if(a.getDate()!=b){var c=a.getDate()<b?1:-1;a.a.setUTCHours(a.a.getUTCHours()+c)}};Z.prototype.valueOf=function(){return this.a.valueOf()};var $=function(){};$.p=function(){$.q||($.q=new $)};$.p();new Z(0,0,1);new Z(9999,11,31);I||K&&P("525");t("ae.init",function(){Ja();Ka();Ea(window,"load",function(){});La()});
+this.setDate(a.getDate()),Y(this,a.getDate()))};h.r=function(){return[this.getFullYear(),x(this.getMonth()+1),x(this.getDate())].join("")+""};h.toString=function(){return this.r()};var Y=function(a,b){if(a.getDate()!=b){var c=a.getDate()<b?1:-1;a.a.setUTCHours(a.a.getUTCHours()+c)}};Z.prototype.valueOf=function(){return this.a.valueOf()};var $=function(){};$.p=function(){$.q||($.q=new $)};$.p();new Z(0,0,1);new Z(9999,11,31);I||K&&P("525");t("ae.init",function(){Ja();Ka();Ea(window,"load",function(){});La()});
var Ja=function(){var a;a=document;if(a=q("ae-content")?a.getElementById("ae-content"):"ae-content"){a=Q("table","ae-table-striped",a);for(var b=0,c;c=a[b];b++){c=Q("tbody",null,c);for(var d=0,e;e=c[d];d++){e=Q("tr",null,e);for(var f=0,g;g=e[f];f++)f%2&&qa(g,"ae-even")}}}},Ka=function(){var a=Q(null,"ae-noscript",void 0);ca(C(a),function(a){sa(a,"ae-noscript")})},La=function(){m._gaq=m._gaq||[];m._gaq.push(function(){m._gaq._createAsyncTracker("UA-3739047-3","ae")._trackPageview()});(function(){var a=
document.createElement("script");a.src=("https:"==document.location.protocol?"https://ssl":"http://www")+".google-analytics.com/ga.js";a.setAttribute("async","true");document.documentElement.firstChild.appendChild(a)})()};t("ae.trackPageView",function(){m._gaq&&m._gaq._getAsyncTracker("ae")._trackPageview()});var Na=function(a){if(void 0==a||null==a||0==a.length)return 0;a=Math.max.apply(Math,a);return Ma(a)},Ma=function(a){var b=5;2>b&&(b=2);b-=1;return Math.ceil(a/b)*b},Oa=function(a,b,c){a=a.getSelection();1==a.length&&(a=a[0],null!=a.row&&(null!=b.starttime&&(c+="&starttime="+b.starttime),null!=b.endtime&&(c+="&endtime="+b.endtime),null!=b.latency_lower&&(c+="&latency_lower="+b.latency_lower),null!=b.latency_upper&&(c+="&latency_upper="+b.latency_upper),b=c+"&detail="+a.row,window.location.href=b))},
Pa=function(a,b,c,d,e){var f=new google.visualization.DataTable;f.addColumn("string","");f.addColumn("number","");f.addColumn({type:"string",role:"tooltip"});for(var g=0;g<b.length;g++)f.addRow(["",b[g],c[g]]);c=Math.max(10*b.length,200);b=Na(b);a=new google.visualization.ColumnChart(document.getElementById("rpctime-"+a));a.draw(f,{height:100,width:c,legend:"none",chartArea:{left:40},fontSize:11,vAxis:{minValue:0,maxValue:b,gridlines:{count:5}}});google.visualization.events.addListener(a,"select",
diff --git a/google/appengine/ext/appstats/static/appstats_js.js b/google/appengine/ext/appstats/static/appstats_js.js
index 5958a1e..9af7e5b 100644
--- a/google/appengine/ext/appstats/static/appstats_js.js
+++ b/google/appengine/ext/appstats/static/appstats_js.js
@@ -1,52 +1,52 @@
/* Copyright 2008-10 Google Inc. All Rights Reserved. */ (function(){var f,l=this,aa=function(){},ba=function(a){a.aa=function(){return a.Eb?a.Eb:a.Eb=new a}},ca=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=
typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},da=function(a){return"array"==ca(a)},ea=function(a){var b=ca(a);return"array"==b||"object"==b&&"number"==typeof a.length},m=function(a){return"string"==typeof a},n=function(a){return"function"==ca(a)},fa=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},ia=function(a){return a[ga]||
-(a[ga]=++ha)},ga="closure_uid_"+(1E9*Math.random()>>>0),ha=0,ja=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},la=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},p=function(a,b){function c(){}c.prototype=b.prototype;a.f=b.prototype;a.prototype=new c;a.prototype.constructor=a};var ma=function(a){Error.captureStackTrace?Error.captureStackTrace(this,ma):this.stack=Error().stack||"";a&&(this.message=String(a))};p(ma,Error);ma.prototype.name="CustomError";var na=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")},oa=function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},ua=function(a){if(!pa.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(qa,"&"));-1!=a.indexOf("<")&&(a=a.replace(ra,"<"));-1!=a.indexOf(">")&&(a=a.replace(sa,">"));-1!=a.indexOf('"')&&(a=a.replace(ta,"""));return a},qa=/&/g,ra=/</g,sa=/>/g,ta=/\"/g,pa=/[&<>\"]/;var va=function(a,b){b.unshift(a);ma.call(this,na.apply(null,b));b.shift()};p(va,ma);va.prototype.name="AssertionError";var wa=function(a,b,c){var d="Assertion failed";if(b)var d=d+(": "+b),e=c;else a&&(d+=": "+a,e=null);throw new va(""+d,e||[]);},r=function(a,b,c){a||wa("",b,Array.prototype.slice.call(arguments,2))},xa=function(a,b,c,d){a instanceof b||wa("instanceof check failed.",c,Array.prototype.slice.call(arguments,3))};var s=Array.prototype,ya=s.indexOf?function(a,b,c){r(null!=a.length);return s.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},za=s.forEach?function(a,b,c){r(null!=a.length);s.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)g in e&&b.call(c,e[g],g,a)},Aa=s.filter?function(a,b,c){r(null!=a.length);return s.filter.call(a,
-b,c)}:function(a,b,c){for(var d=a.length,e=[],g=0,h=m(a)?a.split(""):a,k=0;k<d;k++)if(k in h){var q=h[k];b.call(c,q,k,a)&&(e[g++]=q)}return e},Ba=s.every?function(a,b,c){r(null!=a.length);return s.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)if(g in e&&!b.call(c,e[g],g,a))return!1;return!0},t=function(a,b){return 0<=ya(a,b)},Ca=function(a,b){var c=ya(a,b),d;if(d=0<=c)r(null!=a.length),s.splice.call(a,c,1);return d},Da=function(a){var b=a.length;if(0<b){for(var c=
-Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},Fa=function(a,b,c,d){r(null!=a.length);s.splice.apply(a,Ea(arguments,1))},Ea=function(a,b,c){r(null!=a.length);return 2>=arguments.length?s.slice.call(a,b):s.slice.call(a,b,c)};var Ga=function(a,b){for(var c in a)b.call(void 0,a[c],c,a)},Ha=function(a,b){for(var c in a)if(a[c]==b)return!0;return!1},Ia=function(a,b,c){if(b in a)throw Error('The object already contains the key "'+b+'"');a[b]=c},Ja=function(a){var b={},c;for(c in a)b[a[c]]=c;return b},Ka="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),La=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var g=0;g<Ka.length;g++)c=
-Ka[g],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var u,Ma,Na,Oa,Pa=function(){return l.navigator?l.navigator.userAgent:null};Oa=Na=Ma=u=!1;var Qa;if(Qa=Pa()){var Ra=l.navigator;u=0==Qa.lastIndexOf("Opera",0);Ma=!u&&(-1!=Qa.indexOf("MSIE")||-1!=Qa.indexOf("Trident"));Na=!u&&-1!=Qa.indexOf("WebKit");Oa=!u&&!Na&&!Ma&&"Gecko"==Ra.product}var Sa=u,v=Ma,w=Oa,x=Na,Ta=l.navigator,y=-1!=(Ta&&Ta.platform||"").indexOf("Mac"),Ua=function(){var a=l.document;return a?a.documentMode:void 0},Va;
-t:{var Wa="",Xa;if(Sa&&l.opera)var Ya=l.opera.version,Wa="function"==typeof Ya?Ya():Ya;else if(w?Xa=/rv\:([^\);]+)(\)|;)/:v?Xa=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:x&&(Xa=/WebKit\/(\S+)/),Xa)var Za=Xa.exec(Pa()),Wa=Za?Za[1]:"";if(v){var $a=Ua();if($a>parseFloat(Wa)){Va=String($a);break t}}Va=Wa}
-var ab=Va,bb={},z=function(a){var b;if(!(b=bb[a])){b=0;for(var c=oa(String(ab)).split("."),d=oa(String(a)).split("."),e=Math.max(c.length,d.length),g=0;0==b&&g<e;g++){var h=c[g]||"",k=d[g]||"",q=RegExp("(\\d*)(\\D*)","g"),ka=RegExp("(\\d*)(\\D*)","g");do{var A=q.exec(h)||["","",""],I=ka.exec(k)||["","",""];if(0==A[0].length&&0==I[0].length)break;b=((0==A[1].length?0:parseInt(A[1],10))<(0==I[1].length?0:parseInt(I[1],10))?-1:(0==A[1].length?0:parseInt(A[1],10))>(0==I[1].length?0:parseInt(I[1],10))?
-1:0)||((0==A[2].length)<(0==I[2].length)?-1:(0==A[2].length)>(0==I[2].length)?1:0)||(A[2]<I[2]?-1:A[2]>I[2]?1:0)}while(0==b)}b=bb[a]=0<=b}return b},cb=l.document,db=cb&&v?Ua()||("CSS1Compat"==cb.compatMode?parseInt(ab,10):5):void 0;var eb,fb=!v||v&&9<=db;!w&&!v||v&&v&&9<=db||w&&z("1.9.1");var gb=v&&!z("9");var B=function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},C=function(a,b){for(var c=B(a),d=Ea(arguments,1),e=c.length+d.length,g=c,h=0;h<d.length;h++)t(g,d[h])||g.push(d[h]);a.className=c.join(" ");return c.length==e},ib=function(a,b){var c=B(a),d=Ea(arguments,1),e=hb(c,d);a.className=e.join(" ");return e.length==c.length-d.length},hb=function(a,b){return Aa(a,function(a){return!t(b,a)})};var lb=function(a){return a?new jb(kb(a)):eb||(eb=new jb)},mb=function(a,b){return m(b)?a.getElementById(b):b},nb=function(a,b,c){var d=document;c=c||d;a=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(a||b))return c.querySelectorAll(a+(b?"."+b:""));if(b&&c.getElementsByClassName){c=c.getElementsByClassName(b);if(a){for(var d={},e=0,g=0,h;h=c[g];g++)a==h.nodeName&&(d[e++]=h);d.length=e;return d}return c}c=c.getElementsByTagName(a||"*");if(b){d={};for(g=e=0;h=c[g];g++)a=h.className,
-"function"==typeof a.split&&t(a.split(/\s+/),b)&&(d[e++]=h);d.length=e;return d}return c},pb=function(a,b){Ga(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in ob?a.setAttribute(ob[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},ob={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",
-valign:"vAlign",width:"width"},rb=function(a,b,c){return qb(document,arguments)},qb=function(a,b){var c=b[0],d=b[1];if(!fb&&d&&(d.name||d.type)){c=["<",c];d.name&&c.push(' name="',ua(d.name),'"');if(d.type){c.push(' type="',ua(d.type),'"');var e={};La(e,d);delete e.type;d=e}c.push(">");c=c.join("")}c=a.createElement(c);d&&(m(d)?c.className=d:da(d)?C.apply(null,[c].concat(d)):pb(c,d));2<b.length&&sb(a,c,b);return c},sb=function(a,b,c){function d(c){c&&b.appendChild(m(c)?a.createTextNode(c):c)}for(var e=
-2;e<c.length;e++){var g=c[e];if(!ea(g)||fa(g)&&0<g.nodeType)d(g);else{var h;t:{if(g&&"number"==typeof g.length){if(fa(g)){h="function"==typeof g.item||"string"==typeof g.item;break t}if(n(g)){h="function"==typeof g.item;break t}}h=!1}za(h?Da(g):g,d)}}},tb=function(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a},kb=function(a){return 9==a.nodeType?
+(a[ga]=++ha)},ga="closure_uid_"+(1E9*Math.random()>>>0),ha=0,ja=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}},la=function(a,b){var c=a.split("."),d=l;c[0]in d||!d.execScript||d.execScript("var "+c[0]);for(var e;c.length&&(e=c.shift());)c.length||void 0===b?d=d[e]?d[e]:d[e]={}:d[e]=b},p=function(a,b){function c(){}c.prototype=b.prototype;a.f=b.prototype;a.prototype=new c;a.prototype.constructor=a};var ma=function(a){Error.captureStackTrace?Error.captureStackTrace(this,ma):this.stack=Error().stack||"";a&&(this.message=String(a))};p(ma,Error);ma.prototype.name="CustomError";var na;var oa=function(a,b){for(var c=a.split("%s"),d="",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join("%s")},pa=function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},va=function(a){if(!qa.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ra,"&"));-1!=a.indexOf("<")&&(a=a.replace(sa,"<"));-1!=a.indexOf(">")&&(a=a.replace(ta,">"));-1!=a.indexOf('"')&&(a=a.replace(ua,"""));return a},ra=/&/g,sa=/</g,ta=/>/g,ua=/\"/g,qa=/[&<>\"]/;var wa=function(a,b){b.unshift(a);ma.call(this,oa.apply(null,b));b.shift()};p(wa,ma);wa.prototype.name="AssertionError";var xa=function(a,b,c){var d="Assertion failed";if(b)var d=d+(": "+b),e=c;else a&&(d+=": "+a,e=null);throw new wa(""+d,e||[]);},r=function(a,b,c){a||xa("",b,Array.prototype.slice.call(arguments,2))},ya=function(a,b,c,d){a instanceof b||xa("instanceof check failed.",c,Array.prototype.slice.call(arguments,3))};var s=Array.prototype,za=s.indexOf?function(a,b,c){r(null!=a.length);return s.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(m(a))return m(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Aa=s.forEach?function(a,b,c){r(null!=a.length);s.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)g in e&&b.call(c,e[g],g,a)},Ba=s.filter?function(a,b,c){r(null!=a.length);return s.filter.call(a,
+b,c)}:function(a,b,c){for(var d=a.length,e=[],g=0,h=m(a)?a.split(""):a,k=0;k<d;k++)if(k in h){var q=h[k];b.call(c,q,k,a)&&(e[g++]=q)}return e},Ca=s.every?function(a,b,c){r(null!=a.length);return s.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,g=0;g<d;g++)if(g in e&&!b.call(c,e[g],g,a))return!1;return!0},t=function(a,b){return 0<=za(a,b)},Da=function(a,b){var c=za(a,b),d;if(d=0<=c)r(null!=a.length),s.splice.call(a,c,1);return d},Ea=function(a){var b=a.length;if(0<b){for(var c=
+Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]},Ga=function(a,b,c,d){r(null!=a.length);s.splice.apply(a,Fa(arguments,1))},Fa=function(a,b,c){r(null!=a.length);return 2>=arguments.length?s.slice.call(a,b):s.slice.call(a,b,c)};var Ha=function(a,b){for(var c in a)b.call(void 0,a[c],c,a)},Ia=function(a,b){for(var c in a)if(a[c]==b)return!0;return!1},Ja=function(a,b,c){if(b in a)throw Error('The object already contains the key "'+b+'"');a[b]=c},Ka=function(a){var b={},c;for(c in a)b[a[c]]=c;return b},La="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Ma=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var g=0;g<La.length;g++)c=
+La[g],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var u,Na,Oa,Pa,Qa=function(){return l.navigator?l.navigator.userAgent:null};Pa=Oa=Na=u=!1;var Ra;if(Ra=Qa()){var Sa=l.navigator;u=0==Ra.lastIndexOf("Opera",0);Na=!u&&(-1!=Ra.indexOf("MSIE")||-1!=Ra.indexOf("Trident"));Oa=!u&&-1!=Ra.indexOf("WebKit");Pa=!u&&!Oa&&!Na&&"Gecko"==Sa.product}var Ta=u,v=Na,w=Pa,x=Oa,Ua=l.navigator,y=-1!=(Ua&&Ua.platform||"").indexOf("Mac"),Va=function(){var a=l.document;return a?a.documentMode:void 0},Wa;
+t:{var Xa="",Ya;if(Ta&&l.opera)var Za=l.opera.version,Xa="function"==typeof Za?Za():Za;else if(w?Ya=/rv\:([^\);]+)(\)|;)/:v?Ya=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:x&&(Ya=/WebKit\/(\S+)/),Ya)var $a=Ya.exec(Qa()),Xa=$a?$a[1]:"";if(v){var ab=Va();if(ab>parseFloat(Xa)){Wa=String(ab);break t}}Wa=Xa}
+var bb=Wa,cb={},z=function(a){var b;if(!(b=cb[a])){b=0;for(var c=pa(String(bb)).split("."),d=pa(String(a)).split("."),e=Math.max(c.length,d.length),g=0;0==b&&g<e;g++){var h=c[g]||"",k=d[g]||"",q=RegExp("(\\d*)(\\D*)","g"),ka=RegExp("(\\d*)(\\D*)","g");do{var A=q.exec(h)||["","",""],I=ka.exec(k)||["","",""];if(0==A[0].length&&0==I[0].length)break;b=((0==A[1].length?0:parseInt(A[1],10))<(0==I[1].length?0:parseInt(I[1],10))?-1:(0==A[1].length?0:parseInt(A[1],10))>(0==I[1].length?0:parseInt(I[1],10))?
+1:0)||((0==A[2].length)<(0==I[2].length)?-1:(0==A[2].length)>(0==I[2].length)?1:0)||(A[2]<I[2]?-1:A[2]>I[2]?1:0)}while(0==b)}b=cb[a]=0<=b}return b},db=l.document,eb=db&&v?Va()||("CSS1Compat"==db.compatMode?parseInt(bb,10):5):void 0;var fb=!v||v&&9<=eb;!w&&!v||v&&v&&9<=eb||w&&z("1.9.1");var gb=v&&!z("9");var B=function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},C=function(a,b){for(var c=B(a),d=Fa(arguments,1),e=c.length+d.length,g=c,h=0;h<d.length;h++)t(g,d[h])||g.push(d[h]);a.className=c.join(" ");return c.length==e},ib=function(a,b){var c=B(a),d=Fa(arguments,1),e=hb(c,d);a.className=e.join(" ");return e.length==c.length-d.length},hb=function(a,b){return Ba(a,function(a){return!t(b,a)})};var lb=function(a){return a?new jb(kb(a)):na||(na=new jb)},mb=function(a,b){return m(b)?a.getElementById(b):b},nb=function(a,b,c){var d=document;c=c||d;a=a&&"*"!=a?a.toUpperCase():"";if(c.querySelectorAll&&c.querySelector&&(a||b))return c.querySelectorAll(a+(b?"."+b:""));if(b&&c.getElementsByClassName){c=c.getElementsByClassName(b);if(a){for(var d={},e=0,g=0,h;h=c[g];g++)a==h.nodeName&&(d[e++]=h);d.length=e;return d}return c}c=c.getElementsByTagName(a||"*");if(b){d={};for(g=e=0;h=c[g];g++)a=h.className,
+"function"==typeof a.split&&t(a.split(/\s+/),b)&&(d[e++]=h);d.length=e;return d}return c},pb=function(a,b){Ha(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in ob?a.setAttribute(ob[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})},ob={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",
+valign:"vAlign",width:"width"},rb=function(a,b,c){return qb(document,arguments)},qb=function(a,b){var c=b[0],d=b[1];if(!fb&&d&&(d.name||d.type)){c=["<",c];d.name&&c.push(' name="',va(d.name),'"');if(d.type){c.push(' type="',va(d.type),'"');var e={};Ma(e,d);delete e.type;d=e}c.push(">");c=c.join("")}c=a.createElement(c);d&&(m(d)?c.className=d:da(d)?C.apply(null,[c].concat(d)):pb(c,d));2<b.length&&sb(a,c,b);return c},sb=function(a,b,c){function d(c){c&&b.appendChild(m(c)?a.createTextNode(c):c)}for(var e=
+2;e<c.length;e++){var g=c[e];if(!ea(g)||fa(g)&&0<g.nodeType)d(g);else{var h;t:{if(g&&"number"==typeof g.length){if(fa(g)){h="function"==typeof g.item||"string"==typeof g.item;break t}if(n(g)){h="function"==typeof g.item;break t}}h=!1}Aa(h?Ea(g):g,d)}}},tb=function(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a},kb=function(a){return 9==a.nodeType?
a:a.ownerDocument||a.document},ub=function(a,b){if("textContent"in a)a.textContent=b;else if(a.firstChild&&3==a.firstChild.nodeType){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else{for(var c;c=a.firstChild;)a.removeChild(c);a.appendChild(kb(a).createTextNode(String(b)))}},vb={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},wb={IMG:" ",BR:"\n"},xb=function(a){a=a.getAttributeNode("tabindex");return null!=a&&a.specified},yb=function(a){a=a.tabIndex;return"number"==typeof a&&
0<=a&&32768>a},zb=function(a,b,c){if(!(a.nodeName in vb))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in wb)b.push(wb[a.nodeName]);else for(a=a.firstChild;a;)zb(a,b,c),a=a.nextSibling},jb=function(a){this.F=a||l.document||document};f=jb.prototype;f.kb=lb;f.a=function(a){return mb(this.F,a)};f.r=function(a,b,c){return qb(this.F,arguments)};f.createElement=function(a){return this.F.createElement(a)};f.createTextNode=function(a){return this.F.createTextNode(String(a))};
-f.appendChild=function(a,b){a.appendChild(b)};f.contains=tb;f.J=function(a){var b;(b="INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!xb(a)||yb(a)):xb(a)&&yb(a))&&v?(a=n(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0<a.height&&0<a.width):a=b;return a};var Ab=function(a){Ab[" "](a);return a};Ab[" "]=aa;var Bb=!v||v&&9<=db,Cb=!v||v&&9<=db,Db=v&&!z("9");!x||z("528");w&&z("1.9b")||v&&z("8")||Sa&&z("9.5")||x&&z("528");w&&!z("8")||v&&z("9");var Eb=function(){};Eb.prototype.Sb=!1;var D=function(a,b){this.type=a;this.currentTarget=this.target=b};f=D.prototype;f.S=!1;f.defaultPrevented=!1;f.xb=!0;f.stopPropagation=function(){this.S=!0};f.preventDefault=function(){this.defaultPrevented=!0;this.xb=!1};var E=function(a,b){a&&Fb(this,a,b)};p(E,D);var Gb=[1,4,2];f=E.prototype;f.target=null;f.relatedTarget=null;f.offsetX=0;f.offsetY=0;f.clientX=0;f.clientY=0;f.screenX=0;f.screenY=0;f.button=0;f.keyCode=0;f.charCode=0;f.ctrlKey=!1;f.altKey=!1;f.shiftKey=!1;f.metaKey=!1;f.bb=!1;f.Q=null;
+f.appendChild=function(a,b){a.appendChild(b)};f.contains=tb;f.J=function(a){var b;(b="INPUT"==a.tagName||"TEXTAREA"==a.tagName||"SELECT"==a.tagName||"BUTTON"==a.tagName?!a.disabled&&(!xb(a)||yb(a)):xb(a)&&yb(a))&&v?(a=n(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth},a=null!=a&&0<a.height&&0<a.width):a=b;return a};var Ab=function(a){Ab[" "](a);return a};Ab[" "]=aa;var Bb=!v||v&&9<=eb,Cb=!v||v&&9<=eb,Db=v&&!z("9");!x||z("528");w&&z("1.9b")||v&&z("8")||Ta&&z("9.5")||x&&z("528");w&&!z("8")||v&&z("9");var Eb=function(){};Eb.prototype.Sb=!1;var D=function(a,b){this.type=a;this.currentTarget=this.target=b};f=D.prototype;f.S=!1;f.defaultPrevented=!1;f.xb=!0;f.stopPropagation=function(){this.S=!0};f.preventDefault=function(){this.defaultPrevented=!0;this.xb=!1};var E=function(a,b){a&&Fb(this,a,b)};p(E,D);var Gb=[1,4,2];f=E.prototype;f.target=null;f.relatedTarget=null;f.offsetX=0;f.offsetY=0;f.clientX=0;f.clientY=0;f.screenX=0;f.screenY=0;f.button=0;f.keyCode=0;f.charCode=0;f.ctrlKey=!1;f.altKey=!1;f.shiftKey=!1;f.metaKey=!1;f.bb=!1;f.Q=null;
var Fb=function(a,b,c){var d=a.type=b.type;D.call(a,d);a.target=b.target||b.srcElement;a.currentTarget=c;if(c=b.relatedTarget){if(w){var e;t:{try{Ab(c.nodeName);e=!0;break t}catch(g){}e=!1}e||(c=null)}}else"mouseover"==d?c=b.fromElement:"mouseout"==d&&(c=b.toElement);a.relatedTarget=c;a.offsetX=x||void 0!==b.offsetX?b.offsetX:b.layerX;a.offsetY=x||void 0!==b.offsetY?b.offsetY:b.layerY;a.clientX=void 0!==b.clientX?b.clientX:b.pageX;a.clientY=void 0!==b.clientY?b.clientY:b.pageY;a.screenX=b.screenX||
0;a.screenY=b.screenY||0;a.button=b.button;a.keyCode=b.keyCode||0;a.charCode=b.charCode||("keypress"==d?b.keyCode:0);a.ctrlKey=b.ctrlKey;a.altKey=b.altKey;a.shiftKey=b.shiftKey;a.metaKey=b.metaKey;a.bb=y?b.metaKey:b.ctrlKey;a.state=b.state;a.Q=b;b.defaultPrevented&&a.preventDefault();delete a.S},Hb=function(a){return Bb?0==a.Q.button:"click"==a.type?!0:!!(a.Q.button&Gb[0])};
E.prototype.stopPropagation=function(){E.f.stopPropagation.call(this);this.Q.stopPropagation?this.Q.stopPropagation():this.Q.cancelBubble=!0};E.prototype.preventDefault=function(){E.f.preventDefault.call(this);var a=this.Q;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Db)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Ib="closure_listenable_"+(1E6*Math.random()|0),Jb=function(a){try{return!(!a||!a[Ib])}catch(b){return!1}},Kb=0;var Lb=function(a,b,c,d,e){this.$=a;this.Da=null;this.src=b;this.type=c;this.capture=!!d;this.Ia=e;this.key=++Kb;this.ha=this.Ga=!1},Mb=function(a){a.ha=!0;a.$=null;a.Da=null;a.src=null;a.Ia=null};var F=function(a){this.src=a;this.p={};this.va=0};F.prototype.add=function(a,b,c,d,e){var g=this.p[a];g||(g=this.p[a]=[],this.va++);var h=Nb(g,b,d,e);-1<h?(a=g[h],c||(a.Ga=!1)):(a=new Lb(b,this.src,a,!!d,e),a.Ga=c,g.push(a));return a};F.prototype.remove=function(a,b,c,d){if(!(a in this.p))return!1;var e=this.p[a];b=Nb(e,b,c,d);return-1<b?(Mb(e[b]),r(null!=e.length),s.splice.call(e,b,1),0==e.length&&(delete this.p[a],this.va--),!0):!1};
-var Ob=function(a,b){var c=b.type;if(!(c in a.p))return!1;var d=Ca(a.p[c],b);d&&(Mb(b),0==a.p[c].length&&(delete a.p[c],a.va--));return d};F.prototype.$a=function(a){var b=0,c;for(c in this.p)if(!a||c==a){for(var d=this.p[c],e=0;e<d.length;e++)++b,Mb(d[e]);delete this.p[c];this.va--}return b};F.prototype.wa=function(a,b,c,d){a=this.p[a];var e=-1;a&&(e=Nb(a,b,c,d));return-1<e?a[e]:null};var Nb=function(a,b,c,d){for(var e=0;e<a.length;++e){var g=a[e];if(!g.ha&&g.$==b&&g.capture==!!c&&g.Ia==d)return e}return-1};var Pb="closure_lm_"+(1E6*Math.random()|0),G={},Qb=0,H=function(a,b,c,d,e){if(da(b)){for(var g=0;g<b.length;g++)H(a,b[g],c,d,e);return null}c=Rb(c);if(Jb(a))a=a.d(b,c,d,e);else{if(!b)throw Error("Invalid event type");var g=!!d,h=Sb(a);h||(a[Pb]=h=new F(a));c=h.add(b,c,!1,d,e);c.Da||(d=Tb(),c.Da=d,d.src=a,d.$=c,a.addEventListener?a.addEventListener(b,d,g):a.attachEvent(b in G?G[b]:G[b]="on"+b,d),Qb++);a=c}return a},Tb=function(){var a=Ub,b=Cb?function(c){return a.call(b.src,b.$,c)}:function(c){c=a.call(b.src,
+var Ob=function(a,b){var c=b.type;if(!(c in a.p))return!1;var d=Da(a.p[c],b);d&&(Mb(b),0==a.p[c].length&&(delete a.p[c],a.va--));return d};F.prototype.$a=function(a){var b=0,c;for(c in this.p)if(!a||c==a){for(var d=this.p[c],e=0;e<d.length;e++)++b,Mb(d[e]);delete this.p[c];this.va--}return b};F.prototype.wa=function(a,b,c,d){a=this.p[a];var e=-1;a&&(e=Nb(a,b,c,d));return-1<e?a[e]:null};var Nb=function(a,b,c,d){for(var e=0;e<a.length;++e){var g=a[e];if(!g.ha&&g.$==b&&g.capture==!!c&&g.Ia==d)return e}return-1};var Pb="closure_lm_"+(1E6*Math.random()|0),G={},Qb=0,H=function(a,b,c,d,e){if(da(b)){for(var g=0;g<b.length;g++)H(a,b[g],c,d,e);return null}c=Rb(c);if(Jb(a))a=a.d(b,c,d,e);else{if(!b)throw Error("Invalid event type");var g=!!d,h=Sb(a);h||(a[Pb]=h=new F(a));c=h.add(b,c,!1,d,e);c.Da||(d=Tb(),c.Da=d,d.src=a,d.$=c,a.addEventListener?a.addEventListener(b,d,g):a.attachEvent(b in G?G[b]:G[b]="on"+b,d),Qb++);a=c}return a},Tb=function(){var a=Ub,b=Cb?function(c){return a.call(b.src,b.$,c)}:function(c){c=a.call(b.src,
b.$,c);if(!c)return c};return b},Vb=function(a,b,c,d,e){if(da(b))for(var g=0;g<b.length;g++)Vb(a,b[g],c,d,e);else c=Rb(c),Jb(a)?a.w(b,c,d,e):a&&(a=Sb(a))&&(b=a.wa(b,c,!!d,e))&&J(b)},J=function(a){if("number"==typeof a||!a||a.ha)return!1;var b=a.src;if(Jb(b))return Ob(b.ba,a);var c=a.type,d=a.Da;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(c in G?G[c]:G[c]="on"+c,d);Qb--;(c=Sb(b))?(Ob(c,a),0==c.va&&(c.src=null,b[Pb]=null)):Mb(a);return!0},Xb=function(a,b,
-c,d){var e=1;if(a=Sb(a))if(b=a.p[b])for(b=Da(b),a=0;a<b.length;a++){var g=b[a];g&&g.capture==c&&!g.ha&&(e&=!1!==Wb(g,d))}return Boolean(e)},Wb=function(a,b){var c=a.$,d=a.Ia||a.src;a.Ga&&J(a);return c.call(d,b)},Ub=function(a,b){if(a.ha)return!0;if(!Cb){var c;if(!(c=b))t:{c=["window","event"];for(var d=l,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new E(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var g=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(h){g=!0}if(g||
+c,d){var e=1;if(a=Sb(a))if(b=a.p[b])for(b=Ea(b),a=0;a<b.length;a++){var g=b[a];g&&g.capture==c&&!g.ha&&(e&=!1!==Wb(g,d))}return Boolean(e)},Wb=function(a,b){var c=a.$,d=a.Ia||a.src;a.Ga&&J(a);return c.call(d,b)},Ub=function(a,b){if(a.ha)return!0;if(!Cb){var c;if(!(c=b))t:{c=["window","event"];for(var d=l,e;e=c.shift();)if(null!=d[e])d=d[e];else{c=null;break t}c=d}e=c;c=new E(e,this);d=!0;if(!(0>e.keyCode||void 0!=e.returnValue)){t:{var g=!1;if(0==e.keyCode)try{e.keyCode=-1;break t}catch(h){g=!0}if(g||
void 0==e.returnValue)e.returnValue=!0}e=[];for(g=c.currentTarget;g;g=g.parentNode)e.push(g);for(var g=a.type,k=e.length-1;!c.S&&0<=k;k--)c.currentTarget=e[k],d&=Xb(e[k],g,!0,c);for(k=0;!c.S&&k<e.length;k++)c.currentTarget=e[k],d&=Xb(e[k],g,!1,c)}return d}return Wb(a,new E(b,this))},Sb=function(a){a=a[Pb];return a instanceof F?a:null},Yb="__closure_events_fn_"+(1E9*Math.random()>>>0),Rb=function(a){r(a,"Listener can not be null.");if(n(a))return a;r(a.handleEvent,"An object listener must have handleEvent method.");
return a[Yb]||(a[Yb]=function(b){return a.handleEvent(b)})};var K=function(a){this.Db=a;this.La={}};p(K,Eb);var Zb=[];K.prototype.d=function(a,b,c,d,e){da(b)||(Zb[0]=b,b=Zb);for(var g=0;g<b.length;g++){var h=H(a,b[g],c||this,d||!1,e||this.Db||this);if(!h)break;this.La[h.key]=h}return this};K.prototype.w=function(a,b,c,d,e){if(da(b))for(var g=0;g<b.length;g++)this.w(a,b[g],c,d,e);else e=e||this.Db||this,c=Rb(c||this),d=!!d,b=Jb(a)?a.wa(b,c,d,e):a?(a=Sb(a))?a.wa(b,c,d,e):null:null,b&&(J(b),delete this.La[b.key]);return this};
-K.prototype.$a=function(){Ga(this.La,J);this.La={}};K.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var L=function(){this.ba=new F(this);this.bc=this};p(L,Eb);L.prototype[Ib]=!0;f=L.prototype;f.mb=null;f.Ya=function(a){this.mb=a};f.addEventListener=function(a,b,c,d){H(this,a,b,c,d)};f.removeEventListener=function(a,b,c,d){Vb(this,a,b,c,d)};
-f.dispatchEvent=function(a){$b(this);var b,c=this.mb;if(c){b=[];for(var d=1;c;c=c.mb)b.push(c),r(1E3>++d,"infinite loop")}c=this.bc;d=a.type||a;if(m(a))a=new D(a,c);else if(a instanceof D)a.target=a.target||c;else{var e=a;a=new D(d,c);La(a,e)}var e=!0,g;if(b)for(var h=b.length-1;!a.S&&0<=h;h--)g=a.currentTarget=b[h],e=ac(g,d,!0,a)&&e;a.S||(g=a.currentTarget=c,e=ac(g,d,!0,a)&&e,a.S||(e=ac(g,d,!1,a)&&e));if(b)for(h=0;!a.S&&h<b.length;h++)g=a.currentTarget=b[h],e=ac(g,d,!1,a)&&e;return e};
-f.d=function(a,b,c,d){$b(this);return this.ba.add(a,b,!1,c,d)};f.w=function(a,b,c,d){return this.ba.remove(a,b,c,d)};var ac=function(a,b,c,d){b=a.ba.p[b];if(!b)return!0;b=Da(b);for(var e=!0,g=0;g<b.length;++g){var h=b[g];if(h&&!h.ha&&h.capture==c){var k=h.$,q=h.Ia||h.src;h.Ga&&Ob(a.ba,h);e=!1!==k.call(q,d)&&e}}return e&&!1!=d.xb};L.prototype.wa=function(a,b,c,d){return this.ba.wa(a,b,c,d)};var $b=function(a){r(a.ba,"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")};var M=function(a,b){a.style.display=b?"":"none"},bc=w?"MozUserSelect":x?"WebkitUserSelect":null,cc=function(a,b,c){c=c?null:a.getElementsByTagName("*");if(bc){if(b=b?"none":"",a.style[bc]=b,c){a=0;for(var d;d=c[a];a++)d.style[bc]=b}}else if(v||Sa)if(b=b?"on":"",a.setAttribute("unselectable",b),c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)};var dc=function(){};ba(dc);dc.prototype.ec=0;dc.aa();var N=function(a){L.call(this);this.n=a||lb();this.sa=ec};p(N,L);N.prototype.dc=dc.aa();var ec=null,fc=function(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}throw Error("Invalid component state");};f=N.prototype;f.P=null;f.e=!1;f.c=null;f.sa=null;f.o=null;f.s=null;f.i=null;
-var gc=function(a){return a.P||(a.P=":"+(a.dc.ec++).toString(36))},hc=function(a,b){if(a.o&&a.o.i){var c=a.o.i,d=a.P;d in c&&delete c[d];Ia(a.o.i,b,a)}a.P=b};N.prototype.a=function(){return this.c};var ic=function(a){return a.ib||(a.ib=new K(a))},jc=function(a,b){if(a==b)throw Error("Unable to set parent component");if(b&&a.o&&a.P&&a.o.i&&a.P&&a.P in a.o.i&&a.o.i[a.P]&&a.o!=b)throw Error("Unable to set parent component");a.o=b;N.f.Ya.call(a,b)};f=N.prototype;f.getParent=function(){return this.o};
+K.prototype.$a=function(){Ha(this.La,J);this.La={}};K.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var L=function(){this.ba=new F(this);this.bc=this};p(L,Eb);L.prototype[Ib]=!0;f=L.prototype;f.mb=null;f.Ya=function(a){this.mb=a};f.addEventListener=function(a,b,c,d){H(this,a,b,c,d)};f.removeEventListener=function(a,b,c,d){Vb(this,a,b,c,d)};
+f.dispatchEvent=function(a){$b(this);var b,c=this.mb;if(c){b=[];for(var d=1;c;c=c.mb)b.push(c),r(1E3>++d,"infinite loop")}c=this.bc;d=a.type||a;if(m(a))a=new D(a,c);else if(a instanceof D)a.target=a.target||c;else{var e=a;a=new D(d,c);Ma(a,e)}var e=!0,g;if(b)for(var h=b.length-1;!a.S&&0<=h;h--)g=a.currentTarget=b[h],e=ac(g,d,!0,a)&&e;a.S||(g=a.currentTarget=c,e=ac(g,d,!0,a)&&e,a.S||(e=ac(g,d,!1,a)&&e));if(b)for(h=0;!a.S&&h<b.length;h++)g=a.currentTarget=b[h],e=ac(g,d,!1,a)&&e;return e};
+f.d=function(a,b,c,d){$b(this);return this.ba.add(String(a),b,!1,c,d)};f.w=function(a,b,c,d){return this.ba.remove(String(a),b,c,d)};var ac=function(a,b,c,d){b=a.ba.p[String(b)];if(!b)return!0;b=Ea(b);for(var e=!0,g=0;g<b.length;++g){var h=b[g];if(h&&!h.ha&&h.capture==c){var k=h.$,q=h.Ia||h.src;h.Ga&&Ob(a.ba,h);e=!1!==k.call(q,d)&&e}}return e&&!1!=d.xb};L.prototype.wa=function(a,b,c,d){return this.ba.wa(String(a),b,c,d)};var $b=function(a){r(a.ba,"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")};var M=function(a,b){a.style.display=b?"":"none"},bc=w?"MozUserSelect":x?"WebkitUserSelect":null,cc=function(a,b,c){c=c?null:a.getElementsByTagName("*");if(bc){if(b=b?"none":"",a.style[bc]=b,c){a=0;for(var d;d=c[a];a++)d.style[bc]=b}}else if(v||Ta)if(b=b?"on":"",a.setAttribute("unselectable",b),c)for(a=0;d=c[a];a++)d.setAttribute("unselectable",b)};var dc=function(){};ba(dc);dc.prototype.ec=0;dc.aa();var N=function(a){L.call(this);this.n=a||lb();this.sa=ec};p(N,L);N.prototype.dc=dc.aa();var ec=null,fc=function(a,b){switch(a){case 1:return b?"disable":"enable";case 2:return b?"highlight":"unhighlight";case 4:return b?"activate":"deactivate";case 8:return b?"select":"unselect";case 16:return b?"check":"uncheck";case 32:return b?"focus":"blur";case 64:return b?"open":"close"}throw Error("Invalid component state");};f=N.prototype;f.P=null;f.e=!1;f.c=null;f.sa=null;f.o=null;f.s=null;f.j=null;
+var gc=function(a){return a.P||(a.P=":"+(a.dc.ec++).toString(36))},hc=function(a,b){if(a.o&&a.o.j){var c=a.o.j,d=a.P;d in c&&delete c[d];Ja(a.o.j,b,a)}a.P=b};N.prototype.a=function(){return this.c};var ic=function(a){return a.ib||(a.ib=new K(a))},jc=function(a,b){if(a==b)throw Error("Unable to set parent component");if(b&&a.o&&a.P&&a.o.j&&a.P&&a.P in a.o.j&&a.o.j[a.P]&&a.o!=b)throw Error("Unable to set parent component");a.o=b;N.f.Ya.call(a,b)};f=N.prototype;f.getParent=function(){return this.o};
f.Ya=function(a){if(this.o&&this.o!=a)throw Error("Method not supported");N.f.Ya.call(this,a)};f.kb=function(){return this.n};f.r=function(){this.c=this.n.createElement("div")};f.L=function(a){if(this.e)throw Error("Component already rendered");if(a&&this.Z(a)){var b=kb(a);this.n&&this.n.F==b||(this.n=lb(a));this.Xa(a);this.G()}else throw Error("Invalid element to decorate");};f.Z=function(){return!0};f.Xa=function(a){this.c=a};f.G=function(){this.e=!0;kc(this,function(a){!a.e&&a.a()&&a.G()})};
f.da=function(){kc(this,function(a){a.e&&a.da()});this.ib&&this.ib.$a();this.e=!1};f.Ca=function(a,b){this.Ta(a,lc(this),b)};
-f.Ta=function(a,b,c){if(a.e&&(c||!this.e))throw Error("Component already rendered");if(0>b||b>lc(this))throw Error("Child component index out of bounds");this.i&&this.s||(this.i={},this.s=[]);if(a.getParent()==this){var d=gc(a);this.i[d]=a;Ca(this.s,a)}else Ia(this.i,gc(a),a);jc(a,this);Fa(this.s,b,0,a);if(a.e&&this.e&&a.getParent()==this)c=this.C(),c.insertBefore(a.a(),c.childNodes[b]||null);else if(c){this.c||this.r();c=O(this,b+1);b=this.C();c=c?c.c:null;if(a.e)throw Error("Component already rendered");
+f.Ta=function(a,b,c){if(a.e&&(c||!this.e))throw Error("Component already rendered");if(0>b||b>lc(this))throw Error("Child component index out of bounds");this.j&&this.s||(this.j={},this.s=[]);if(a.getParent()==this){var d=gc(a);this.j[d]=a;Da(this.s,a)}else Ja(this.j,gc(a),a);jc(a,this);Ga(this.s,b,0,a);if(a.e&&this.e&&a.getParent()==this)c=this.C(),c.insertBefore(a.a(),c.childNodes[b]||null);else if(c){this.c||this.r();c=O(this,b+1);b=this.C();c=c?c.c:null;if(a.e)throw Error("Component already rendered");
a.c||a.r();b?b.insertBefore(a.c,c||null):a.n.F.body.appendChild(a.c);a.o&&!a.o.e||a.G()}else this.e&&!a.e&&a.c&&a.c.parentNode&&1==a.c.parentNode.nodeType&&a.G()};f.C=function(){return this.c};
var mc=function(a){if(null==a.sa){var b;t:{b=a.e?a.c:a.n.F.body;var c=kb(b);if(c.defaultView&&c.defaultView.getComputedStyle&&(b=c.defaultView.getComputedStyle(b,null))){b=b.direction||b.getPropertyValue("direction")||"";break t}b=""}a.sa="rtl"==(b||((a.e?a.c:a.n.F.body).currentStyle?(a.e?a.c:a.n.F.body).currentStyle.direction:null)||(a.e?a.c:a.n.F.body).style&&(a.e?a.c:a.n.F.body).style.direction)}return a.sa};
-N.prototype.qa=function(a){if(this.e)throw Error("Component already rendered");this.sa=a};var lc=function(a){return a.s?a.s.length:0},O=function(a,b){return a.s?a.s[b]||null:null},kc=function(a,b,c){a.s&&za(a.s,b,c)},nc=function(a,b){return a.s&&b?ya(a.s,b):-1};
-N.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:gc(a);a=this.i&&c?(c in this.i?this.i[c]:void 0)||null:null;if(c&&a){var d=this.i;c in d&&delete d[c];Ca(this.s,a);b&&(a.da(),a.c&&(c=a.c)&&c.parentNode&&c.parentNode.removeChild(c));jc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var oc,pc={kc:"activedescendant",pc:"atomic",qc:"autocomplete",sc:"busy",vc:"checked",Ac:"controls",Cc:"describedby",Fc:"disabled",Hc:"dropeffect",Ic:"expanded",Jc:"flowto",Lc:"grabbed",Pc:"haspopup",Rc:"hidden",Tc:"invalid",Uc:"label",Vc:"labelledby",Wc:"level",ad:"live",ld:"multiline",md:"multiselectable",qd:"orientation",rd:"owns",sd:"posinset",ud:"pressed",yd:"readonly",Ad:"relevant",Bd:"required",Hd:"selected",Jd:"setsize",Ld:"sort",Yd:"valuemax",Zd:"valuemin",$d:"valuenow",ae:"valuetext"};var qc={lc:"alert",mc:"alertdialog",nc:"application",oc:"article",rc:"banner",tc:"button",uc:"checkbox",wc:"columnheader",xc:"combobox",yc:"complementary",zc:"contentinfo",Bc:"definition",Dc:"dialog",Ec:"directory",Gc:"document",Kc:"form",Mc:"grid",Nc:"gridcell",Oc:"group",Qc:"heading",Sc:"img",Xc:"link",Yc:"list",Zc:"listbox",$c:"listitem",bd:"log",cd:"main",dd:"marquee",ed:"math",fd:"menu",gd:"menubar",hd:"menuitem",jd:"menuitemcheckbox",kd:"menuitemradio",nd:"navigation",od:"note",pd:"option",
-td:"presentation",vd:"progressbar",wd:"radio",xd:"radiogroup",zd:"region",Cd:"row",Dd:"rowgroup",Ed:"rowheader",Fd:"scrollbar",Gd:"search",Id:"separator",Kd:"slider",Md:"spinbutton",Nd:"status",Od:"tab",Pd:"tablist",Qd:"tabpanel",Rd:"textbox",Sd:"timer",Td:"toolbar",Ud:"tooltip",Vd:"tree",Wd:"treegrid",Xd:"treeitem"};var rc=function(a,b){b?(r(Ha(qc,b),"No such ARIA role "+b),a.setAttribute("role",b)):a.removeAttribute("role")},tc=function(a,b,c){ea(c)&&(c=c.join(" "));var d=sc(b);""===c||void 0==c?(oc||(oc={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=oc,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d,
-c)},sc=function(a){r(a,"ARIA attribute cannot be empty.");r(Ha(pc,a),"No such ARIA attribute "+a);return"aria-"+a};var vc=function(a,b,c,d,e){if(!(v||x&&z("525")))return!0;if(y&&e)return uc(a);if(e&&!d||!c&&(17==b||18==b||y&&91==b))return!1;if(x&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(v&&d&&b==a)return!1;switch(a){case 13:return!(v&&v&&9<=db);case 27:return!x}return uc(a)},uc=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||x&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;
+N.prototype.qa=function(a){if(this.e)throw Error("Component already rendered");this.sa=a};var lc=function(a){return a.s?a.s.length:0},O=function(a,b){return a.s?a.s[b]||null:null},kc=function(a,b,c){a.s&&Aa(a.s,b,c)},nc=function(a,b){return a.s&&b?za(a.s,b):-1};
+N.prototype.removeChild=function(a,b){if(a){var c=m(a)?a:gc(a);a=this.j&&c?(c in this.j?this.j[c]:void 0)||null:null;if(c&&a){var d=this.j;c in d&&delete d[c];Da(this.s,a);b&&(a.da(),a.c&&(c=a.c)&&c.parentNode&&c.parentNode.removeChild(c));jc(a,null)}}if(!a)throw Error("Child is not in parent component");return a};var oc,pc={kc:"activedescendant",pc:"atomic",qc:"autocomplete",sc:"busy",vc:"checked",Ac:"controls",Cc:"describedby",Fc:"disabled",Hc:"dropeffect",Ic:"expanded",Jc:"flowto",Lc:"grabbed",Pc:"haspopup",Rc:"hidden",Tc:"invalid",Uc:"label",Vc:"labelledby",Wc:"level",ad:"live",ld:"multiline",md:"multiselectable",qd:"orientation",rd:"owns",sd:"posinset",ud:"pressed",yd:"readonly",Ad:"relevant",Bd:"required",Hd:"selected",Jd:"setsize",Ld:"sort",Yd:"valuemax",Zd:"valuemin",$d:"valuenow",ae:"valuetext"};var qc={lc:"alert",mc:"alertdialog",nc:"application",oc:"article",rc:"banner",tc:"button",uc:"checkbox",wc:"columnheader",xc:"combobox",yc:"complementary",zc:"contentinfo",Bc:"definition",Dc:"dialog",Ec:"directory",Gc:"document",Kc:"form",Mc:"grid",Nc:"gridcell",Oc:"group",Qc:"heading",Sc:"img",Xc:"link",Yc:"list",Zc:"listbox",$c:"listitem",bd:"log",cd:"main",dd:"marquee",ed:"math",fd:"menu",gd:"menubar",hd:"menuitem",jd:"menuitemcheckbox",kd:"menuitemradio",nd:"navigation",od:"note",pd:"option",
+td:"presentation",vd:"progressbar",wd:"radio",xd:"radiogroup",zd:"region",Cd:"row",Dd:"rowgroup",Ed:"rowheader",Fd:"scrollbar",Gd:"search",Id:"separator",Kd:"slider",Md:"spinbutton",Nd:"status",Od:"tab",Pd:"tablist",Qd:"tabpanel",Rd:"textbox",Sd:"timer",Td:"toolbar",Ud:"tooltip",Vd:"tree",Wd:"treegrid",Xd:"treeitem"};var rc=function(a,b){b?(r(Ia(qc,b),"No such ARIA role "+b),a.setAttribute("role",b)):a.removeAttribute("role")},tc=function(a,b,c){ea(c)&&(c=c.join(" "));var d=sc(b);""===c||void 0==c?(oc||(oc={atomic:!1,autocomplete:"none",dropeffect:"none",haspopup:!1,live:"off",multiline:!1,multiselectable:!1,orientation:"vertical",readonly:!1,relevant:"additions text",required:!1,sort:"none",busy:!1,disabled:!1,hidden:!1,invalid:"false"}),c=oc,b in c?a.setAttribute(d,c[b]):a.removeAttribute(d)):a.setAttribute(d,
+c)},sc=function(a){r(a,"ARIA attribute cannot be empty.");r(Ia(pc,a),"No such ARIA attribute "+a);return"aria-"+a};var vc=function(a,b,c,d,e){if(!(v||x&&z("525")))return!0;if(y&&e)return uc(a);if(e&&!d||!c&&(17==b||18==b||y&&91==b))return!1;if(x&&d&&c)switch(a){case 220:case 219:case 221:case 192:case 186:case 189:case 187:case 188:case 190:case 191:case 192:case 222:return!1}if(v&&d&&b==a)return!1;switch(a){case 13:return!(v&&v&&9<=eb);case 27:return!x}return uc(a)},uc=function(a){if(48<=a&&57>=a||96<=a&&106>=a||65<=a&&90>=a||x&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;
default:return!1}},wc=function(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};var P=function(a,b){L.call(this);a&&xc(this,a,b)};p(P,L);f=P.prototype;f.c=null;f.Ea=null;f.Wa=null;f.Fa=null;f.t=-1;f.O=-1;f.jb=!1;
var yc={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},zc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},Ac=v||x&&z("525"),Bc=y&&w;
P.prototype.Qb=function(a){x&&(17==this.t&&!a.ctrlKey||18==this.t&&!a.altKey||y&&91==this.t&&!a.metaKey)&&(this.O=this.t=-1);-1==this.t&&(a.ctrlKey&&17!=a.keyCode?this.t=17:a.altKey&&18!=a.keyCode?this.t=18:a.metaKey&&91!=a.keyCode&&(this.t=91));Ac&&!vc(a.keyCode,this.t,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.O=w?wc(a.keyCode):a.keyCode,Bc&&(this.jb=a.altKey))};P.prototype.Rb=function(a){this.O=this.t=-1;this.jb=a.altKey};
-P.prototype.handleEvent=function(a){var b=a.Q,c,d,e=b.altKey;v&&"keypress"==a.type?(c=this.O,d=13!=c&&27!=c?b.keyCode:0):x&&"keypress"==a.type?(c=this.O,d=0<=b.charCode&&63232>b.charCode&&uc(c)?b.charCode:0):Sa?(c=this.O,d=uc(c)?b.keyCode:0):(c=b.keyCode||this.O,d=b.charCode||0,Bc&&(e=this.jb),y&&63==d&&224==c&&(c=191));var g=c,h=b.keyIdentifier;c?63232<=c&&c in yc?g=yc[c]:25==c&&a.shiftKey&&(g=9):h&&h in zc&&(g=zc[h]);a=g==this.t;this.t=g;b=new Cc(g,d,a,b);b.altKey=e;this.dispatchEvent(b)};
-P.prototype.a=function(){return this.c};var xc=function(a,b,c){a.Fa&&a.detach();a.c=b;a.Ea=H(a.c,"keypress",a,c);a.Wa=H(a.c,"keydown",a.Qb,c,a);a.Fa=H(a.c,"keyup",a.Rb,c,a)};P.prototype.detach=function(){this.Ea&&(J(this.Ea),J(this.Wa),J(this.Fa),this.Fa=this.Wa=this.Ea=null);this.c=null;this.O=this.t=-1};var Cc=function(a,b,c,d){d&&Fb(this,d,void 0);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};p(Cc,E);var Ec=function(a,b){if(!a)throw Error("Invalid class name "+a);if(!n(b))throw Error("Invalid decorator function "+b);Dc[a]=b},Fc={},Dc={};var Q=function(){};ba(Q);Q.prototype.V=function(){};var Gc=function(a,b){a&&(a.tabIndex=b?0:-1)};f=Q.prototype;f.r=function(a){return a.kb().r("div",this.ta(a).join(" "))};f.C=function(a){return a};f.Z=function(a){return"DIV"==a.tagName};f.L=function(a,b){b.id&&hc(a,b.id);var c=this.A(),d=!1,e=B(b);e&&za(e,function(b){b==c?d=!0:b&&this.ab(a,b,c)},this);d||C(b,c);Hc(a,this.C(b));return b};f.ab=function(a,b,c){b==c+"-disabled"?a.pa(!1):b==c+"-horizontal"?Ic(a,"horizontal"):b==c+"-vertical"&&Ic(a,"vertical")};
-var Hc=function(a,b){if(b)for(var c=b.firstChild,d;c&&c.parentNode==b;){d=c.nextSibling;if(1==c.nodeType){var e;t:{e=void 0;for(var g=B(c),h=0,k=g.length;h<k;h++)if(e=g[h]in Dc?Dc[g[h]]():null)break t;e=null}e&&(e.c=c,a.isEnabled()||e.pa(!1),a.Ca(e),e.L(c))}else c.nodeValue&&""!=oa(c.nodeValue)||b.removeChild(c);c=d}};Q.prototype.Ma=function(a){a=a.a();r(a,"The container DOM element cannot be null.");cc(a,!0,w);v&&(a.hideFocus=!0);var b=this.V();b&&rc(a,b)};Q.prototype.k=function(a){return a.a()};
+P.prototype.handleEvent=function(a){var b=a.Q,c,d,e=b.altKey;v&&"keypress"==a.type?(c=this.O,d=13!=c&&27!=c?b.keyCode:0):x&&"keypress"==a.type?(c=this.O,d=0<=b.charCode&&63232>b.charCode&&uc(c)?b.charCode:0):Ta?(c=this.O,d=uc(c)?b.keyCode:0):(c=b.keyCode||this.O,d=b.charCode||0,Bc&&(e=this.jb),y&&63==d&&224==c&&(c=191));var g=c,h=b.keyIdentifier;c?63232<=c&&c in yc?g=yc[c]:25==c&&a.shiftKey&&(g=9):h&&h in zc&&(g=zc[h]);a=g==this.t;this.t=g;b=new Cc(g,d,a,b);b.altKey=e;this.dispatchEvent(b)};
+P.prototype.a=function(){return this.c};var xc=function(a,b,c){a.Fa&&a.detach();a.c=b;a.Ea=H(a.c,"keypress",a,c);a.Wa=H(a.c,"keydown",a.Qb,c,a);a.Fa=H(a.c,"keyup",a.Rb,c,a)};P.prototype.detach=function(){this.Ea&&(J(this.Ea),J(this.Wa),J(this.Fa),this.Fa=this.Wa=this.Ea=null);this.c=null;this.O=this.t=-1};var Cc=function(a,b,c,d){d&&Fb(this,d,void 0);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c};p(Cc,E);var Ec=function(a,b){if(!a)throw Error("Invalid class name "+a);if(!n(b))throw Error("Invalid decorator function "+b);Dc[a]=b},Fc={},Dc={};var Q=function(){};ba(Q);Q.prototype.V=function(){};var Gc=function(a,b){a&&(a.tabIndex=b?0:-1)};f=Q.prototype;f.r=function(a){return a.kb().r("div",this.ta(a).join(" "))};f.C=function(a){return a};f.Z=function(a){return"DIV"==a.tagName};f.L=function(a,b){b.id&&hc(a,b.id);var c=this.A(),d=!1,e=B(b);e&&Aa(e,function(b){b==c?d=!0:b&&this.ab(a,b,c)},this);d||C(b,c);Hc(a,this.C(b));return b};f.ab=function(a,b,c){b==c+"-disabled"?a.pa(!1):b==c+"-horizontal"?Ic(a,"horizontal"):b==c+"-vertical"&&Ic(a,"vertical")};
+var Hc=function(a,b){if(b)for(var c=b.firstChild,d;c&&c.parentNode==b;){d=c.nextSibling;if(1==c.nodeType){var e;t:{e=void 0;for(var g=B(c),h=0,k=g.length;h<k;h++)if(e=g[h]in Dc?Dc[g[h]]():null)break t;e=null}e&&(e.c=c,a.isEnabled()||e.pa(!1),a.Ca(e),e.L(c))}else c.nodeValue&&""!=pa(c.nodeValue)||b.removeChild(c);c=d}};Q.prototype.Ma=function(a){a=a.a();r(a,"The container DOM element cannot be null.");cc(a,!0,w);v&&(a.hideFocus=!0);var b=this.V();b&&rc(a,b)};Q.prototype.k=function(a){return a.a()};
Q.prototype.A=function(){return"goog-container"};Q.prototype.ta=function(a){var b=this.A(),c=[b,"horizontal"==a.M?b+"-horizontal":b+"-vertical"];a.isEnabled()||c.push(b+"-disabled");return c};var R=function(){},Jc;ba(R);f=R.prototype;f.V=function(){};f.r=function(a){var b=a.kb().r("div",this.ta(a).join(" "),a.Ba);Kc(a,b);return b};f.C=function(a){return a};f.ra=function(a,b,c){if(a=a.a?a.a():a)if(v&&!z("7")){var d=Lc(B(a),b);d.push(b);ja(c?C:ib,a).apply(null,d)}else c?C(a,b):ib(a,b)};f.Z=function(){return!0};
-f.L=function(a,b){b.id&&hc(a,b.id);var c=this.C(b);c&&c.firstChild?Mc(a,c.firstChild.nextSibling?Da(c.childNodes):c.firstChild):a.Ba=null;var d=0,e=this.A(),g=this.A(),h=!1,k=!1,c=!1,q=B(b);za(q,function(a){if(h||a!=e)if(k||a!=g){var b=d;this.tb||(this.Ha||Nc(this),this.tb=Ja(this.Ha));a=parseInt(this.tb[a],10);d=b|(isNaN(a)?0:a)}else k=!0;else h=!0,g==e&&(k=!0)},this);a.g=d;h||(q.push(e),g==e&&(k=!0));k||q.push(g);var ka=a.H;ka&&q.push.apply(q,ka);if(v&&!z("7")){var A=Lc(q);0<A.length&&(q.push.apply(q,
-A),c=!0)}if(!h||!k||ka||c)b.className=q.join(" ");Kc(a,b);return b};f.Ma=function(a){mc(a)&&this.qa(a.a(),!0);a.isEnabled()&&this.na(a,a.u())};var Oc=function(a,b,c){if(a=c||a.V())r(b,"The element passed as a first parameter cannot be null."),rc(b,a)},Kc=function(a,b){r(a);r(b);a.u()||tc(b,"hidden",!a.u());a.isEnabled()||Pc(b,1,!a.isEnabled());a.m&8&&Pc(b,8,!!(a.g&8));a.m&16&&Pc(b,16,!!(a.g&16));a.m&64&&Pc(b,64,!!(a.g&64))};f=R.prototype;f.za=function(a,b){cc(a,!b,!v&&!Sa)};
+f.L=function(a,b){b.id&&hc(a,b.id);var c=this.C(b);c&&c.firstChild?Mc(a,c.firstChild.nextSibling?Ea(c.childNodes):c.firstChild):a.Ba=null;var d=0,e=this.A(),g=this.A(),h=!1,k=!1,c=!1,q=B(b);Aa(q,function(a){if(h||a!=e)if(k||a!=g){var b=d;this.tb||(this.Ha||Nc(this),this.tb=Ka(this.Ha));a=parseInt(this.tb[a],10);d=b|(isNaN(a)?0:a)}else k=!0;else h=!0,g==e&&(k=!0)},this);a.g=d;h||(q.push(e),g==e&&(k=!0));k||q.push(g);var ka=a.H;ka&&q.push.apply(q,ka);if(v&&!z("7")){var A=Lc(q);0<A.length&&(q.push.apply(q,
+A),c=!0)}if(!h||!k||ka||c)b.className=q.join(" ");Kc(a,b);return b};f.Ma=function(a){mc(a)&&this.qa(a.a(),!0);a.isEnabled()&&this.na(a,a.u())};var Oc=function(a,b,c){if(a=c||a.V())r(b,"The element passed as a first parameter cannot be null."),rc(b,a)},Kc=function(a,b){r(a);r(b);a.u()||tc(b,"hidden",!a.u());a.isEnabled()||Pc(b,1,!a.isEnabled());a.m&8&&Pc(b,8,!!(a.g&8));a.m&16&&Pc(b,16,!!(a.g&16));a.m&64&&Pc(b,64,!!(a.g&64))};f=R.prototype;f.za=function(a,b){cc(a,!b,!v&&!Ta)};
f.qa=function(a,b){this.ra(a,this.A()+"-rtl",b)};f.J=function(a){var b;return a.m&32&&(b=a.k())?xb(b)&&yb(b):!1};f.na=function(a,b){var c;if(a.m&32&&(c=a.k())){if(!b&&a.g&32){try{c.blur()}catch(d){}a.g&32&&a.la(null)}(xb(c)&&yb(c))!=b&&(b?c.tabIndex=0:(c.tabIndex=-1,c.removeAttribute("tabIndex")))}};f.ja=function(a,b){M(a,b);a&&tc(a,"hidden",!b)};f.v=function(a,b,c){var d=a.a();if(d){var e=Qc(this,b);e&&this.ra(a,e,c);Pc(d,b,c)}};
var Pc=function(a,b,c){Jc||(Jc={1:"disabled",8:"selected",16:"checked",64:"expanded"});if(b=Jc[b])r(a,"The element passed as a first parameter cannot be null."),tc(a,b,c)};R.prototype.k=function(a){return a.a()};R.prototype.A=function(){return"goog-control"};R.prototype.ta=function(a){var b=this.A(),c=[b],d=this.A();d!=b&&c.push(d);b=a.g;for(d=[];b;){var e=b&-b;d.push(Qc(this,e));b&=~e}c.push.apply(c,d);(a=a.H)&&c.push.apply(c,a);v&&!z("7")&&c.push.apply(c,Lc(c));return c};
-var Lc=function(a,b){var c=[];b&&(a=a.concat([b]));za([],function(d){!Ba(d,ja(t,a))||b&&!t(d,b)||c.push(d.join("_"))});return c},Qc=function(a,b){a.Ha||Nc(a);return a.Ha[b]},Nc=function(a){var b=a.A();a.Ha={1:b+"-disabled",2:b+"-hover",4:b+"-active",8:b+"-selected",16:b+"-checked",32:b+"-focused",64:b+"-open"}};var S=function(a,b,c){N.call(this,c);if(!b){b=this.constructor;for(var d;b;){d=ia(b);if(d=Fc[d])break;b=b.f?b.f.constructor:null}b=d?n(d.aa)?d.aa():new d:null}this.b=b;this.Ba=void 0!==a?a:null};p(S,N);f=S.prototype;f.Ba=null;f.g=0;f.m=39;f.cc=255;f.W=0;f.q=!0;f.H=null;f.ca=!0;f.xa=!1;f.qb=null;f.pb=function(){return this.ca};f.Na=function(a){this.e&&a!=this.ca&&Rc(this,a);this.ca=a};f.k=function(){return this.b.k(this)};f.ya=function(){return this.ga||(this.ga=new P)};f.zb=function(){return this.b};
-f.ra=function(a,b){b?a&&(this.H?t(this.H,a)||this.H.push(a):this.H=[a],this.b.ra(this,a,!0)):a&&this.H&&Ca(this.H,a)&&(0==this.H.length&&(this.H=null),this.b.ra(this,a,!1))};f.r=function(){var a=this.b.r(this);this.c=a;Oc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.u()||this.b.ja(a,!1)};f.C=function(){return this.b.C(this.a())};f.Z=function(a){return this.b.Z(a)};f.Xa=function(a){this.c=a=this.b.L(this,a);Oc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.q="none"!=a.style.display};
+var Lc=function(a,b){var c=[];b&&(a=a.concat([b]));Aa([],function(d){!Ca(d,ja(t,a))||b&&!t(d,b)||c.push(d.join("_"))});return c},Qc=function(a,b){a.Ha||Nc(a);return a.Ha[b]},Nc=function(a){var b=a.A();a.Ha={1:b+"-disabled",2:b+"-hover",4:b+"-active",8:b+"-selected",16:b+"-checked",32:b+"-focused",64:b+"-open"}};var S=function(a,b,c){N.call(this,c);if(!b){b=this.constructor;for(var d;b;){d=ia(b);if(d=Fc[d])break;b=b.f?b.f.constructor:null}b=d?n(d.aa)?d.aa():new d:null}this.b=b;this.Ba=void 0!==a?a:null};p(S,N);f=S.prototype;f.Ba=null;f.g=0;f.m=39;f.cc=255;f.W=0;f.q=!0;f.H=null;f.ca=!0;f.xa=!1;f.qb=null;f.pb=function(){return this.ca};f.Na=function(a){this.e&&a!=this.ca&&Rc(this,a);this.ca=a};f.k=function(){return this.b.k(this)};f.ya=function(){return this.ga||(this.ga=new P)};f.zb=function(){return this.b};
+f.ra=function(a,b){b?a&&(this.H?t(this.H,a)||this.H.push(a):this.H=[a],this.b.ra(this,a,!0)):a&&this.H&&Da(this.H,a)&&(0==this.H.length&&(this.H=null),this.b.ra(this,a,!1))};f.r=function(){var a=this.b.r(this);this.c=a;Oc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.u()||this.b.ja(a,!1)};f.C=function(){return this.b.C(this.a())};f.Z=function(a){return this.b.Z(a)};f.Xa=function(a){this.c=a=this.b.L(this,a);Oc(this.b,a,this.qb);this.xa||this.b.za(a,!1);this.q="none"!=a.style.display};
f.G=function(){S.f.G.call(this);this.b.Ma(this);if(this.m&-2&&(this.pb()&&Rc(this,!0),this.m&32)){var a=this.k();if(a){var b=this.ya();xc(b,a);ic(this).d(b,"key",this.K).d(a,"focus",this.ma).d(a,"blur",this.la)}}};
var Rc=function(a,b){var c=ic(a),d=a.a();b?(c.d(d,"mouseover",a.Qa).d(d,"mousedown",a.ka).d(d,"mouseup",a.Ra).d(d,"mouseout",a.Pa),a.oa!=aa&&c.d(d,"contextmenu",a.oa),v&&c.d(d,"dblclick",a.sb)):(c.w(d,"mouseover",a.Qa).w(d,"mousedown",a.ka).w(d,"mouseup",a.Ra).w(d,"mouseout",a.Pa),a.oa!=aa&&c.w(d,"contextmenu",a.oa),v&&c.w(d,"dblclick",a.sb))};S.prototype.da=function(){S.f.da.call(this);this.ga&&this.ga.detach();this.u()&&this.isEnabled()&&this.b.na(this,!1)};var Mc=function(a,b){a.Ba=b};f=S.prototype;
f.qa=function(a){S.f.qa.call(this,a);var b=this.a();b&&this.b.qa(b,a)};f.za=function(a){this.xa=a;var b=this.a();b&&this.b.za(b,a)};f.u=function(){return this.q};f.ja=function(a,b){if(b||this.q!=a&&this.dispatchEvent(a?"show":"hide")){var c=this.a();c&&this.b.ja(c,a);this.isEnabled()&&this.b.na(this,a);this.q=a;return!0}return!1};f.isEnabled=function(){return!(this.g&1)};
@@ -54,21 +54,21 @@
var Uc=function(a,b,c){if(a.e&&a.g&b&&!c)throw Error("Component already rendered");!c&&a.g&b&&a.v(b,!1);a.m=c?a.m|b:a.m&~b},U=function(a,b){return!!(a.cc&b)&&!!(a.m&b)},T=function(a,b,c){return!!(a.m&b)&&!!(a.g&b)!=c&&(!(a.W&b)||a.dispatchEvent(fc(b,c)))&&!a.Sb};f=S.prototype;f.Qa=function(a){(!a.relatedTarget||!tb(this.a(),a.relatedTarget))&&this.dispatchEvent("enter")&&this.isEnabled()&&U(this,2)&&this.D(!0)};
f.Pa=function(a){a.relatedTarget&&tb(this.a(),a.relatedTarget)||!this.dispatchEvent("leave")||(U(this,4)&&this.setActive(!1),U(this,2)&&this.D(!1))};f.oa=aa;f.ka=function(a){this.isEnabled()&&(U(this,2)&&this.D(!0),!Hb(a)||x&&y&&a.ctrlKey||(U(this,4)&&this.setActive(!0),this.b.J(this)&&this.k().focus()));this.xa||!Hb(a)||x&&y&&a.ctrlKey||a.preventDefault()};f.Ra=function(a){this.isEnabled()&&(U(this,2)&&this.D(!0),this.g&4&&Vc(this,a)&&U(this,4)&&this.setActive(!1))};
f.sb=function(a){this.isEnabled()&&Vc(this,a)};var Vc=function(a,b){if(U(a,16)){var c=!(a.g&16);T(a,16,c)&&a.v(16,c)}U(a,8)&&Sc(a,!0);U(a,64)&&Tc(a,!(a.g&64));c=new D("action",a);b&&(c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey,c.bb=b.bb);return a.dispatchEvent(c)};S.prototype.ma=function(){U(this,32)&&T(this,32,!0)&&this.v(32,!0)};S.prototype.la=function(){U(this,4)&&this.setActive(!1);U(this,32)&&T(this,32,!1)&&this.v(32,!1)};
-S.prototype.K=function(a){return this.u()&&this.isEnabled()&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};S.prototype.lb=function(a){return 13==a.keyCode&&Vc(this,a)};if(!n(S))throw Error("Invalid component class "+S);if(!n(R))throw Error("Invalid renderer class "+R);var Wc=ia(S);Fc[Wc]=R;Ec("goog-control",function(){return new S(null)});var V=function(a,b,c){N.call(this,c);this.b=b||Q.aa();this.M=a||"vertical"};p(V,N);f=V.prototype;f.ub=null;f.ga=null;f.b=null;f.M=null;f.q=!0;f.X=!0;f.Za=!0;f.j=-1;f.h=null;f.ea=!1;f.Pb=!1;f.Ob=!0;f.N=null;f.k=function(){return this.ub||this.b.k(this)};f.ya=function(){return this.ga||(this.ga=new P(this.k()))};f.zb=function(){return this.b};f.r=function(){this.c=this.b.r(this)};f.C=function(){return this.b.C(this.a())};f.Z=function(a){return this.b.Z(a)};
+S.prototype.K=function(a){return this.u()&&this.isEnabled()&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};S.prototype.lb=function(a){return 13==a.keyCode&&Vc(this,a)};if(!n(S))throw Error("Invalid component class "+S);if(!n(R))throw Error("Invalid renderer class "+R);var Wc=ia(S);Fc[Wc]=R;Ec("goog-control",function(){return new S(null)});var V=function(a,b,c){N.call(this,c);this.b=b||Q.aa();this.M=a||"vertical"};p(V,N);f=V.prototype;f.ub=null;f.ga=null;f.b=null;f.M=null;f.q=!0;f.X=!0;f.Za=!0;f.h=-1;f.i=null;f.ea=!1;f.Pb=!1;f.Ob=!0;f.N=null;f.k=function(){return this.ub||this.b.k(this)};f.ya=function(){return this.ga||(this.ga=new P(this.k()))};f.zb=function(){return this.b};f.r=function(){this.c=this.b.r(this)};f.C=function(){return this.b.C(this.a())};f.Z=function(a){return this.b.Z(a)};
f.Xa=function(a){this.c=this.b.L(this,a);"none"==a.style.display&&(this.q=!1)};f.G=function(){V.f.G.call(this);kc(this,function(a){a.e&&Xc(this,a)},this);var a=this.a();this.b.Ma(this);this.ja(this.q,!0);ic(this).d(this,"enter",this.Ib).d(this,"highlight",this.Jb).d(this,"unhighlight",this.Lb).d(this,"open",this.Kb).d(this,"close",this.Gb).d(a,"mousedown",this.ka).d(kb(a),"mouseup",this.Hb).d(a,["mousedown","mouseup","mouseover","mouseout","contextmenu"],this.Fb);this.J()&&Yc(this,!0)};
-var Yc=function(a,b){var c=ic(a),d=a.k();b?c.d(d,"focus",a.ma).d(d,"blur",a.la).d(a.ya(),"key",a.K):c.w(d,"focus",a.ma).w(d,"blur",a.la).w(a.ya(),"key",a.K)};f=V.prototype;f.da=function(){Zc(this,-1);this.h&&Tc(this.h,!1);this.ea=!1;V.f.da.call(this)};f.Ib=function(){return!0};
-f.Jb=function(a){var b=nc(this,a.target);if(-1<b&&b!=this.j){var c=O(this,this.j);c&&c.D(!1);this.j=b;c=O(this,this.j);this.ea&&c.setActive(!0);this.Ob&&this.h&&c!=this.h&&(c.m&64?Tc(c,!0):Tc(this.h,!1))}b=this.a();r(b,"The DOM element for the container cannot be null.");null!=a.target.a()&&tc(b,"activedescendant",a.target.a().id)};f.Lb=function(a){a.target==O(this,this.j)&&(this.j=-1);a=this.a();r(a,"The DOM element for the container cannot be null.");a.removeAttribute(sc("activedescendant"))};
-f.Kb=function(a){(a=a.target)&&a!=this.h&&a.getParent()==this&&(this.h&&Tc(this.h,!1),this.h=a)};f.Gb=function(a){a.target==this.h&&(this.h=null)};f.ka=function(a){this.X&&(this.ea=!0);var b=this.k();b&&xb(b)&&yb(b)?b.focus():a.preventDefault()};f.Hb=function(){this.ea=!1};
-f.Fb=function(a){var b;t:{b=a.target;if(this.N)for(var c=this.a();b&&b!==c;){var d=b.id;if(d in this.N){b=this.N[d];break t}b=b.parentNode}b=null}if(b)switch(a.type){case "mousedown":b.ka(a);break;case "mouseup":b.Ra(a);break;case "mouseover":b.Qa(a);break;case "mouseout":b.Pa(a);break;case "contextmenu":b.oa(a)}};f.ma=function(){};f.la=function(){Zc(this,-1);this.ea=!1;this.h&&Tc(this.h,!1)};
+var Yc=function(a,b){var c=ic(a),d=a.k();b?c.d(d,"focus",a.ma).d(d,"blur",a.la).d(a.ya(),"key",a.K):c.w(d,"focus",a.ma).w(d,"blur",a.la).w(a.ya(),"key",a.K)};f=V.prototype;f.da=function(){Zc(this,-1);this.i&&Tc(this.i,!1);this.ea=!1;V.f.da.call(this)};f.Ib=function(){return!0};
+f.Jb=function(a){var b=nc(this,a.target);if(-1<b&&b!=this.h){var c=O(this,this.h);c&&c.D(!1);this.h=b;c=O(this,this.h);this.ea&&c.setActive(!0);this.Ob&&this.i&&c!=this.i&&(c.m&64?Tc(c,!0):Tc(this.i,!1))}b=this.a();r(b,"The DOM element for the container cannot be null.");null!=a.target.a()&&tc(b,"activedescendant",a.target.a().id)};f.Lb=function(a){a.target==O(this,this.h)&&(this.h=-1);a=this.a();r(a,"The DOM element for the container cannot be null.");a.removeAttribute(sc("activedescendant"))};
+f.Kb=function(a){(a=a.target)&&a!=this.i&&a.getParent()==this&&(this.i&&Tc(this.i,!1),this.i=a)};f.Gb=function(a){a.target==this.i&&(this.i=null)};f.ka=function(a){this.X&&(this.ea=!0);var b=this.k();b&&xb(b)&&yb(b)?b.focus():a.preventDefault()};f.Hb=function(){this.ea=!1};
+f.Fb=function(a){var b;t:{b=a.target;if(this.N)for(var c=this.a();b&&b!==c;){var d=b.id;if(d in this.N){b=this.N[d];break t}b=b.parentNode}b=null}if(b)switch(a.type){case "mousedown":b.ka(a);break;case "mouseup":b.Ra(a);break;case "mouseover":b.Qa(a);break;case "mouseout":b.Pa(a);break;case "contextmenu":b.oa(a)}};f.ma=function(){};f.la=function(){Zc(this,-1);this.ea=!1;this.i&&Tc(this.i,!1)};
f.K=function(a){return this.isEnabled()&&this.u()&&(0!=lc(this)||this.ub)&&this.lb(a)?(a.preventDefault(),a.stopPropagation(),!0):!1};
-f.lb=function(a){var b=O(this,this.j);if(b&&"function"==typeof b.K&&b.K(a)||this.h&&this.h!=b&&"function"==typeof this.h.K&&this.h.K(a))return!0;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return!1;switch(a.keyCode){case 27:if(this.J())this.k().blur();else return!1;break;case 36:$c(this);break;case 35:ad(this);break;case 38:if("vertical"==this.M)bd(this);else return!1;break;case 37:if("horizontal"==this.M)mc(this)?cd(this):bd(this);else return!1;break;case 40:if("vertical"==this.M)cd(this);else return!1;
-break;case 39:if("horizontal"==this.M)mc(this)?bd(this):cd(this);else return!1;break;default:return!1}return!0};var Xc=function(a,b){var c=b.a(),c=c.id||(c.id=gc(b));a.N||(a.N={});a.N[c]=b};V.prototype.Ca=function(a,b){xa(a,S,"The child of a container must be a control");V.f.Ca.call(this,a,b)};V.prototype.Ta=function(a,b,c){a.W|=2;a.W|=64;!this.J()&&this.Pb||Uc(a,32,!1);a.Na(!1);V.f.Ta.call(this,a,b,c);a.e&&this.e&&Xc(this,a);b<=this.j&&this.j++};
-V.prototype.removeChild=function(a,b){if(a=m(a)?this.i&&a?(a in this.i?this.i[a]:void 0)||null:null:a){var c=nc(this,a);-1!=c&&(c==this.j?a.D(!1):c<this.j&&this.j--);var d=a.a();d&&d.id&&this.N&&(c=this.N,d=d.id,d in c&&delete c[d])}a=V.f.removeChild.call(this,a,b);a.Na(!0);return a};var Ic=function(a,b){if(a.a())throw Error("Component already rendered");a.M=b};f=V.prototype;f.u=function(){return this.q};
+f.lb=function(a){var b=O(this,this.h);if(b&&"function"==typeof b.K&&b.K(a)||this.i&&this.i!=b&&"function"==typeof this.i.K&&this.i.K(a))return!0;if(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)return!1;switch(a.keyCode){case 27:if(this.J())this.k().blur();else return!1;break;case 36:$c(this);break;case 35:ad(this);break;case 38:if("vertical"==this.M)bd(this);else return!1;break;case 37:if("horizontal"==this.M)mc(this)?cd(this):bd(this);else return!1;break;case 40:if("vertical"==this.M)cd(this);else return!1;
+break;case 39:if("horizontal"==this.M)mc(this)?bd(this):cd(this);else return!1;break;default:return!1}return!0};var Xc=function(a,b){var c=b.a(),c=c.id||(c.id=gc(b));a.N||(a.N={});a.N[c]=b};V.prototype.Ca=function(a,b){ya(a,S,"The child of a container must be a control");V.f.Ca.call(this,a,b)};V.prototype.Ta=function(a,b,c){a.W|=2;a.W|=64;!this.J()&&this.Pb||Uc(a,32,!1);a.Na(!1);V.f.Ta.call(this,a,b,c);a.e&&this.e&&Xc(this,a);b<=this.h&&this.h++};
+V.prototype.removeChild=function(a,b){if(a=m(a)?this.j&&a?(a in this.j?this.j[a]:void 0)||null:null:a){var c=nc(this,a);-1!=c&&(c==this.h?(a.D(!1),this.h=-1):c<this.h&&this.h--);var d=a.a();d&&d.id&&this.N&&(c=this.N,d=d.id,d in c&&delete c[d])}a=V.f.removeChild.call(this,a,b);a.Na(!0);return a};var Ic=function(a,b){if(a.a())throw Error("Component already rendered");a.M=b};f=V.prototype;f.u=function(){return this.q};
f.ja=function(a,b){if(b||this.q!=a&&this.dispatchEvent(a?"show":"hide")){this.q=a;var c=this.a();c&&(M(c,a),this.J()&&Gc(this.k(),this.X&&this.q),b||this.dispatchEvent(this.q?"aftershow":"afterhide"));return!0}return!1};f.isEnabled=function(){return this.X};f.pa=function(a){this.X!=a&&this.dispatchEvent(a?"enable":"disable")&&(a?(this.X=!0,kc(this,function(a){a.vb?delete a.vb:a.pa(!0)})):(kc(this,function(a){a.isEnabled()?a.pa(!1):a.vb=!0}),this.ea=this.X=!1),this.J()&&Gc(this.k(),a&&this.q))};
-f.J=function(){return this.Za};f.na=function(a){a!=this.Za&&this.e&&Yc(this,a);this.Za=a;this.X&&this.q&&Gc(this.k(),a)};var Zc=function(a,b){var c=O(a,b);c?c.D(!0):-1<a.j&&O(a,a.j).D(!1)};V.prototype.D=function(a){Zc(this,nc(this,a))};
-var $c=function(a){dd(a,function(a,c){return(a+1)%c},lc(a)-1)},ad=function(a){dd(a,function(a,c){a--;return 0>a?c-1:a},0)},cd=function(a){dd(a,function(a,c){return(a+1)%c},a.j)},bd=function(a){dd(a,function(a,c){a--;return 0>a?c-1:a},a.j)},dd=function(a,b,c){c=0>c?nc(a,a.h):c;var d=lc(a);c=b.call(a,c,d);for(var e=0;e<=d;){var g=O(a,c);if(g&&g.u()&&g.isEnabled()&&g.m&2){a.Ua(c);break}e++;c=b.call(a,c,d)}};V.prototype.Ua=function(a){Zc(this,a)};var ed=function(){};p(ed,R);ba(ed);f=ed.prototype;f.A=function(){return"goog-tab"};f.V=function(){return"tab"};f.r=function(a){var b=ed.f.r.call(this,a);(a=a.Sa())&&this.Va(b,a);return b};f.L=function(a,b){b=ed.f.L.call(this,a,b);var c=this.Sa(b);c&&(a.rb=c);a.g&8&&(c=a.getParent())&&n(c.Y)&&(a.v(8,!1),c.Y(a));return b};f.Sa=function(a){return a.title||""};f.Va=function(a,b){a&&(a.title=b||"")};var fd=function(a,b,c){S.call(this,a,b||ed.aa(),c);Uc(this,8,!0);this.W|=9};p(fd,S);fd.prototype.Sa=function(){return this.rb};fd.prototype.Va=function(a){this.zb().Va(this.a(),a);this.rb=a};Ec("goog-tab",function(){return new fd(null)});var W=function(){};p(W,Q);ba(W);W.prototype.A=function(){return"goog-tab-bar"};W.prototype.V=function(){return"tablist"};W.prototype.ab=function(a,b,c){this.Ab||(this.Ja||gd(this),this.Ab=Ja(this.Ja));var d=this.Ab[b];d?(Ic(a,hd(d)),a.wb=d):W.f.ab.call(this,a,b,c)};W.prototype.ta=function(a){var b=W.f.ta.call(this,a);this.Ja||gd(this);b.push(this.Ja[a.wb]);return b};var gd=function(a){var b=a.A();a.Ja={top:b+"-top",bottom:b+"-bottom",start:b+"-start",end:b+"-end"}};var X=function(a,b,c){a=a||"top";Ic(this,hd(a));this.wb=a;V.call(this,this.M,b||W.aa(),c);id(this)};p(X,V);f=X.prototype;f.Zb=!0;f.I=null;f.G=function(){X.f.G.call(this);id(this)};f.removeChild=function(a,b){jd(this,a);return X.f.removeChild.call(this,a,b)};f.Ua=function(a){X.f.Ua.call(this,a);this.Zb&&this.Y(O(this,a))};f.Y=function(a){a?Sc(a,!0):this.I&&Sc(this.I,!1)};
-var jd=function(a,b){if(b&&b==a.I){for(var c=nc(a,b),d=c-1;b=O(a,d);d--)if(b.u()&&b.isEnabled()){a.Y(b);return}for(c+=1;b=O(a,c);c++)if(b.u()&&b.isEnabled()){a.Y(b);return}a.Y(null)}};f=X.prototype;f.Xb=function(a){this.I&&this.I!=a.target&&Sc(this.I,!1);this.I=a.target};f.Yb=function(a){a.target==this.I&&(this.I=null)};f.Vb=function(a){jd(this,a.target)};f.Wb=function(a){jd(this,a.target)};f.ma=function(){O(this,this.j)||this.D(this.I||O(this,0))};
-var id=function(a){ic(a).d(a,"select",a.Xb).d(a,"unselect",a.Yb).d(a,"disable",a.Vb).d(a,"hide",a.Wb)},hd=function(a){return"start"==a||"end"==a?"vertical":"horizontal"};Ec("goog-tab-bar",function(){return new X});var kd=!!l.DOMTokenList,ld=kd?function(a){return a.classList}:function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},md=kd?function(a,b){r(!!a.classList);return a.classList.contains(b)}:function(a,b){return t(ld(a),b)},nd=kd?function(a,b){a.classList.add(b)}:function(a,b){md(a,b)||(a.className+=0<a.className.length?" "+b:b)},od=kd?function(a,b){a.classList.remove(b)}:function(a,b){md(a,b)&&(a.className=Aa(ld(a),function(a){return a!=b}).join(" "))};var Y=function(a,b,c,d,e){function g(a){a&&(a.tabIndex=0,rc(a,h.V()),nd(a,"goog-zippy-header"),pd(h,a),a&&h.Mb.d(a,"keydown",h.Nb))}L.call(this);this.n=e||lb();this.T=this.n.a(a)||null;this.Aa=this.n.a(d||null);this.fa=(this.Oa=n(b)?b:null)||!b?null:this.n.a(b);this.l=!0==c;this.Mb=new K(this);this.ob=new K(this);var h=this;g(this.T);g(this.Aa);this.U(this.l)};p(Y,L);f=Y.prototype;f.ca=!0;f.V=function(){return"tab"};f.C=function(){return this.fa};f.toggle=function(){this.U(!this.l)};
+f.J=function(){return this.Za};f.na=function(a){a!=this.Za&&this.e&&Yc(this,a);this.Za=a;this.X&&this.q&&Gc(this.k(),a)};var Zc=function(a,b){var c=O(a,b);c?c.D(!0):-1<a.h&&O(a,a.h).D(!1)};V.prototype.D=function(a){Zc(this,nc(this,a))};
+var $c=function(a){dd(a,function(a,c){return(a+1)%c},lc(a)-1)},ad=function(a){dd(a,function(a,c){a--;return 0>a?c-1:a},0)},cd=function(a){dd(a,function(a,c){return(a+1)%c},a.h)},bd=function(a){dd(a,function(a,c){a--;return 0>a?c-1:a},a.h)},dd=function(a,b,c){c=0>c?nc(a,a.i):c;var d=lc(a);c=b.call(a,c,d);for(var e=0;e<=d;){var g=O(a,c);if(g&&g.u()&&g.isEnabled()&&g.m&2){a.Ua(c);break}e++;c=b.call(a,c,d)}};V.prototype.Ua=function(a){Zc(this,a)};var ed=function(){};p(ed,R);ba(ed);f=ed.prototype;f.A=function(){return"goog-tab"};f.V=function(){return"tab"};f.r=function(a){var b=ed.f.r.call(this,a);(a=a.Sa())&&this.Va(b,a);return b};f.L=function(a,b){b=ed.f.L.call(this,a,b);var c=this.Sa(b);c&&(a.rb=c);a.g&8&&(c=a.getParent())&&n(c.Y)&&(a.v(8,!1),c.Y(a));return b};f.Sa=function(a){return a.title||""};f.Va=function(a,b){a&&(a.title=b||"")};var fd=function(a,b,c){S.call(this,a,b||ed.aa(),c);Uc(this,8,!0);this.W|=9};p(fd,S);fd.prototype.Sa=function(){return this.rb};fd.prototype.Va=function(a){this.zb().Va(this.a(),a);this.rb=a};Ec("goog-tab",function(){return new fd(null)});var W=function(){};p(W,Q);ba(W);W.prototype.A=function(){return"goog-tab-bar"};W.prototype.V=function(){return"tablist"};W.prototype.ab=function(a,b,c){this.Ab||(this.Ja||gd(this),this.Ab=Ka(this.Ja));var d=this.Ab[b];d?(Ic(a,hd(d)),a.wb=d):W.f.ab.call(this,a,b,c)};W.prototype.ta=function(a){var b=W.f.ta.call(this,a);this.Ja||gd(this);b.push(this.Ja[a.wb]);return b};var gd=function(a){var b=a.A();a.Ja={top:b+"-top",bottom:b+"-bottom",start:b+"-start",end:b+"-end"}};var X=function(a,b,c){a=a||"top";Ic(this,hd(a));this.wb=a;V.call(this,this.M,b||W.aa(),c);id(this)};p(X,V);f=X.prototype;f.Zb=!0;f.I=null;f.G=function(){X.f.G.call(this);id(this)};f.removeChild=function(a,b){jd(this,a);return X.f.removeChild.call(this,a,b)};f.Ua=function(a){X.f.Ua.call(this,a);this.Zb&&this.Y(O(this,a))};f.Y=function(a){a?Sc(a,!0):this.I&&Sc(this.I,!1)};
+var jd=function(a,b){if(b&&b==a.I){for(var c=nc(a,b),d=c-1;b=O(a,d);d--)if(b.u()&&b.isEnabled()){a.Y(b);return}for(c+=1;b=O(a,c);c++)if(b.u()&&b.isEnabled()){a.Y(b);return}a.Y(null)}};f=X.prototype;f.Xb=function(a){this.I&&this.I!=a.target&&Sc(this.I,!1);this.I=a.target};f.Yb=function(a){a.target==this.I&&(this.I=null)};f.Vb=function(a){jd(this,a.target)};f.Wb=function(a){jd(this,a.target)};f.ma=function(){O(this,this.h)||this.D(this.I||O(this,0))};
+var id=function(a){ic(a).d(a,"select",a.Xb).d(a,"unselect",a.Yb).d(a,"disable",a.Vb).d(a,"hide",a.Wb)},hd=function(a){return"start"==a||"end"==a?"vertical":"horizontal"};Ec("goog-tab-bar",function(){return new X});var kd=!!l.DOMTokenList,ld=kd?function(a){return a.classList}:function(a){a=a.className;return m(a)&&a.match(/\S+/g)||[]},md=kd?function(a,b){r(!!a.classList);return a.classList.contains(b)}:function(a,b){return t(ld(a),b)},nd=kd?function(a,b){a.classList.add(b)}:function(a,b){md(a,b)||(a.className+=0<a.className.length?" "+b:b)},od=kd?function(a,b){a.classList.remove(b)}:function(a,b){md(a,b)&&(a.className=Ba(ld(a),function(a){return a!=b}).join(" "))};var Y=function(a,b,c,d,e){function g(a){a&&(a.tabIndex=0,rc(a,h.V()),nd(a,"goog-zippy-header"),pd(h,a),a&&h.Mb.d(a,"keydown",h.Nb))}L.call(this);this.n=e||lb();this.T=this.n.a(a)||null;this.Aa=this.n.a(d||null);this.fa=(this.Oa=n(b)?b:null)||!b?null:this.n.a(b);this.l=!0==c;this.Mb=new K(this);this.ob=new K(this);var h=this;g(this.T);g(this.Aa);this.U(this.l)};p(Y,L);f=Y.prototype;f.ca=!0;f.V=function(){return"tab"};f.C=function(){return this.fa};f.toggle=function(){this.U(!this.l)};
f.U=function(a){this.fa?M(this.fa,a):a&&this.Oa&&(this.fa=this.Oa());this.fa&&nd(this.fa,"goog-zippy-content");if(this.Aa)M(this.T,!a),M(this.Aa,a);else if(this.T){var b=this.T;a?nd(b,"goog-zippy-expanded"):od(b,"goog-zippy-expanded");b=this.T;a?od(b,"goog-zippy-collapsed"):nd(b,"goog-zippy-collapsed");tc(this.T,"expanded",a)}this.l=a;this.dispatchEvent(new qd("toggle",this))};f.pb=function(){return this.ca};f.Na=function(a){this.ca!=a&&((this.ca=a)?(pd(this,this.T),pd(this,this.Aa)):this.ob.$a())};
var pd=function(a,b){b&&a.ob.d(b,"click",a.$b)};Y.prototype.Nb=function(a){if(13==a.keyCode||32==a.keyCode)this.toggle(),this.dispatchEvent(new D("action",this)),a.preventDefault(),a.stopPropagation()};Y.prototype.$b=function(){this.toggle();this.dispatchEvent(new D("action",this))};var qd=function(a,b){D.call(this,a,b)};p(qd,D);var Z=function(a,b){this.nb=[];for(var c=nb("span","ae-zippy",mb(document,a)),d=0,e;e=c[d];d++){var g;if(void 0!=e.parentNode.parentNode.parentNode.nextElementSibling)g=e.parentNode.parentNode.parentNode.nextElementSibling;else for(g=e.parentNode.parentNode.parentNode.nextSibling;g&&1!=g.nodeType;)g=g.nextSibling;e=new Y(e,g,!1);this.nb.push(e)}this.fc=new rd(this.nb,mb(document,b))};Z.prototype.ic=function(){return this.fc};Z.prototype.jc=function(){return this.nb};
var rd=function(a,b){this.ua=a;if(this.ua.length)for(var c=0,d;d=this.ua[c];c++)H(d,"toggle",this.Ub,!1,this);this.Ka=0;this.l=!1;c="ae-toggle ae-plus ae-action";this.ua.length||(c+=" ae-disabled");this.R=rb("span",{className:c},"Expand All");H(this.R,"click",this.Tb,!1,this);b&&b.appendChild(this.R)};rd.prototype.Tb=function(){this.ua.length&&this.U(!this.l)};
diff --git a/google/appengine/ext/cloudstorage/cloudstorage_stub.py b/google/appengine/ext/cloudstorage/cloudstorage_stub.py
index 5f442e4..6576082 100644
--- a/google/appengine/ext/cloudstorage/cloudstorage_stub.py
+++ b/google/appengine/ext/cloudstorage/cloudstorage_stub.py
@@ -22,6 +22,7 @@
import calendar
import datetime
import hashlib
+import httplib
import StringIO
from google.appengine.api import datastore
@@ -54,6 +55,9 @@
size = db.IntegerProperty()
+
+ next_offset = db.IntegerProperty(default=0)
+
creation = db.DateTimeProperty()
content_type = db.StringProperty(default=_GCS_DEFAULT_CONTENT_TYPE)
@@ -131,6 +135,12 @@
This implements the resumable upload XML API.
+ Only major limitation of current implementation is that we don't
+ support multiple upload sessions for the same GCS file. Previous
+ _AE_GCSFileInfo (which represents either a finalized file, or
+ an upload session) will be removed when a new upload session is
+ created.
+
Args:
filename: gcs filename of form /bucket/filename.
options: a dict containing all user specified request headers.
@@ -178,7 +188,7 @@
@db.non_transactional
def put_continue_creation(self, token, content, content_range,
- last=False,
+ length=None,
_upload_filename=None):
"""Continue object upload with PUTs.
@@ -186,10 +196,11 @@
Args:
token: upload token returned by post_start_creation.
- content: object content.
+ content: object content. None if no content was provided with this
+ PUT request.
content_range: a (start, end) tuple specifying the content range of this
- chunk. Both are inclusive according to XML API.
- last: True if this is the last chunk of file content.
+ chunk. Both are inclusive according to XML API. None is content is None.
+ length: file length, if this is the last chunk of file content.
_upload_filename: internal use. Might be removed any time! This is
used by blobstore to pass in the upload filename from user.
@@ -197,28 +208,56 @@
_AE_GCSFileInfo entity for this file if the file is finalized.
Raises:
- ValueError: if token is invalid.
+ ValueError: if something is invalid. The exception.args is a tuple of
+ (msg, http status code).
"""
+
+
ns = namespace_manager.get_namespace()
try:
namespace_manager.set_namespace('')
gcs_file = _AE_GCSFileInfo_.get_by_key_name(token)
if not gcs_file:
- raise ValueError('Invalid token')
+ raise ValueError('Invalid token', httplib.BAD_REQUEST)
+ if gcs_file.next_offset == -1:
+ raise ValueError('Received more uploads after file %s '
+ 'was finalized.' % gcs_file.filename,
+ httplib.OK)
if content:
start, end = content_range
if len(content) != (end - start + 1):
- raise ValueError('Invalid content range %d-%d' % content_range)
- blobkey = '%s-%d-%d' % (token, content_range[0], content_range[1])
- self.blob_storage.StoreBlob(blobkey, StringIO.StringIO(content))
- new_content = _AE_GCSPartialFile_(parent=gcs_file,
+ raise ValueError('Invalid content range %d-%d' % content_range,
+ httplib.REQUESTED_RANGE_NOT_SATISFIABLE)
- key_name='%020d' % start,
- partial_content=blobkey,
- start=start,
- end=end + 1)
- new_content.put()
- if last:
+ if start > gcs_file.next_offset:
+ raise ValueError('Expect start offset %s, got %s' %
+ (gcs_file.next_offset, start),
+ httplib.REQUESTED_RANGE_NOT_SATISFIABLE)
+
+ elif end < gcs_file.next_offset:
+ return
+ else:
+
+ content = content[gcs_file.next_offset - start:]
+ start = gcs_file.next_offset
+ blobkey = '%s-%d-%d' % (token, start, end)
+ self.blob_storage.StoreBlob(blobkey, StringIO.StringIO(content))
+ new_content = _AE_GCSPartialFile_(
+ parent=gcs_file,
+
+ key_name='%020d' % start,
+ partial_content=blobkey,
+ start=start,
+ end=end + 1)
+ new_content.put()
+ gcs_file.next_offset = end + 1
+ gcs_file.put()
+ if length is not None and length != gcs_file.next_offset:
+ raise ValueError(
+ 'Got finalization request with wrong file length. '
+ 'Expecting %s, got %s' % (gcs_file.next_offset, length),
+ httplib.REQUESTED_RANGE_NOT_SATISFIABLE)
+ elif length is not None:
return self._end_creation(token, _upload_filename)
finally:
namespace_manager.set_namespace(ns)
@@ -276,6 +315,8 @@
gcs_file = _AE_GCSFileInfo_.get_by_key_name(token)
if not gcs_file:
raise ValueError('Invalid token')
+ if gcs_file.finalized:
+ return gcs_file
error_msg, content = self._get_content(gcs_file)
if error_msg:
@@ -298,6 +339,8 @@
self.blob_storage.StoreBlob(token, StringIO.StringIO(content))
gcs_file.finalized = True
+
+ gcs_file.next_offset = -1
gcs_file.put()
return gcs_file
diff --git a/google/appengine/ext/cloudstorage/common.py b/google/appengine/ext/cloudstorage/common.py
index 07432ff..9a88971 100644
--- a/google/appengine/ext/cloudstorage/common.py
+++ b/google/appengine/ext/cloudstorage/common.py
@@ -399,7 +399,7 @@
return True
if 'remote_api' in server_software:
return False
- if server_software.startswith('Development'):
+ if server_software.startswith(('Development', 'testutil')):
return True
return False
diff --git a/google/appengine/ext/cloudstorage/stub_dispatcher.py b/google/appengine/ext/cloudstorage/stub_dispatcher.py
index 1346409..4567737 100644
--- a/google/appengine/ext/cloudstorage/stub_dispatcher.py
+++ b/google/appengine/ext/cloudstorage/stub_dispatcher.py
@@ -176,7 +176,7 @@
if not token:
- if not content_range.last:
+ if not content_range.length:
raise ValueError('Content-Range must have a final length.')
elif not content_range.no_data and content_range.range[0] != 0:
raise ValueError('Content-Range must specify complete object.')
@@ -184,11 +184,15 @@
token = gcs_stub.post_start_creation(filename, headers)
- gcs_stub.put_continue_creation(token,
- payload,
- content_range.range,
- content_range.last)
- if content_range.last:
+ try:
+ gcs_stub.put_continue_creation(token,
+ payload,
+ content_range.range,
+ content_range.length)
+ except ValueError, e:
+ return _FakeUrlFetchResult(e.args[1], {}, e.args[0])
+
+ if content_range.length is not None:
filestat = gcs_stub.head_object(filename)
response_headers = {
'content-length': filestat.st_size,
@@ -397,8 +401,11 @@
raise ValueError('Invalid content-range header %s' % self.value)
self.no_data = result.group(1) == '*'
- self.last = result.group(4) != '*'
- if self.no_data and not self.last:
+ last = result.group(4) != '*'
+ self.length = None
+ if last:
+ self.length = long(result.group(4))
+ if self.no_data and not last:
raise ValueError('Invalid content-range header %s' % self.value)
self.range = None
diff --git a/google/appengine/ext/datastore_admin/main.py b/google/appengine/ext/datastore_admin/main.py
index 7019bbe..ace4411 100644
--- a/google/appengine/ext/datastore_admin/main.py
+++ b/google/appengine/ext/datastore_admin/main.py
@@ -167,6 +167,9 @@
template_params = {
'run_as_a_service': self.request.get('run_as_a_service'),
+ 'datastore_admin_home': utils.GenerateHomeUrl(None),
+ 'offer_service': (self.request.get('service') and not
+ self.request.get('run_as_a_service')),
'kind_stats': kind_stats,
'more_kinds': more_kinds,
'last_stats_update': last_stats_update,
diff --git a/google/appengine/ext/datastore_admin/remote_api_put_stub.py b/google/appengine/ext/datastore_admin/remote_api_put_stub.py
index 76bb8fe..e8ac9f6 100644
--- a/google/appengine/ext/datastore_admin/remote_api_put_stub.py
+++ b/google/appengine/ext/datastore_admin/remote_api_put_stub.py
@@ -275,7 +275,7 @@
if not response.startswith('{'):
logging.info('Response unparasable: %s', response)
raise ConfigurationError(
- 'Invalid response recieved from server: %s' % response)
+ 'Invalid response received from server: %s' % response)
app_info = yaml.load(response)
if not app_info or 'rtok' not in app_info or 'app_id' not in app_info:
logging.info('Response unparsable: %s', response)
diff --git a/google/appengine/ext/datastore_admin/templates/list_actions.html b/google/appengine/ext/datastore_admin/templates/list_actions.html
index e5820b6..40088da 100644
--- a/google/appengine/ext/datastore_admin/templates/list_actions.html
+++ b/google/appengine/ext/datastore_admin/templates/list_actions.html
@@ -1,6 +1,19 @@
{% extends "base.html" %}
{% block body %}
<h2>Datastore Admin of {{hosting_app_id}}</h2>
+ {% if run_as_a_service %}
+ <div class="ae-message ae-rounded-sml">
+ Backup/Restore running as a service.
+ <a href="{{datastore_admin_home}}">Disable</a>
+ </div>
+ {% else %}
+ {% if offer_service %}
+ <div class="ae-message ae-rounded-sml">
+ This application may run backup/restore as a service.
+ <a href="{{datastore_admin_home}}?run_as_a_service=True">Enable</a>
+ </div>
+ {% endif %}
+ {% endif %}
{% if error %}
<div class="ae-errorbox">
{{ error|escape }}
diff --git a/google/appengine/ext/mapreduce/context.py b/google/appengine/ext/mapreduce/context.py
index 7513516..ef74a70 100644
--- a/google/appengine/ext/mapreduce/context.py
+++ b/google/appengine/ext/mapreduce/context.py
@@ -355,10 +355,12 @@
force_writes=self.force_writes)
-
-
class _Counters(Pool):
- """Regulates access to counters."""
+ """Regulates access to counters.
+
+ Counters Pool is a str to int map. It is saved as part of ShardState so it
+ is flushed when ShardState commits to datastore successfully.
+ """
def __init__(self, shard_state):
"""Constructor.
@@ -385,9 +387,22 @@
class Context(object):
"""MapReduce execution context.
+ The main purpose of Context is to facilitate IO. User code, input reader,
+ and output writer code can plug in pools (see Pool class) to Context to
+ batch operations.
+
+ There is a single Context instance associated with each worker thread.
+ It can be accessed via context.get(). handlers.MapperWorkerHandler creates
+ this instance before any IO code (input_reader, output_writer, user functions)
+ is called.
+
+ Each Pool decides how to batch and when to flush.
+ Context and all its pools are flushed by the end of a slice.
+ Upon error in slice execution, what is flushed is undefined. (See _Counters
+ for an exception).
+
Properties:
mapreduce_spec: current mapreduce specification as model.MapreduceSpec.
- shard_state: current shard state as model.ShardState.
"""
@@ -398,11 +413,13 @@
Args:
mapreduce_spec: mapreduce specification as model.MapreduceSpec.
- shard_state: shard state as model.ShardState.
+ shard_state: an instance of model.ShardState. This has to be the same
+ instance as the one MapperWorkerHandler mutates. All mutations are
+ flushed to datastore in the end of the slice.
task_retry_count: how many times this task has been retried.
"""
+ self._shard_state = shard_state
self.mapreduce_spec = mapreduce_spec
- self.shard_state = shard_state
self.task_retry_count = task_retry_count
if self.mapreduce_spec:
@@ -410,8 +427,8 @@
else:
self.mapreduce_id = None
- if self.shard_state:
- self.shard_id = self.shard_state.get_shard_id()
+ if shard_state:
+ self.shard_id = shard_state.get_shard_id()
else:
self.shard_id = None
@@ -463,12 +480,15 @@
cls._local._context_instance = context
+
+
+
def get():
"""Get current context instance.
Returns:
current context as Context.
"""
- if not hasattr(Context._local, '_context_instance') :
+ if not hasattr(Context._local, "_context_instance") :
return None
return Context._local._context_instance
diff --git a/google/appengine/ext/mapreduce/handlers.py b/google/appengine/ext/mapreduce/handlers.py
index 94b811e..66a6e35 100644
--- a/google/appengine/ext/mapreduce/handlers.py
+++ b/google/appengine/ext/mapreduce/handlers.py
@@ -95,8 +95,18 @@
class MapperWorkerCallbackHandler(base_handler.HugeTaskHandler):
"""Callback handler for mapreduce worker task."""
- _TASK_STATE = util._enum(RETRY_TASK="retry_task",
- DROP_TASK="drop_task")
+
+ _TASK_STATE = util._enum(
+
+ RETRY_TASK="retry_task",
+
+ DROP_TASK="drop_task",
+
+ PROCEED_TASK="proceed_task",
+
+ RETRY_SHARD="retry_shard",
+
+ FAIL_TASK="fail_task")
def __init__(self, *args):
"""Constructor."""
@@ -131,10 +141,11 @@
tstate: model.TransientShardState from taskqueue paylod.
Returns:
- A fresh shard state entity if lease is acquired. A _TASK_STATE
- enum if this task should be retried or dropped. Only old tasks
- (comparing to datastore state) will be dropped. Future tasks are
- retried until they naturally become old so that we don't ever stuck MR.
+ A _TASK_STATE enum. PROCEED_TASK if lock is acquired.
+ RETRY_TASK if task should be retried, DROP_TASK if task should
+ be dropped. Only old tasks (comparing to datastore state)
+ will be dropped. Future tasks are retried until they naturally
+ become old so that we don't ever stuck MR.
"""
if not shard_state:
@@ -225,7 +236,7 @@
the previous validation code). The task would die naturally eventually.
Returns:
- Fresh shard state if state commit succeeded. None otherwise.
+ A _TASK_STATE enum.
"""
fresh_state = model.ShardState.get_by_shard_id(tstate.shard_id)
if not fresh_state:
@@ -234,11 +245,11 @@
if (fresh_state.active and
fresh_state.slice_id == shard_state.slice_id and
fresh_state.slice_start_time == shard_state.slice_start_time):
- fresh_state.slice_start_time = datetime.datetime.now()
- fresh_state.slice_request_id = os.environ.get("REQUEST_LOG_ID")
- fresh_state.acquired_once = True
- fresh_state.put(config=config)
- return fresh_state
+ shard_state.slice_start_time = datetime.datetime.now()
+ shard_state.slice_request_id = os.environ.get("REQUEST_LOG_ID")
+ shard_state.acquired_once = True
+ shard_state.put(config=config)
+ return self._TASK_STATE.PROCEED_TASK
else:
logging.warning(
"Contention on slice %s-%s execution. Will retry again.",
@@ -335,25 +346,39 @@
shard_state.shard_id)
def handle(self):
- """Handle request."""
- tstate = model.TransientShardState.from_request(self.request)
- spec = tstate.mapreduce_spec
+ """Handle request.
+
+ This method has to be careful to pass the same ShardState instance to
+ its subroutines calls if the calls mutate or read from ShardState.
+ Note especially that Context instance caches and updates the ShardState
+ instance.
+
+ Returns:
+ Set HTTP status code and always returns None.
+ """
self._start_time = self._time()
+ shard_id = self.request.headers[util._MR_SHARD_ID_TASK_HEADER]
+ mr_id = self.request.headers[util._MR_ID_TASK_HEADER]
+ spec = model.MapreduceSpec._get_mapreduce_spec(mr_id)
shard_state, control = db.get([
- model.ShardState.get_key_by_shard_id(tstate.shard_id),
- model.MapreduceControl.get_key_by_job_id(spec.mapreduce_id),
+ model.ShardState.get_key_by_shard_id(shard_id),
+ model.MapreduceControl.get_key_by_job_id(mr_id),
])
- shard_state = self._try_acquire_lease(shard_state, tstate)
- if shard_state == self._TASK_STATE.RETRY_TASK:
- self.retry_task()
- return
- if shard_state == self._TASK_STATE.DROP_TASK:
- return
ctx = context.Context(spec, shard_state,
task_retry_count=self.task_retry_count())
+ context.Context._set(ctx)
+
+
+ tstate = model.TransientShardState.from_request(self.request)
+ task_state = self._try_acquire_lease(shard_state, tstate)
+ if task_state == self._TASK_STATE.RETRY_TASK:
+ return self.retry_task()
+ if task_state == self._TASK_STATE.DROP_TASK:
+ return
+ assert task_state == self._TASK_STATE.PROCEED_TASK
if control and control.command == model.MapreduceControl.ABORT:
logging.info("Abort command received by shard %d of job '%s'",
@@ -364,17 +389,7 @@
shard_state.put(config=util.create_datastore_write_config(spec))
return
-
-
-
-
-
- ndb_ctx = ndb.get_context()
- ndb_ctx.set_cache_policy(lambda key: False)
- ndb_ctx.set_memcache_policy(lambda key: False)
-
- context.Context._set(ctx)
- retry_directive = False
+ util._set_ndb_cache_policy()
try:
self.process_inputs(
@@ -392,14 +407,13 @@
tstate.output_writer.finalize(ctx, shard_state)
except Exception, e:
- retry_directive = self._retry_logic(
+ task_state = self._retry_logic(
e, shard_state, tstate, spec.mapreduce_id)
- finally:
- context.Context._set(None)
- if retry_directive is None:
+ if task_state == self._TASK_STATE.RETRY_TASK:
+
return self.retry_task()
- self._save_state_and_schedule_next(shard_state, tstate, retry_directive)
+ self._save_state_and_schedule_next(shard_state, tstate, task_state)
def process_inputs(self,
input_reader,
@@ -490,13 +504,13 @@
logging.error(
"Handler yielded %s, but no output writer is set.", output)
else:
- output_writer.write(output, ctx)
+ output_writer.write(output)
if self._time() - self._start_time >= parameters._SLICE_DURATION_SEC:
return False
return True
- def _save_state_and_schedule_next(self, shard_state, tstate, retry_shard):
+ def _save_state_and_schedule_next(self, shard_state, tstate, task_state):
"""Save state to datastore and schedule next task for this shard.
Update and save shard state. Schedule next slice if needed.
@@ -505,23 +519,26 @@
Args:
shard_state: model.ShardState for current shard.
tstate: model.TransientShardState for current shard.
- retry_shard: whether to retry shard.
+ task_state: enum _TASK_STATE.
"""
spec = tstate.mapreduce_spec
config = util.create_datastore_write_config(spec)
- task = None
- if retry_shard:
+ if task_state == self._TASK_STATE.RETRY_SHARD:
task = self._state_to_task(tstate, shard_state)
- elif shard_state.active:
+ elif task_state == self._TASK_STATE.PROCEED_TASK:
shard_state.advance_for_next_slice()
tstate.advance_for_next_slice()
countdown = self._get_countdown_for_next_slice(spec)
task = self._state_to_task(tstate, shard_state, countdown=countdown)
+ else:
+ assert task_state == self._TASK_STATE.FAIL_TASK
+ task = None
+
queue_name = os.environ.get("HTTP_X_APPENGINE_QUEUENAME", "default")
@db.transactional(retries=5)
@@ -543,7 +560,6 @@
if fresh_shard_state.active:
- assert task is not None
self._add_task(task, spec, queue_name)
@@ -577,8 +593,8 @@
mr_id: mapreduce id.
Returns:
- True if shard should be retried. None if slice should be retried.
- False otherwise.
+ A _TASK_STATE enum. RETRY_SHARD if shard should be retried.
+ RETRY_TASK if slice should be retried. DROP_TASK otherwise.
"""
logging.error("Shard %s got error.", shard_state.shard_id)
@@ -590,14 +606,12 @@
logging.error("Got FailJobError. Shard %s failed permanently.",
shard_state.shard_id)
shard_state.set_for_failure()
- return False
+ return self._TASK_STATE.FAIL_TASK
if type(e) in errors.SHARD_RETRY_ERRORS:
return self._attempt_shard_retry(shard_state, tstate, mr_id)
else:
- if self._attempt_slice_retry(shard_state, tstate):
- return
- return False
+ return self._attempt_slice_retry(shard_state, tstate)
def _attempt_shard_retry(self, shard_state, tstate, mr_id):
"""Whether to retry shard.
@@ -610,7 +624,8 @@
mr_id: mapreduce id.
Returns:
- True if shard should be retried. False otherwise.
+ A _TASK_STATE enum. RETRY_SHARD if shard should be retried.
+ FAIL_TASK otherwise.
"""
shard_retry = shard_state.retries
permanent_shard_failure = False
@@ -628,7 +643,7 @@
if permanent_shard_failure:
shard_state.set_for_failure()
- return False
+ return self._TASK_STATE.FAIL_TASK
shard_state.reset_for_retry()
logging.error("Shard %s will be retried for the %s time.",
@@ -640,7 +655,7 @@
output_writer = tstate.output_writer.create(
mr_state, shard_state)
tstate.reset_for_retry(output_writer)
- return True
+ return self._TASK_STATE.RETRY_SHARD
def _attempt_slice_retry(self, shard_state, tstate):
"""Attempt to retry this slice.
@@ -652,8 +667,8 @@
tstate: model.TransientShardState for current shard.
Returns:
- True when slice should be retried.
- False when slice can't be retried anymore.
+ A _TASK_STATE enum. RETRY_TASK if slice should be retried.
+ FAIL_TASK otherwise.
Raises:
errors.RetrySliceError: in order to trigger a slice retry.
@@ -670,14 +685,14 @@
sys.exc_clear()
self._try_free_lease(shard_state, slice_retry=True)
- return True
+ return self._TASK_STATE.RETRY_TASK
logging.error("Slice reached max retry limit of %s. "
"Shard %s failed permanently.",
self.task_retry_count(),
shard_state.shard_id)
shard_state.set_for_failure()
- return False
+ return self._TASK_STATE.FAIL_TASK
@staticmethod
def get_task_name(shard_id, slice_id, retry=0):
diff --git a/google/appengine/ext/mapreduce/input_readers.py b/google/appengine/ext/mapreduce/input_readers.py
index ef577cb..8fe1ad5 100644
--- a/google/appengine/ext/mapreduce/input_readers.py
+++ b/google/appengine/ext/mapreduce/input_readers.py
@@ -56,6 +56,7 @@
+
import base64
import copy
import logging
@@ -2448,12 +2449,19 @@
Optional configuration in the mapper_sec.input_reader dictionary.
BUFFER_SIZE_PARAM: the size of the read buffer for each file handle.
+ DELIMITER_PARAM: if specified, turn on the shallow splitting mode.
+ The delimiter is used as a path separator to designate directory
+ hierarchy. Matching of prefixes from OBJECT_NAME_PARAM
+ will stop at the first directory instead of matching
+ all files under the directory. This allows MR to process bucket with
+ hundreds of thousands of files.
"""
BUCKET_NAME_PARAM = "bucket_name"
OBJECT_NAMES_PARAM = "objects"
BUFFER_SIZE_PARAM = "buffer_size"
+ DELIMITER_PARAM = "delimiter"
_ACCOUNT_ID_PARAM = "account_id"
@@ -2462,7 +2470,14 @@
_JSON_PICKLE = "pickle"
_STRING_MAX_FILES_LISTED = 10
- def __init__(self, filenames, index=0, buffer_size=None, _account_id=None):
+
+
+
+
+
+
+ def __init__(self, filenames, index=0, buffer_size=None, _account_id=None,
+ delimiter=None):
"""Initialize a GoogleCloudStorageInputReader instance.
Args:
@@ -2471,11 +2486,40 @@
index: Index of the next filename to read.
buffer_size: The size of the read buffer, None to use default.
_account_id: Internal use only. See cloudstorage documentation.
+ delimiter: Delimiter used as path separator. See class doc.
"""
self._filenames = filenames
self._index = index
self._buffer_size = buffer_size
self._account_id = _account_id
+ self._delimiter = delimiter
+ self._bucket = None
+ self._bucket_iter = None
+
+ def _next_file(self):
+ """Find next filename.
+
+ self._filenames may need to be expanded via listbucket.
+
+ Returns:
+ None if no more file is left. Filename otherwise.
+ """
+ while True:
+ if self._bucket_iter:
+ try:
+ return self._bucket_iter.next().filename
+ except StopIteration:
+ self._bucket_iter = None
+ self._bucket = None
+ if self._index >= len(self._filenames):
+ return
+ filename = self._filenames[self._index]
+ self._index += 1
+ if self._delimiter is None or not filename.endswith(self._delimiter):
+ return filename
+ self._bucket = cloudstorage.listbucket(filename,
+ delimiter=self._delimiter)
+ self._bucket_iter = iter(self._bucket)
@classmethod
def validate(cls, mapper_spec):
@@ -2516,6 +2560,12 @@
raise errors.BadReaderParamsError(
"Object name is not a string but a %s" %
filename.__class__.__name__)
+ if cls.DELIMITER_PARAM in reader_spec:
+ delimiter = reader_spec[cls.DELIMITER_PARAM]
+ if not isinstance(delimiter, str):
+ raise errors.BadReaderParamsError(
+ "%s is not a string but a %s" %
+ (cls.DELIMITER_PARAM, type(delimiter)))
@classmethod
def split_input(cls, mapper_spec):
@@ -2533,45 +2583,50 @@
A list of InputReaders. None when no input data can be found.
"""
reader_spec = _get_params(mapper_spec, allow_old=False)
+ bucket = reader_spec[cls.BUCKET_NAME_PARAM]
+ filenames = reader_spec[cls.OBJECT_NAMES_PARAM]
+ delimiter = reader_spec.get(cls.DELIMITER_PARAM)
+ account_id = reader_spec.get(cls._ACCOUNT_ID_PARAM)
+ buffer_size = reader_spec.get(cls.BUFFER_SIZE_PARAM)
all_filenames = []
- bucket = reader_spec[cls.BUCKET_NAME_PARAM]
- filenames = reader_spec[cls.OBJECT_NAMES_PARAM]
for filename in filenames:
if filename.endswith("*"):
all_filenames.extend(
[file_stat.filename for file_stat in cloudstorage.listbucket(
- "/" + bucket,
- prefix=filename[:-1],
- _account_id=reader_spec.get(cls._ACCOUNT_ID_PARAM, None))])
+ "/" + bucket + "/" + filename[:-1], delimiter=delimiter,
+ _account_id=account_id)])
else:
all_filenames.append("/%s/%s" % (bucket, filename))
-
-
-
readers = []
for shard in range(0, mapper_spec.shard_count):
shard_filenames = all_filenames[shard::mapper_spec.shard_count]
if shard_filenames:
readers.append(cls(
- shard_filenames,
- buffer_size=reader_spec.get(cls.BUFFER_SIZE_PARAM, None),
- _account_id=reader_spec.get(cls._ACCOUNT_ID_PARAM, None)))
+ shard_filenames, buffer_size=buffer_size, _account_id=account_id,
+ delimiter=delimiter))
return readers
@classmethod
def from_json(cls, state):
- return pickle.loads(state[cls._JSON_PICKLE])
+ obj = pickle.loads(state[cls._JSON_PICKLE])
+ if obj._bucket:
+ obj._bucket_iter = iter(obj._bucket)
+ return obj
def to_json(self):
+ self._bucket_iter = None
return {self._JSON_PICKLE: pickle.dumps(self)}
def next(self):
"""Returns the next input from this input reader, a block of bytes.
+ Non existent files will be logged and skipped. The file might have been
+ removed after input splitting.
+
Returns:
The next input from this input reader in the form of a cloudstorage
ReadBuffer that supports a File-like interface (read, readline, seek,
@@ -2580,19 +2635,21 @@
Raises:
StopIteration: The list of files has been exhausted.
"""
-
-
- if self._index >= len(self._filenames):
- raise StopIteration()
- else:
- options = {}
- if self._buffer_size:
- options["read_buffer_size"] = self._buffer_size
- if self._account_id:
- options["_account_id"] = self._account_id
- handle = cloudstorage.open(self._filenames[self._index], **options)
- self._index += 1
- return handle
+ options = {}
+ if self._buffer_size:
+ options["read_buffer_size"] = self._buffer_size
+ if self._account_id:
+ options["_account_id"] = self._account_id
+ while True:
+ filename = self._next_file()
+ if filename is None:
+ raise StopIteration()
+ try:
+ handle = cloudstorage.open(filename, **options)
+ return handle
+ except cloudstorage.NotFoundError:
+ logging.warning("File %s may have been removed. Skipping file.",
+ filename)
def __str__(self):
diff --git a/google/appengine/ext/mapreduce/model.py b/google/appengine/ext/mapreduce/model.py
index 0e6e83b..b42ed1f 100644
--- a/google/appengine/ext/mapreduce/model.py
+++ b/google/appengine/ext/mapreduce/model.py
@@ -69,6 +69,7 @@
from google.appengine.api import datastore_errors
from google.appengine.api import datastore_types
+from google.appengine.api import memcache
from google.appengine.api import taskqueue
from google.appengine.datastore import datastore_rpc
from google.appengine.ext import db
@@ -777,6 +778,19 @@
return False
return self.to_json() == other.to_json()
+ @classmethod
+ def _get_mapreduce_spec(cls, mr_id):
+ """Get Mapreduce spec from mr id."""
+ key = 'GAE-MR-spec: %s' % mr_id
+ spec_json = memcache.get(key)
+ if spec_json:
+ return cls.from_json(spec_json)
+ state = MapreduceState.get_by_job_id(mr_id)
+ spec = state.mapreduce_spec
+ spec_json = spec.to_json()
+ memcache.set(key, spec_json)
+ return spec
+
class MapreduceState(db.Model):
"""Holds accumulated state of mapreduce execution.
@@ -877,7 +891,7 @@
chart.bottom.labels.append(x)
else:
chart.bottom.labels.append("")
- chart.left.labels = ['0', str(max(shards_processed))]
+ chart.left.labels = ["0", str(max(shards_processed))]
chart.left.min = 0
self.chart_width = min(700, max(300, shard_count * 20))
diff --git a/google/appengine/ext/mapreduce/output_writers.py b/google/appengine/ext/mapreduce/output_writers.py
index afa0e24..87636c1 100644
--- a/google/appengine/ext/mapreduce/output_writers.py
+++ b/google/appengine/ext/mapreduce/output_writers.py
@@ -52,6 +52,7 @@
+
import cStringIO
import gc
import logging
@@ -184,12 +185,11 @@
"""
raise NotImplementedError("create() not implemented in %s" % cls)
- def write(self, data, ctx):
+ def write(self, data):
"""Write data.
Args:
data: actual data yielded from handler. Type is writer-specific.
- ctx: an instance of context.Context.
"""
raise NotImplementedError("write() not implemented in %s" %
self.__class__)
@@ -761,13 +761,13 @@
class FileOutputWriter(FileOutputWriterBase):
"""An implementation of OutputWriter which outputs data into file."""
- def write(self, data, ctx):
+ def write(self, data):
"""Write data.
Args:
data: actual data yielded from handler. Type is writer-specific.
- ctx: an instance of context.Context.
"""
+ ctx = context.get()
if ctx.get_pool("file_pool") is None:
ctx.register_pool("file_pool", _FilePool(ctx=ctx))
ctx.get_pool("file_pool").append(self._filename, str(data))
@@ -792,13 +792,13 @@
def _get_output_sharding(cls, mapreduce_state=None, mapper_spec=None):
return cls.OUTPUT_SHARDING_INPUT_SHARDS
- def write(self, data, ctx):
+ def write(self, data):
"""Write data.
Args:
data: actual data yielded from handler. Type is writer-specific.
- ctx: an instance of context.Context.
"""
+ ctx = context.get()
if ctx.get_pool("records_pool") is None:
ctx.register_pool("records_pool",
@@ -810,7 +810,7 @@
class KeyValueFileOutputWriter(FileRecordsOutputWriter):
"""A file output writer for KeyValue records."""
- def write(self, data, ctx):
+ def write(self, data):
if len(data) != 2:
logging.error("Got bad tuple of length %d (2-tuple expected): %s",
len(data), data)
@@ -825,7 +825,7 @@
proto = file_service_pb.KeyValue()
proto.set_key(key)
proto.set_value(value)
- FileRecordsOutputWriter.write(self, proto.Encode(), ctx)
+ FileRecordsOutputWriter.write(self, proto.Encode())
class BlobstoreOutputWriterBase(FileOutputWriterBase):
@@ -888,7 +888,8 @@
_ACCOUNT_ID_PARAM = "account_id"
- _JSON_PICKLE = "pickle"
+ _JSON_FILENAME = "filename"
+ _JSON_GCS_BUFFER = "buffer"
def __init__(self, streaming_buffer, filename, writer_spec=None):
@@ -1013,24 +1014,25 @@
@classmethod
def from_json(cls, state):
- return pickle.loads(state[cls._JSON_PICKLE])
+ return cls(pickle.loads(state[cls._JSON_GCS_BUFFER]),
+ state[cls._JSON_FILENAME])
def to_json(self):
- return {self._JSON_PICKLE: pickle.dumps(self)}
+ return {self._JSON_GCS_BUFFER: pickle.dumps(self._streaming_buffer),
+ self._JSON_FILENAME: self._filename}
- def write(self, data, ctx):
+ def write(self, data):
"""Write data to the GoogleCloudStorage file.
Args:
data: string containing the data to be written.
- ctx: a model.Context for this shard.
"""
start_time = time.time()
self._streaming_buffer.write(data)
- if ctx:
- operation.counters.Increment(COUNTER_IO_WRITE_BYTES, len(data))(ctx)
- operation.counters.Increment(
- COUNTER_IO_WRITE_MSEC, int((time.time() - start_time) * 1000))(ctx)
+ ctx = context.get()
+ operation.counters.Increment(COUNTER_IO_WRITE_BYTES, len(data))(ctx)
+ operation.counters.Increment(
+ COUNTER_IO_WRITE_MSEC, int((time.time() - start_time) * 1000))(ctx)
def finalize(self, ctx, shard_state):
self._streaming_buffer.close()
@@ -1038,59 +1040,16 @@
shard_state.writer_state = {"filename": self._filename}
-class _PassthroughWriter(object):
- """Interface adapter from file-like write() to output writer write().
-
- Handles the mismatch of an output writer's write(), which requires a context,
- and a file-like write() which does not. The context is provided at init time
- and used with each write call.
- """
-
- def __init__(self, output_writer, ctx):
- """Initialize passthrough writer.
-
- Args:
- output_writer: the underlying mapreduce output writer.
- ctx: the mapreduce context to pass to the output writer on each write.
- """
- self._output_writer = output_writer
- self._ctx = ctx
-
- def write(self, data):
- """Write data.
-
- Args:
- data: data to write
- """
- self._output_writer.write(data, self._ctx)
-
-
class _GoogleCloudStorageRecordOutputWriter(_GoogleCloudStorageOutputWriter):
"""Write data to the Google Cloud Storage file using LevelDB format.
- Records are buffered in this writer till FLUSH_SIZE is reached or before
- serialization to reduce the amount buffered. Up to 32KB of padding may be
- added with each flush. Additionally, the underlying cloudstorage stream will
- perform additional/separate buffering to ensure that data is sent to Google
- Cloud Storage in the correct chunk sizes.
+ Data are written to cloudstorage in record format. On writer serializaton,
+ up to 32KB padding may be added to ensure the next slice aligns with
+ record boundary.
- Buffering may be improved in the future through a different implementation
- of the underlying LevelDB/Records writer.
-
- Optional configuration in the mapper_spec.output_writer dictionary:
- FLUSH_SIZE_PARAM: amount of data to buffer before generating records and
- sending to the underlying Google Cloud Storage writer. The total data
- buffered in memory may be this plus the buffer of the underlying writer.
-
- See the _GoogleCloudStorageOutputWriter for additional configuration options.
+ See the _GoogleCloudStorageOutputWriter for configuration options.
"""
-
- FLUSH_SIZE_PARAM = "record_flush_size"
-
-
- DEFAULT_FLUSH_SIZE = 1024 * 1024 * 1
-
def __init__(self,
streaming_buffer,
filename,
@@ -1104,51 +1063,20 @@
"""
super(_GoogleCloudStorageRecordOutputWriter, self).__init__(
streaming_buffer, filename, writer_spec)
- self._flush_size = writer_spec.get(self.FLUSH_SIZE_PARAM,
- self.DEFAULT_FLUSH_SIZE)
- self._reset()
+ self._record_writer = records.RecordsWriter(
+ super(_GoogleCloudStorageRecordOutputWriter, self))
def to_json(self):
- if self._buffer:
- self._flush(self._last_ctx)
+
+ if not self._streaming_buffer.closed:
+ self._record_writer._pad_block()
return super(_GoogleCloudStorageRecordOutputWriter, self).to_json()
- def write(self, data, ctx):
+ def write(self, data):
"""Write a single record of data to the file using LevelDB format.
Args:
data: string containing the data to be written.
- ctx: a model.Context for this shard.
"""
- self._buffer.append(data)
- self._size += len(data)
- self._last_ctx = ctx
- if self._size > self._flush_size:
- self._flush(ctx)
+ self._record_writer.write(data)
- def finalize(self, ctx, shard_state):
- """Finalize output file making it durable for a shard.
-
- Args:
- ctx: a model.Context for the shard.
- shard_state: an instance of model.ShardState for the shard.
- """
- self._flush(ctx)
- super(_GoogleCloudStorageRecordOutputWriter, self).finalize(ctx,
- shard_state)
-
- def _flush(self, ctx):
- record_writer = records.RecordsWriter(
- _PassthroughWriter(super(_GoogleCloudStorageRecordOutputWriter, self),
- ctx))
- with record_writer as w:
- for record in self._buffer:
- w.write(record)
- w._pad_block()
- self._reset()
-
- def _reset(self):
- self._buffer = []
-
- self._size = 0
- self._last_ctx = None
diff --git a/google/appengine/ext/mapreduce/shuffler.py b/google/appengine/ext/mapreduce/shuffler.py
index f9aea9e..1baca0a 100644
--- a/google/appengine/ext/mapreduce/shuffler.py
+++ b/google/appengine/ext/mapreduce/shuffler.py
@@ -63,6 +63,7 @@
+
class _OutputFile(db.Model):
"""Entity to store output filenames of pipelines.
@@ -264,7 +265,7 @@
"""
ctx = context.get()
mapper_spec = ctx.mapreduce_spec.mapper
- shard_number = ctx.shard_state.shard_number
+ shard_number = ctx._shard_state.shard_number
filenames = mapper_spec.params[self.FILES_PARAM][shard_number]
if len(filenames) != len(self._offsets):
@@ -478,13 +479,13 @@
def finalize(self, ctx, shard_state):
pass
- def write(self, data, ctx):
+ def write(self, data):
"""Write data.
Args:
data: actual data yielded from handler. Type is writer-specific.
- ctx: an instance of context.Context.
"""
+ ctx = context.get()
if len(data) != 2:
logging.error("Got bad tuple of length %d (2-tuple expected): %s",
len(data), data)
@@ -602,12 +603,12 @@
if shards is None:
shards = len(filenames)
yield mapper_pipeline.MapperPipeline(
- job_name + "-shuffle-hash",
- __name__ + "._hashing_map",
- input_readers.__name__ + ".RecordsReader",
- output_writer_spec= __name__ + "._HashingBlobstoreOutputWriter",
- params={'files': filenames},
- shards=shards)
+ job_name + "-shuffle-hash",
+ __name__ + "._hashing_map",
+ input_readers.__name__ + ".RecordsReader",
+ output_writer_spec= __name__ + "._HashingBlobstoreOutputWriter",
+ params={'files': filenames},
+ shards=shards)
class _ShuffleServicePipeline(pipeline_base.PipelineBase):
diff --git a/google/appengine/ext/mapreduce/util.py b/google/appengine/ext/mapreduce/util.py
index 7396790..8000e9b 100644
--- a/google/appengine/ext/mapreduce/util.py
+++ b/google/appengine/ext/mapreduce/util.py
@@ -54,6 +54,9 @@
import pickle
import types
+import google
+from google.appengine.ext import ndb
+
from google.appengine.datastore import datastore_rpc
from google.appengine.ext.mapreduce import parameters
@@ -349,3 +352,16 @@
else:
return datastore_rpc.Configuration()
+
+
+def _set_ndb_cache_policy():
+ """Tell NDB to never cache anything in memcache or in-process.
+
+ This ensures that entities fetched from Datastore input_readers via NDB
+ will not bloat up the request memory size and Datastore Puts will avoid
+ doing calls to memcache. Without this you get soft memory limit exits,
+ which hurts overall throughput.
+ """
+ ndb_ctx = ndb.get_context()
+ ndb_ctx.set_cache_policy(lambda key: False)
+ ndb_ctx.set_memcache_policy(lambda key: False)
diff --git a/google/appengine/ext/remote_api/remote_api_stub.py b/google/appengine/ext/remote_api/remote_api_stub.py
index fc2f380..1304812 100644
--- a/google/appengine/ext/remote_api/remote_api_stub.py
+++ b/google/appengine/ext/remote_api/remote_api_stub.py
@@ -568,7 +568,7 @@
response = server.Send(path, payload=None, **urlargs)
if not response.startswith('{'):
raise ConfigurationError(
- 'Invalid response recieved from server: %s' % response)
+ 'Invalid response received from server: %s' % response)
app_info = yaml.load(response)
if not app_info or 'rtok' not in app_info or 'app_id' not in app_info:
raise ConfigurationError('Error parsing app_id lookup response')
diff --git a/google/appengine/ext/testbed/__init__.py b/google/appengine/ext/testbed/__init__.py
index 2395cb8..b19dd16 100644
--- a/google/appengine/ext/testbed/__init__.py
+++ b/google/appengine/ext/testbed/__init__.py
@@ -113,6 +113,7 @@
mail_stub = None
+from google.appengine.api import request_info
from google.appengine.api import urlfetch_stub
from google.appengine.api import user_service_stub
from google.appengine.api.app_identity import app_identity_stub
@@ -130,6 +131,7 @@
except ImportError:
logservice_stub = None
from google.appengine.api.memcache import memcache_stub
+from google.appengine.api.modules import modules_stub
try:
from google.appengine.api.search import simple_search_stub
except ImportError:
@@ -183,6 +185,7 @@
USER_SERVICE_NAME = 'user'
XMPP_SERVICE_NAME = 'xmpp'
SEARCH_SERVICE_NAME = 'search'
+MODULES_SERVICE_NAME = 'modules'
INIT_STUB_METHOD_NAMES = {
@@ -201,6 +204,7 @@
USER_SERVICE_NAME: 'init_user_stub',
XMPP_SERVICE_NAME: 'init_xmpp_stub',
SEARCH_SERVICE_NAME: 'init_search_stub',
+ MODULES_SERVICE_NAME: 'init_modules_stub'
}
@@ -657,6 +661,20 @@
stub = simple_search_stub.SearchServiceStub()
self._register_stub(SEARCH_SERVICE_NAME, stub)
+ def init_modules_stub(self, enable=True):
+ """Enable the modules stub.
+
+ Args:
+ enable: True, if the fake service should be enabled, False if real
+ service should be disabled.
+ """
+ if not enable:
+ self._disable_stub(MODULES_SERVICE_NAME)
+ return
+
+ stub = modules_stub.ModulesServiceStub(request_info._LocalRequestInfo())
+ self._register_stub(MODULES_SERVICE_NAME, stub)
+
def _init_stub(self, service_name, *args, **kwargs):
"""Enable a stub by service name.
diff --git a/google/appengine/ext/webapp/mail_handlers.py b/google/appengine/ext/webapp/mail_handlers.py
index 86f11d7..baa2edc 100644
--- a/google/appengine/ext/webapp/mail_handlers.py
+++ b/google/appengine/ext/webapp/mail_handlers.py
@@ -23,7 +23,7 @@
Contains handlers to help with receiving mail and mail bounces.
InboundMailHandler: Has helper method for easily setting up
- email recievers.
+ email receivers.
BounceNotificationHandler: Has helper method for easily setting
up bounce notification receiver. Will parse HTTP request to
extract bounce notification.
diff --git a/google/appengine/tools/api_server.py b/google/appengine/tools/api_server.py
index 0f20950..11ecf5d 100644
--- a/google/appengine/tools/api_server.py
+++ b/google/appengine/tools/api_server.py
@@ -67,6 +67,7 @@
from google.appengine.api import datastore_file_stub
from google.appengine.datastore import datastore_sqlite_stub
from google.appengine.datastore import datastore_stub_util
+from google.appengine.datastore import datastore_v4_stub
from google.appengine.api import apiproxy_stub_map
from google.appengine.ext.remote_api import remote_api_pb
@@ -367,6 +368,10 @@
'datastore_v3', datastore)
apiproxy_stub_map.apiproxy.RegisterStub(
+ 'datastore_v4',
+ datastore_v4_stub.DatastoreV4Stub(app_id))
+
+ apiproxy_stub_map.apiproxy.RegisterStub(
'file',
file_service_stub.FileServiceStub(blob_storage))
diff --git a/google/appengine/tools/app_engine_web_xml_parser.py b/google/appengine/tools/app_engine_web_xml_parser.py
index 89e9b51..b2206d9 100644
--- a/google/appengine/tools/app_engine_web_xml_parser.py
+++ b/google/appengine/tools/app_engine_web_xml_parser.py
@@ -409,7 +409,7 @@
self.url_stream_handler_type = None
self.use_google_connector_j = None
self.static_file_includes = []
- self.static_file_excludes = []
+ self.static_file_excludes = ['WEB-INF/**', '**.jsp']
self.resource_file_includes = []
self.resource_file_excludes = []
self.auto_id_policy = self.DEFAULT_POLICY
diff --git a/google/appengine/tools/appcfg.py b/google/appengine/tools/appcfg.py
index 6b8870a..f89d3fb 100644
--- a/google/appengine/tools/appcfg.py
+++ b/google/appengine/tools/appcfg.py
@@ -34,6 +34,7 @@
import calendar
+import contextlib
import copy
import datetime
import errno
@@ -213,6 +214,24 @@
sys.exit(exit_code)
+@contextlib.contextmanager
+def TempChangeField(obj, field_name, new_value):
+ """Context manager to change a field value on an object temporarily.
+
+ Args:
+ obj: The object to change the field on.
+ field_name: The field name to change.
+ new_value: The new value.
+
+ Yields:
+ The old value.
+ """
+ old_value = getattr(obj, field_name)
+ setattr(obj, field_name, new_value)
+ yield old_value
+ setattr(obj, field_name, old_value)
+
+
class FileClassification(object):
"""A class to hold a file's classification.
@@ -481,26 +500,25 @@
class IndexDefinitionUpload(object):
"""Provides facilities to upload index definitions to the hosting service."""
- def __init__(self, rpcserver, config, definitions):
+ def __init__(self, rpcserver, definitions):
"""Creates a new DatastoreIndexUpload.
Args:
rpcserver: The RPC server to use. Should be an instance of HttpRpcServer
or TestRpcServer.
- config: The AppInfoExternal object derived from the app.yaml file.
definitions: An IndexDefinitions object.
"""
self.rpcserver = rpcserver
- self.config = config
self.definitions = definitions
def DoUpload(self):
"""Uploads the index definitions."""
StatusUpdate('Uploading index definitions.')
- self.rpcserver.Send('/api/datastore/index/add',
- app_id=self.config.application,
- version=self.config.version,
- payload=self.definitions.ToYAML())
+
+ with TempChangeField(self.definitions, 'application', None) as app_id:
+ self.rpcserver.Send('/api/datastore/index/add',
+ app_id=app_id,
+ payload=self.definitions.ToYAML())
class CronEntryUpload(object):
@@ -519,15 +537,12 @@
def DoUpload(self):
"""Uploads the cron entries."""
-
-
- app_id = self.cron.application
- self.cron.application = None
-
StatusUpdate('Uploading cron entries.')
- self.rpcserver.Send('/api/cron/update',
- app_id=app_id,
- payload=self.cron.ToYAML())
+
+ with TempChangeField(self.cron, 'application', None) as app_id:
+ self.rpcserver.Send('/api/cron/update',
+ app_id=app_id,
+ payload=self.cron.ToYAML())
class QueueEntryUpload(object):
@@ -546,14 +561,12 @@
def DoUpload(self):
"""Uploads the task queue entries."""
-
-
- app_id = self.queue.application
- self.queue.application = None
StatusUpdate('Uploading task queue entries.')
- self.rpcserver.Send('/api/queue/update',
- app_id=app_id,
- payload=self.queue.ToYAML())
+
+ with TempChangeField(self.queue, 'application', None) as app_id:
+ self.rpcserver.Send('/api/queue/update',
+ app_id=app_id,
+ payload=self.queue.ToYAML())
class DosEntryUpload(object):
@@ -572,14 +585,12 @@
def DoUpload(self):
"""Uploads the dos entries."""
-
-
- app_id = self.dos.application
- self.dos.application = None
StatusUpdate('Uploading DOS entries.')
- self.rpcserver.Send('/api/dos/update',
- app_id=app_id,
- payload=self.dos.ToYAML())
+
+ with TempChangeField(self.dos, 'application', None) as app_id:
+ self.rpcserver.Send('/api/dos/update',
+ app_id=app_id,
+ payload=self.dos.ToYAML())
class PagespeedEntryUpload(object):
@@ -674,19 +685,43 @@
version=self.version)
+class TrafficMigrator(object):
+ """Provides facilities to migrate traffic."""
+
+ def __init__(self, rpcserver, app_id, version):
+ """Creates a new TrafficMigrator.
+
+ Args:
+ rpcserver: The RPC server to use. Should be an instance of a subclass of
+ AbstractRpcServer.
+ app_id: The application to make the change to.
+
+ version: The version to set as the default.
+ """
+ self.rpcserver = rpcserver
+ self.app_id = app_id
+ self.version = version
+
+ def MigrateTraffic(self):
+ """Migrates traffic."""
+ StatusUpdate('Migrating traffic of application %s to %s.'
+ % (self.app_id, self.version))
+ self.rpcserver.Send('/api/appversion/migratetraffic',
+ app_id=self.app_id,
+ version=self.version)
+
+
class IndexOperation(object):
"""Provide facilities for writing Index operation commands."""
- def __init__(self, rpcserver, config):
+ def __init__(self, rpcserver):
"""Creates a new IndexOperation.
Args:
rpcserver: The RPC server to use. Should be an instance of HttpRpcServer
or TestRpcServer.
- config: appinfo.AppInfoExternal configuration object.
"""
self.rpcserver = rpcserver
- self.config = config
def DoDiff(self, definitions):
"""Retrieve diff file from the server.
@@ -703,16 +738,19 @@
that these indexes should probably be vacuumed).
"""
StatusUpdate('Fetching index definitions diff.')
- response = self.rpcserver.Send('/api/datastore/index/diff',
- app_id=self.config.application,
- payload=definitions.ToYAML())
+ with TempChangeField(definitions, 'application', None) as app_id:
+ response = self.rpcserver.Send('/api/datastore/index/diff',
+ app_id=app_id,
+ payload=definitions.ToYAML())
+
return datastore_index.ParseMultipleIndexDefinitions(response)
- def DoDelete(self, definitions):
+ def DoDelete(self, definitions, app_id):
"""Delete indexes from the server.
Args:
definitions: Index definitions to delete from datastore.
+ app_id: The application id.
Returns:
A single datstore_index.IndexDefinitions containing indexes that were
@@ -721,8 +759,9 @@
the index-diff and sending deletion confirmation through.
"""
StatusUpdate('Deleting selected index definitions.')
+
response = self.rpcserver.Send('/api/datastore/index/delete',
- app_id=self.config.application,
+ app_id=app_id,
payload=definitions.ToYAML())
return datastore_index.ParseIndexDefinitions(response)
@@ -730,18 +769,16 @@
class VacuumIndexesOperation(IndexOperation):
"""Provide facilities to request the deletion of datastore indexes."""
- def __init__(self, rpcserver, config, force,
- confirmation_fn=raw_input):
+ def __init__(self, rpcserver, force, confirmation_fn=raw_input):
"""Creates a new VacuumIndexesOperation.
Args:
rpcserver: The RPC server to use. Should be an instance of HttpRpcServer
or TestRpcServer.
- config: appinfo.AppInfoExternal configuration object.
force: True to force deletion of indexes, else False.
confirmation_fn: Function used for getting input form user.
"""
- super(VacuumIndexesOperation, self).__init__(rpcserver, config)
+ super(VacuumIndexesOperation, self).__init__(rpcserver)
self.force = force
self.confirmation_fn = confirmation_fn
@@ -819,7 +856,7 @@
if deletions.indexes:
- not_deleted = self.DoDelete(deletions)
+ not_deleted = self.DoDelete(deletions, definitions.application)
if not_deleted.indexes:
@@ -2719,6 +2756,8 @@
else:
if not self.rpc_server_class:
self.rpc_server_class = appengine_rpc.HttpRpcServerWithOAuth2Suggestion
+ if hasattr(self, 'runtime'):
+ self.rpc_server_class.RUNTIME = self.runtime
get_user_credentials = GetUserCredentials
source = GetSourceName()
@@ -2863,17 +2902,23 @@
return self._ParseYamlFile(basepath, 'backends',
backendinfo.LoadBackendInfo)
- def _ParseIndexYaml(self, basepath):
+ def _ParseIndexYaml(self, basepath, appyaml=None):
"""Parses the index.yaml file.
Args:
basepath: the directory of the application.
-
+ appyaml: The app.yaml, if present.
Returns:
A single parsed yaml file or None if the file does not exist.
"""
- return self._ParseYamlFile(basepath, 'index',
- datastore_index.ParseIndexDefinitions)
+ index_yaml = self._ParseYamlFile(basepath,
+ 'index',
+ datastore_index.ParseIndexDefinitions)
+ if not index_yaml:
+ return None
+ self._SetApplication(index_yaml, 'index', appyaml)
+
+ return index_yaml
def _SetApplication(self, dest_yaml, basename, appyaml=None):
"""Parses and sets the application property onto the dest_yaml parameter.
@@ -3036,6 +3081,10 @@
Returns:
An appinfo.AppInfoSummary if one was returned from the Deploy, None
otherwise.
+
+ Raises:
+ RuntimeError: If go-app-builder fails to generate a mapping from relative
+ paths to absolute paths, its stderr is raised.
"""
if not self.options.precompilation and appyaml.runtime == 'go':
@@ -3076,16 +3125,16 @@
try:
p = subprocess.Popen(gab_argv, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env={})
- rc = p.wait()
+ (stdout, stderr) = p.communicate()
except Exception, e:
raise RuntimeError('failed running go-app-builder', e)
- if rc != 0:
- raise RuntimeError(p.stderr.read())
+ if p.returncode != 0:
+ raise RuntimeError(stderr)
- overlay = dict([l.split('|') for l in p.stdout.read().split('\n') if l])
+ overlay = dict([l.split('|') for l in stdout.split('\n') if l])
logging.info('GOPATH overlay: %s', overlay)
def ofunc(path):
@@ -3130,8 +3179,6 @@
def Update(self):
"""Updates and deploys a new appversion and global app configs."""
- appyaml = None
- rpcserver = self._GetRpcServer()
if not os.path.isdir(self.basepath):
self.UpdateUsingSpecificFiles()
@@ -3142,6 +3189,8 @@
appyaml = self._ParseAppInfoFromYaml(
self.basepath,
basename=os.path.splitext(yaml_file_basename)[0])
+ self.runtime = appyaml.runtime
+ rpcserver = self._GetRpcServer()
@@ -3169,6 +3218,10 @@
if cron_yaml and cron_yaml.application != appyaml.application:
return _AbortAppMismatch('cron.yaml')
+ index_defs = self._ParseIndexYaml(self.basepath, appyaml)
+ if index_defs and index_defs.application != appyaml.application:
+ return _AbortAppMismatch('index.yaml')
+
self.UpdateVersion(rpcserver, self.basepath, appyaml, yaml_file_basename)
if appyaml.runtime == 'python':
@@ -3183,9 +3236,8 @@
- index_defs = self._ParseIndexYaml(self.basepath)
if index_defs:
- index_upload = IndexDefinitionUpload(rpcserver, appyaml, index_defs)
+ index_upload = IndexDefinitionUpload(rpcserver, index_defs)
try:
index_upload.DoUpload()
except urllib2.HTTPError, e:
@@ -3244,8 +3296,6 @@
if self.args:
self.parser.error('Expected a single <directory> argument.')
- appyaml = self._ParseAppInfoFromYaml(self.basepath)
-
index_defs = self._ParseIndexYaml(self.basepath)
if index_defs is None:
@@ -3253,7 +3303,6 @@
rpcserver = self._GetRpcServer()
vacuum = VacuumIndexesOperation(rpcserver,
- appyaml,
self.options.force_delete)
vacuum.DoVacuum(index_defs)
@@ -3287,14 +3336,12 @@
if self.args:
self.parser.error('Expected a single <directory> argument.')
-
- appyaml = self._ParseAppInfoFromYaml(self.basepath)
rpcserver = self._GetRpcServer()
index_defs = self._ParseIndexYaml(self.basepath)
if index_defs:
- index_upload = IndexDefinitionUpload(rpcserver, appyaml, index_defs)
+ index_upload = IndexDefinitionUpload(rpcserver, index_defs)
index_upload.DoUpload()
else:
print >>sys.stderr, 'Could not find index configuration. No action taken.'
@@ -3720,6 +3767,31 @@
version)
version_setter.SetVersion()
+
+ def MigrateTraffic(self):
+ """Migrates traffic."""
+ if len(self.args) == 1:
+ appyaml = self._ParseAppInfoFromYaml(self.args[0])
+ app_id = appyaml.application
+ version = appyaml.version
+ elif not self.args:
+ if not (self.options.app_id and self.options.version):
+ self.parser.error(
+ ('Expected a <directory> argument or both --application and '
+ '--version flags.'))
+ else:
+ self._PrintHelpAndExit()
+
+
+ if self.options.app_id:
+ app_id = self.options.app_id
+ if self.options.version:
+ version = self.options.version
+
+ traffic_migrator = TrafficMigrator(
+ self._GetRpcServer(), app_id, version)
+ traffic_migrator.MigrateTraffic()
+
def RequestLogs(self):
"""Write request logs to a file."""
@@ -4477,9 +4549,32 @@
use the --application, --version and --module flags to override these values.
The --module flag can also be a comma-delimited string of several modules. (ex.
module1,module2,module2) In this case, the default version of each module will
-be changed to the version specified.""",
+be changed to the version specified.
+
+The 'migrate_traffic' command can be thought of as a safer version of this
+command.""",
uses_basepath=False),
+ 'migrate_traffic': Action(
+ function='MigrateTraffic',
+ usage='%prog [options] migrate_traffic [directory]',
+ short_desc='Migrates traffic to another version.',
+ long_desc="""
+The 'migrate_traffic' command gradually gradually sends an increasing fraction
+of traffic your app's traffic from the current default version to another
+version. Once all traffic has been migrated, the new version is set as the
+default version.
+
+app.yaml specifies the target application, version, and (optionally) module; use
+the --application, --version and --module flags to override these values.
+
+Can be thought of as an enhanced version of the 'set_default_version'
+command.""",
+
+ uses_basepath=False,
+
+ hidden=True),
+
'resource_limits_info': Action(
function='ResourceLimitsInfo',
usage='%prog [options] resource_limits_info <directory>',
diff --git a/google/appengine/tools/appengine_rpc.py b/google/appengine/tools/appengine_rpc.py
index 387eb3a..9dc1ec9 100644
--- a/google/appengine/tools/appengine_rpc.py
+++ b/google/appengine/tools/appengine_rpc.py
@@ -38,6 +38,20 @@
from google.appengine.tools import dev_appserver_login
+_UPLOADING_APP_DOC_URLS = {
+ "go": "https://developers.google.com/appengine/docs/go/tools/"
+ "uploadinganapp#Go_Password-less_login_with_OAuth2",
+ "php": "https://developers.google.com/appengine/docs/php/tools/"
+ "uploadinganapp#PHP_Password-less_login_with_OAuth2",
+ "python": "https://developers.google.com/appengine/docs/python/tools/"
+ "uploadinganapp#Python_Password-less_login_with_OAuth2",
+ "python27": "https://developers.google.com/appengine/docs/python/tools/"
+ "uploadinganapp#Python_Password-less_login_with_OAuth2",
+ "java": "https://developers.google.com/appengine/docs/java/tools/"
+ "uploadinganapp#Passwordless_Login_with_OAuth2",
+ "java7": "https://developers.google.com/appengine/docs/java/tools/"
+ "uploadinganapp#Passwordless_Login_with_OAuth2",
+ }
logger = logging.getLogger('google.appengine.tools.appengine_rpc')
@@ -80,10 +94,10 @@
template = template + "\n%(data)s"
return template % {
- 'method' : req.get_method(),
- 'selector' : req.get_selector(),
- 'type' : req.get_type().upper(),
- 'host' : req.get_host(),
+ 'method': req.get_method(),
+ 'selector': req.get_selector(),
+ 'type': req.get_type().upper(),
+ 'host': req.get_host(),
'headers': headers,
'data': req.get_data(),
}
@@ -113,6 +127,9 @@
SUGGEST_OAUTH2 = False
+
+ RUNTIME = "python"
+
def __init__(self, host, auth_function, user_agent, source,
host_override=None, extra_headers=None, save_cookies=False,
auth_tries=3, account_type=None, debug_data=True, secure=True,
@@ -310,8 +327,7 @@
if self.SUGGEST_OAUTH2:
print >>sys.stderr, ("However, now the recommended way to log in "
"is using OAuth2. See")
- print >>sys.stderr, ("https://developers.google.com/appengine/"
- "docs/python/tools/uploadinganapp#oauth")
+ print >>sys.stderr, _UPLOADING_APP_DOC_URLS[self.RUNTIME]
else:
print >>sys.stderr, "Invalid username or password."
continue
diff --git a/google/appengine/tools/dev-channel-js.js b/google/appengine/tools/dev-channel-js.js
index 6b0bacb..3a0c75a 100644
--- a/google/appengine/tools/dev-channel-js.js
+++ b/google/appengine/tools/dev-channel-js.js
@@ -385,6 +385,8 @@
};
goog.inherits(goog.debug.Error, Error);
goog.debug.Error.prototype.name = "CustomError";
+goog.dom = {};
+goog.dom.NodeType = {ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, ENTITY_REFERENCE:5, ENTITY:6, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9, DOCUMENT_TYPE:10, DOCUMENT_FRAGMENT:11, NOTATION:12};
goog.string = {};
goog.string.Unicode = {NBSP:"\u00a0"};
goog.string.startsWith = function(str, prefix) {
@@ -792,6 +794,10 @@
goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value) && goog.asserts.doAssertFailure_("Expected boolean but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2));
return value
};
+goog.asserts.assertElement = function(value, opt_message, var_args) {
+ !goog.asserts.ENABLE_ASSERTS || goog.isObject(value) && value.nodeType == goog.dom.NodeType.ELEMENT || goog.asserts.doAssertFailure_("Expected Element but got %s: %s.", [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2));
+ return value
+};
goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
!goog.asserts.ENABLE_ASSERTS || value instanceof type || goog.asserts.doAssertFailure_("instanceof check failed.", null, opt_message, Array.prototype.slice.call(arguments, 3));
return value
@@ -1026,9 +1032,11 @@
goog.asserts.assert(null != arr.length);
return 2 >= arguments.length ? goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start) : goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end)
};
-goog.array.removeDuplicates = function(arr, opt_rv) {
- for(var returnArray = opt_rv || arr, seen = {}, cursorInsert = 0, cursorRead = 0;cursorRead < arr.length;) {
- var current = arr[cursorRead++], key = goog.isObject(current) ? "o" + goog.getUid(current) : (typeof current).charAt(0) + current;
+goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) {
+ for(var returnArray = opt_rv || arr, defaultHashFn = function() {
+ return goog.isObject(current) ? "o" + goog.getUid(current) : (typeof current).charAt(0) + current
+ }, hashFn = opt_hashFn || defaultHashFn, seen = {}, cursorInsert = 0, cursorRead = 0;cursorRead < arr.length;) {
+ var current = arr[cursorRead++], key = hashFn(current);
Object.prototype.hasOwnProperty.call(seen, key) || (seen[key] = !0, returnArray[cursorInsert++] = current)
}
returnArray.length = cursorInsert
@@ -1805,7 +1813,6 @@
JSCompiler_inline_result$$0 = void 0
}
goog.userAgent.DOCUMENT_MODE = JSCompiler_inline_result$$0;
-goog.dom = {};
goog.dom.BrowserFeature = {CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9), CAN_USE_CHILDREN_ATTRIBUTE:!goog.userAgent.GECKO && !goog.userAgent.IE || goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) || goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher("1.9.1"), CAN_USE_INNER_TEXT:goog.userAgent.IE && !goog.userAgent.isVersionOrHigher("9"), CAN_USE_PARENT_ELEMENT_PROPERTY:goog.userAgent.IE || goog.userAgent.OPERA || goog.userAgent.WEBKIT,
INNER_HTML_NEEDS_SCOPED_ELEMENT:goog.userAgent.IE};
goog.dom.classes = {};
@@ -1861,7 +1868,6 @@
goog.dom.classes.enable(element, className, add);
return add
};
-goog.dom.NodeType = {ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, ENTITY_REFERENCE:5, ENTITY:6, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9, DOCUMENT_TYPE:10, DOCUMENT_FRAGMENT:11, NOTATION:12};
goog.dom.TagName = {A:"A", ABBR:"ABBR", ACRONYM:"ACRONYM", ADDRESS:"ADDRESS", APPLET:"APPLET", AREA:"AREA", ARTICLE:"ARTICLE", ASIDE:"ASIDE", AUDIO:"AUDIO", B:"B", BASE:"BASE", BASEFONT:"BASEFONT", BDI:"BDI", BDO:"BDO", BIG:"BIG", BLOCKQUOTE:"BLOCKQUOTE", BODY:"BODY", BR:"BR", BUTTON:"BUTTON", CANVAS:"CANVAS", CAPTION:"CAPTION", CENTER:"CENTER", CITE:"CITE", CODE:"CODE", COL:"COL", COLGROUP:"COLGROUP", COMMAND:"COMMAND", DATA:"DATA", DATALIST:"DATALIST", DD:"DD", DEL:"DEL", DETAILS:"DETAILS", DFN:"DFN",
DIALOG:"DIALOG", DIR:"DIR", DIV:"DIV", DL:"DL", DT:"DT", EM:"EM", EMBED:"EMBED", FIELDSET:"FIELDSET", FIGCAPTION:"FIGCAPTION", FIGURE:"FIGURE", FONT:"FONT", FOOTER:"FOOTER", FORM:"FORM", FRAME:"FRAME", FRAMESET:"FRAMESET", H1:"H1", H2:"H2", H3:"H3", H4:"H4", H5:"H5", H6:"H6", HEAD:"HEAD", HEADER:"HEADER", HGROUP:"HGROUP", HR:"HR", HTML:"HTML", I:"I", IFRAME:"IFRAME", IMG:"IMG", INPUT:"INPUT", INS:"INS", ISINDEX:"ISINDEX", KBD:"KBD", KEYGEN:"KEYGEN", LABEL:"LABEL", LEGEND:"LEGEND", LI:"LI", LINK:"LINK",
MAP:"MAP", MARK:"MARK", MATH:"MATH", MENU:"MENU", META:"META", METER:"METER", NAV:"NAV", NOFRAMES:"NOFRAMES", NOSCRIPT:"NOSCRIPT", OBJECT:"OBJECT", OL:"OL", OPTGROUP:"OPTGROUP", OPTION:"OPTION", OUTPUT:"OUTPUT", P:"P", PARAM:"PARAM", PRE:"PRE", PROGRESS:"PROGRESS", Q:"Q", RP:"RP", RT:"RT", RUBY:"RUBY", S:"S", SAMP:"SAMP", SCRIPT:"SCRIPT", SECTION:"SECTION", SELECT:"SELECT", SMALL:"SMALL", SOURCE:"SOURCE", SPAN:"SPAN", STRIKE:"STRIKE", STRONG:"STRONG", STYLE:"STYLE", SUB:"SUB", SUMMARY:"SUMMARY",
@@ -1887,8 +1893,7 @@
goog.dom.getRequiredElementHelper_ = function(doc, id) {
goog.asserts.assertString(id);
var element = goog.dom.getElement(id);
- goog.asserts.assert(element, "No element found with id: " + id);
- return element
+ return element = goog.asserts.assertElement(element, "No element found with id: " + id)
};
goog.dom.$ = goog.dom.getElement;
goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
@@ -1980,7 +1985,7 @@
return goog.dom.getDocumentScrollElement_(document)
};
goog.dom.getDocumentScrollElement_ = function(doc) {
- return!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body
+ return!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body || doc.documentElement
};
goog.dom.getWindow = function(opt_doc) {
return opt_doc ? goog.dom.getWindow_(opt_doc) : window
@@ -2057,9 +2062,6 @@
}
return fragment
};
-goog.dom.getCompatMode = function() {
- return goog.dom.isCss1CompatMode() ? "CSS1Compat" : "BackCompat"
-};
goog.dom.isCss1CompatMode = function() {
return goog.dom.isCss1CompatMode_(document)
};
@@ -2563,9 +2565,6 @@
goog.dom.DomHelper.prototype.htmlToDocumentFragment = function(htmlString) {
return goog.dom.htmlToDocumentFragment_(this.document_, htmlString)
};
-goog.dom.DomHelper.prototype.getCompatMode = function() {
- return this.isCss1CompatMode() ? "CSS1Compat" : "BackCompat"
-};
goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
return goog.dom.isCss1CompatMode_(this.document_)
};
@@ -2754,8 +2753,9 @@
goog.events.EventType = {CLICK:"click", DBLCLICK:"dblclick", MOUSEDOWN:"mousedown", MOUSEUP:"mouseup", MOUSEOVER:"mouseover", MOUSEOUT:"mouseout", MOUSEMOVE:"mousemove", SELECTSTART:"selectstart", KEYPRESS:"keypress", KEYDOWN:"keydown", KEYUP:"keyup", BLUR:"blur", FOCUS:"focus", DEACTIVATE:"deactivate", FOCUSIN:goog.userAgent.IE ? "focusin" : "DOMFocusIn", FOCUSOUT:goog.userAgent.IE ? "focusout" : "DOMFocusOut", CHANGE:"change", SELECT:"select", SUBMIT:"submit", INPUT:"input", PROPERTYCHANGE:"propertychange",
DRAGSTART:"dragstart", DRAG:"drag", DRAGENTER:"dragenter", DRAGOVER:"dragover", DRAGLEAVE:"dragleave", DROP:"drop", DRAGEND:"dragend", TOUCHSTART:"touchstart", TOUCHMOVE:"touchmove", TOUCHEND:"touchend", TOUCHCANCEL:"touchcancel", BEFOREUNLOAD:"beforeunload", CONSOLEMESSAGE:"consolemessage", CONTEXTMENU:"contextmenu", DOMCONTENTLOADED:"DOMContentLoaded", ERROR:"error", HELP:"help", LOAD:"load", LOSECAPTURE:"losecapture", ORIENTATIONCHANGE:"orientationchange", READYSTATECHANGE:"readystatechange",
RESIZE:"resize", SCROLL:"scroll", UNLOAD:"unload", HASHCHANGE:"hashchange", PAGEHIDE:"pagehide", PAGESHOW:"pageshow", POPSTATE:"popstate", COPY:"copy", PASTE:"paste", CUT:"cut", BEFORECOPY:"beforecopy", BEFORECUT:"beforecut", BEFOREPASTE:"beforepaste", ONLINE:"online", OFFLINE:"offline", MESSAGE:"message", CONNECT:"connect", ANIMATIONSTART:goog.events.getVendorPrefixedName_("AnimationStart"), ANIMATIONEND:goog.events.getVendorPrefixedName_("AnimationEnd"), ANIMATIONITERATION:goog.events.getVendorPrefixedName_("AnimationIteration"),
-TRANSITIONEND:goog.events.getVendorPrefixedName_("TransitionEnd"), MSGESTURECHANGE:"MSGestureChange", MSGESTUREEND:"MSGestureEnd", MSGESTUREHOLD:"MSGestureHold", MSGESTURESTART:"MSGestureStart", MSGESTURETAP:"MSGestureTap", MSGOTPOINTERCAPTURE:"MSGotPointerCapture", MSINERTIASTART:"MSInertiaStart", MSLOSTPOINTERCAPTURE:"MSLostPointerCapture", MSPOINTERCANCEL:"MSPointerCancel", MSPOINTERDOWN:"MSPointerDown", MSPOINTERMOVE:"MSPointerMove", MSPOINTEROVER:"MSPointerOver", MSPOINTEROUT:"MSPointerOut",
-MSPOINTERUP:"MSPointerUp", TEXTINPUT:"textinput", COMPOSITIONSTART:"compositionstart", COMPOSITIONUPDATE:"compositionupdate", COMPOSITIONEND:"compositionend", EXIT:"exit", LOADABORT:"loadabort", LOADCOMMIT:"loadcommit", LOADREDIRECT:"loadredirect", LOADSTART:"loadstart", LOADSTOP:"loadstop", RESPONSIVE:"responsive", SIZECHANGED:"sizechanged", UNRESPONSIVE:"unresponsive", VISIBILITYCHANGE:"visibilitychange"};
+TRANSITIONEND:goog.events.getVendorPrefixedName_("TransitionEnd"), POINTERDOWN:"pointerdown", POINTERUP:"pointerup", POINTERCANCEL:"pointercancel", POINTERMOVE:"pointermove", POINTEROVER:"pointerover", POINTEROUT:"pointerout", POINTERENTER:"pointerenter", POINTERLEAVE:"pointerleave", GOTPOINTERCAPTURE:"gotpointercapture", LOSTPOINTERCAPTURE:"lostpointercapture", MSGESTURECHANGE:"MSGestureChange", MSGESTUREEND:"MSGestureEnd", MSGESTUREHOLD:"MSGestureHold", MSGESTURESTART:"MSGestureStart", MSGESTURETAP:"MSGestureTap",
+MSGOTPOINTERCAPTURE:"MSGotPointerCapture", MSINERTIASTART:"MSInertiaStart", MSLOSTPOINTERCAPTURE:"MSLostPointerCapture", MSPOINTERCANCEL:"MSPointerCancel", MSPOINTERDOWN:"MSPointerDown", MSPOINTERENTER:"MSPointerEnter", MSPOINTERHOVER:"MSPointerHover", MSPOINTERLEAVE:"MSPointerLeave", MSPOINTERMOVE:"MSPointerMove", MSPOINTEROUT:"MSPointerOut", MSPOINTEROVER:"MSPointerOver", MSPOINTERUP:"MSPointerUp", TEXTINPUT:"textinput", COMPOSITIONSTART:"compositionstart", COMPOSITIONUPDATE:"compositionupdate",
+COMPOSITIONEND:"compositionend", EXIT:"exit", LOADABORT:"loadabort", LOADCOMMIT:"loadcommit", LOADREDIRECT:"loadredirect", LOADSTART:"loadstart", LOADSTOP:"loadstop", RESPONSIVE:"responsive", SIZECHANGED:"sizechanged", UNRESPONSIVE:"unresponsive", VISIBILITYCHANGE:"visibilitychange"};
goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
opt_e && this.init(opt_e, opt_currentTarget)
};
@@ -3254,13 +3254,13 @@
};
goog.events.EventTarget.prototype.listen = function(type, listener, opt_useCapture, opt_listenerScope) {
this.assertInitialized_();
- return this.eventTargetListeners_.add(type, listener, !1, opt_useCapture, opt_listenerScope)
+ return this.eventTargetListeners_.add(String(type), listener, !1, opt_useCapture, opt_listenerScope)
};
goog.events.EventTarget.prototype.listenOnce = function(type, listener, opt_useCapture, opt_listenerScope) {
- return this.eventTargetListeners_.add(type, listener, !0, opt_useCapture, opt_listenerScope)
+ return this.eventTargetListeners_.add(String(type), listener, !0, opt_useCapture, opt_listenerScope)
};
goog.events.EventTarget.prototype.unlisten = function(type, listener, opt_useCapture, opt_listenerScope) {
- return this.eventTargetListeners_.remove(type, listener, opt_useCapture, opt_listenerScope)
+ return this.eventTargetListeners_.remove(String(type), listener, opt_useCapture, opt_listenerScope)
};
goog.events.EventTarget.prototype.unlistenByKey = function(key) {
return this.eventTargetListeners_.removeByKey(key)
@@ -3269,7 +3269,7 @@
return this.eventTargetListeners_ ? this.eventTargetListeners_.removeAll(opt_type) : 0
};
goog.events.EventTarget.prototype.fireListeners = function(type, capture, eventObject) {
- var listenerArray = this.eventTargetListeners_.listeners[type];
+ var listenerArray = this.eventTargetListeners_.listeners[String(type)];
if(!listenerArray) {
return!0
}
@@ -3284,13 +3284,14 @@
return rv && !1 != eventObject.returnValue_
};
goog.events.EventTarget.prototype.getListeners = function(type, capture) {
- return this.eventTargetListeners_.getListeners(type, capture)
+ return this.eventTargetListeners_.getListeners(String(type), capture)
};
goog.events.EventTarget.prototype.getListener = function(type, listener, capture, opt_listenerScope) {
- return this.eventTargetListeners_.getListener(type, listener, capture, opt_listenerScope)
+ return this.eventTargetListeners_.getListener(String(type), listener, capture, opt_listenerScope)
};
goog.events.EventTarget.prototype.hasListener = function(opt_type, opt_capture) {
- return this.eventTargetListeners_.hasListener(opt_type, opt_capture)
+ var id = goog.isDef(opt_type) ? String(opt_type) : void 0;
+ return this.eventTargetListeners_.hasListener(id, opt_capture)
};
goog.events.EventTarget.prototype.assertInitialized_ = function() {
goog.asserts.assert(this.eventTargetListeners_, "Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")
@@ -3714,6 +3715,120 @@
};
return iter
};
+goog.iter.count = function(opt_start, opt_step) {
+ var counter = opt_start || 0, step = goog.isDef(opt_step) ? opt_step : 1, iter = new goog.iter.Iterator;
+ iter.next = function() {
+ var returnValue = counter;
+ counter += step;
+ return returnValue
+ };
+ return iter
+};
+goog.iter.repeat = function(value) {
+ var iter = new goog.iter.Iterator;
+ iter.next = goog.functions.constant(value);
+ return iter
+};
+goog.iter.accumulate = function(iterable) {
+ var iterator = goog.iter.toIterator(iterable), total = 0, iter = new goog.iter.Iterator;
+ iter.next = function() {
+ return total += iterator.next()
+ };
+ return iter
+};
+goog.iter.zip = function(var_args) {
+ var args = arguments, iter = new goog.iter.Iterator;
+ if(0 < args.length) {
+ var iterators = goog.array.map(args, goog.iter.toIterator);
+ iter.next = function() {
+ var arr = goog.array.map(iterators, function(it) {
+ return it.next()
+ });
+ return arr
+ }
+ }
+ return iter
+};
+goog.iter.zipLongest = function(fillValue, var_args) {
+ var args = goog.array.slice(arguments, 1), iter = new goog.iter.Iterator;
+ if(0 < args.length) {
+ var iterators = goog.array.map(args, goog.iter.toIterator);
+ iter.next = function() {
+ var iteratorsHaveValues = !1, arr = goog.array.map(iterators, function(it) {
+ var returnValue;
+ try {
+ returnValue = it.next(), iteratorsHaveValues = !0
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ returnValue = fillValue
+ }
+ return returnValue
+ });
+ if(!iteratorsHaveValues) {
+ throw goog.iter.StopIteration;
+ }
+ return arr
+ }
+ }
+ return iter
+};
+goog.iter.compress = function(iterable, selectors) {
+ var selectorIterator = goog.iter.toIterator(selectors);
+ return goog.iter.filter(iterable, function() {
+ return!!selectorIterator.next()
+ })
+};
+goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {
+ this.iterator = goog.iter.toIterator(iterable);
+ this.keyFunc = opt_keyFunc || goog.functions.identity
+};
+goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);
+goog.iter.GroupByIterator_.prototype.next = function() {
+ for(;this.currentKey == this.targetKey;) {
+ this.currentValue = this.iterator.next(), this.currentKey = this.keyFunc(this.currentValue)
+ }
+ this.targetKey = this.currentKey;
+ return[this.currentKey, this.groupItems_(this.targetKey)]
+};
+goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {
+ for(var arr = [];this.currentKey == targetKey;) {
+ arr.push(this.currentValue);
+ try {
+ this.currentValue = this.iterator.next()
+ }catch(ex) {
+ if(ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ break
+ }
+ this.currentKey = this.keyFunc(this.currentValue)
+ }
+ return arr
+};
+goog.iter.groupBy = function(iterable, opt_keyFunc) {
+ return new goog.iter.GroupByIterator_(iterable, opt_keyFunc)
+};
+goog.iter.tee = function(iterable, opt_num) {
+ var iterator = goog.iter.toIterator(iterable), num = goog.isNumber(opt_num) ? opt_num : 2, buffers = goog.array.map(goog.array.range(num), function() {
+ return[]
+ }), addNextIteratorValueToBuffers = function() {
+ var val = iterator.next();
+ goog.array.forEach(buffers, function(buffer) {
+ buffer.push(val)
+ })
+ }, createIterator = function(buffer) {
+ var iter = new goog.iter.Iterator;
+ iter.next = function() {
+ goog.array.isEmpty(buffer) && addNextIteratorValueToBuffers();
+ goog.asserts.assert(!goog.array.isEmpty(buffer));
+ return buffer.shift()
+ };
+ return iter
+ };
+ return goog.array.map(buffers, createIterator)
+};
goog.structs.Map = function(opt_map, var_args) {
this.map_ = {};
this.keys_ = [];
diff --git a/google/appengine/tools/dev_appserver.py b/google/appengine/tools/dev_appserver.py
index 4352cd6..ff45ffb 100644
--- a/google/appengine/tools/dev_appserver.py
+++ b/google/appengine/tools/dev_appserver.py
@@ -130,6 +130,7 @@
from google.appengine.api.xmpp import xmpp_service_stub
from google.appengine.datastore import datastore_sqlite_stub
from google.appengine.datastore import datastore_stub_util
+from google.appengine.datastore import datastore_v4_stub
from google.appengine.ext.cloudstorage import stub_dispatcher as gcs_dispatcher
from google.appengine import dist
@@ -3575,6 +3576,10 @@
'datastore_v3', datastore)
apiproxy_stub_map.apiproxy.RegisterStub(
+ 'datastore_v4',
+ datastore_v4_stub.DatastoreV4Stub(app_id))
+
+ apiproxy_stub_map.apiproxy.RegisterStub(
'mail',
mail_stub.MailServiceStub(smtp_host,
smtp_port,
diff --git a/google/appengine/tools/dev_appserver_apiserver.py b/google/appengine/tools/dev_appserver_apiserver.py
index b318f5a..e213936 100644
--- a/google/appengine/tools/dev_appserver_apiserver.py
+++ b/google/appengine/tools/dev_appserver_apiserver.py
@@ -809,7 +809,7 @@
class ApiserverDispatcher(dev_appserver.URLDispatcher):
"""Dispatcher that handles requests to the built-in apiserver handlers."""
- _API_EXPLORER_URL = 'https://developers.google.com/apis-explorer/?base='
+ _API_EXPLORER_URL = 'https://apis-explorer.appspot.com/apis-explorer/?base='
class RequestState(object):
"""Enum tracking request state."""
diff --git a/google/appengine/tools/dev_appserver_import_hook.py b/google/appengine/tools/dev_appserver_import_hook.py
index d376c85..5185f8d 100644
--- a/google/appengine/tools/dev_appserver_import_hook.py
+++ b/google/appengine/tools/dev_appserver_import_hook.py
@@ -878,6 +878,7 @@
__PY27_OPTIONAL_ALLOWED_MODULES = {
'django': [],
+ 'endpoints': [],
'jinja2': ['_debugsupport', '_speedups'],
'lxml': ['etree', 'objectify'],
'markupsafe': ['_speedups'],
@@ -1155,7 +1156,6 @@
-
if 'django' not in self._module_dict:
version = libentry.version
if version == 'latest':
@@ -1183,6 +1183,18 @@
else:
logging.warn('Enabling Django version %s (no directory found)',
version)
+ elif libentry.name == 'endpoints':
+ try:
+
+ from google.third_party.apphosting.python.endpoints import v1_0
+ sys.path.append(os.path.dirname(v1_0.__file__))
+ del v1_0
+ except ImportError:
+
+
+ endpoints_path = os.path.join(SDK_ROOT, 'lib', 'endpoints-1.0')
+ if endpoints_path not in sys.path:
+ sys.path.append(endpoints_path)
@Trace
diff --git a/google/appengine/tools/dev_appserver_upload.py b/google/appengine/tools/dev_appserver_upload.py
index 6c2a47c..98a7fdd 100644
--- a/google/appengine/tools/dev_appserver_upload.py
+++ b/google/appengine/tools/dev_appserver_upload.py
@@ -163,7 +163,7 @@
class UploadCGIHandler(object):
"""Class used for handling an upload post.
- The main interface to this class is the UploadCGI method. This will recieve
+ The main interface to this class is the UploadCGI method. This will receive
the upload form, store the blobs contained in the post and rewrite the blobs
to contain BlobKeys instead of blobs.
"""
diff --git a/google/appengine/tools/devappserver2/api_server.py b/google/appengine/tools/devappserver2/api_server.py
index ba48730..f7e1559 100644
--- a/google/appengine/tools/devappserver2/api_server.py
+++ b/google/appengine/tools/devappserver2/api_server.py
@@ -58,6 +58,7 @@
from google.appengine.api.xmpp import xmpp_service_stub
from google.appengine.datastore import datastore_sqlite_stub
from google.appengine.datastore import datastore_stub_util
+from google.appengine.datastore import datastore_v4_stub
from google.appengine.api import apiproxy_stub_map
from google.appengine.api import datastore
@@ -317,6 +318,10 @@
'datastore_v3', datastore_stub)
apiproxy_stub_map.apiproxy.RegisterStub(
+ 'datastore_v4',
+ datastore_v4_stub.DatastoreV4Stub(app_id))
+
+ apiproxy_stub_map.apiproxy.RegisterStub(
'file',
file_service_stub.FileServiceStub(blob_storage))
diff --git a/google/appengine/tools/devappserver2/blob_download_test.py b/google/appengine/tools/devappserver2/blob_download_test.py
index eb85461..4f0acfa 100644
--- a/google/appengine/tools/devappserver2/blob_download_test.py
+++ b/google/appengine/tools/devappserver2/blob_download_test.py
@@ -486,7 +486,7 @@
if content_type:
options['content-type'] = content_type
blob_key = stub.post_start_creation(filename, options)
- stub.put_continue_creation(blob_key, data, (0, len(data) - 1), True)
+ stub.put_continue_creation(blob_key, data, (0, len(data) - 1), len(data))
self.blob_storage.StoreBlob(blob_key, cStringIO.StringIO(data))
return blob_key
diff --git a/google/appengine/tools/devappserver2/blob_upload.py b/google/appengine/tools/devappserver2/blob_upload.py
index 6616aa3..afdc1ba 100644
--- a/google/appengine/tools/devappserver2/blob_upload.py
+++ b/google/appengine/tools/devappserver2/blob_upload.py
@@ -263,8 +263,8 @@
blobkey = gs_stub.post_start_creation('/' + gs_filename,
{'content-type': content_type})
content = blob_file.read()
- return gs_stub.put_continue_creation(blobkey, content,
- (0, len(content) - 1), True, filename)
+ return gs_stub.put_continue_creation(
+ blobkey, content, (0, len(content) - 1), len(content), filename)
def _preprocess_data(self, content_type, blob_file,
filename, base64_encoding):
diff --git a/google/appengine/tools/devappserver2/devappserver2.py b/google/appengine/tools/devappserver2/devappserver2.py
index b027f2d..1402272 100644
--- a/google/appengine/tools/devappserver2/devappserver2.py
+++ b/google/appengine/tools/devappserver2/devappserver2.py
@@ -637,6 +637,7 @@
def module_to_address(self, module_name, instance=None):
"""Returns the address of a module."""
+
if module_name is None:
return self._dispatcher.dispatch_address
return self._dispatcher.get_hostname(
@@ -667,24 +668,6 @@
_setup_environ(configuration.app_id)
- if options.max_module_instances is None:
- module_to_max_instances = {}
- elif isinstance(options.max_module_instances, int):
- module_to_max_instances = {
- module_configuration.module_name: options.max_module_instances
- for module_configuration in configuration.modules}
- else:
- module_to_max_instances = options.max_module_instances
-
- if options.threadsafe_override is None:
- module_to_threadsafe_override = {}
- elif isinstance(options.threadsafe_override, bool):
- module_to_threadsafe_override = {
- module_configuration.module_name: options.threadsafe_override
- for module_configuration in configuration.modules}
- else:
- module_to_threadsafe_override = options.threadsafe_override
-
self._dispatcher = dispatcher.Dispatcher(
configuration,
options.host,
@@ -694,11 +677,13 @@
self._create_php_config(options),
self._create_python_config(options),
self._create_cloud_sql_config(options),
- module_to_max_instances,
+ self._create_module_to_setting(options.max_module_instances,
+ configuration, '--max_module_instances'),
options.use_mtime_file_watcher,
options.automatic_restart,
options.allow_skipped_files,
- module_to_threadsafe_override)
+ self._create_module_to_setting(options.threadsafe_override,
+ configuration, '--threadsafe_override'))
request_data = wsgi_request_info.WSGIRequestInfo(self._dispatcher)
storage_path = _get_storage_path(options.storage_path, configuration.app_id)
@@ -832,6 +817,39 @@
cloud_sql_config.mysql_socket = options.mysql_socket
return cloud_sql_config
+ @staticmethod
+ def _create_module_to_setting(setting, configuration, option):
+ """Create a per-module dictionary configuration.
+
+ Creates a dictionary that maps a module name to a configuration
+ setting. Used in conjunction with parse_per_module_option.
+
+ Args:
+ setting: a value that can be None, a dict of str->type or a single value.
+ configuration: an ApplicationConfiguration object.
+ option: the option name the setting came from.
+
+ Returns:
+ A dict of str->type.
+ """
+ if setting is None:
+ return {}
+
+ module_names = [module_configuration.module_name
+ for module_configuration in configuration.modules]
+ if isinstance(setting, dict):
+ # Warn and remove a setting if the module name is unknown.
+ module_to_setting = {}
+ for module_name, value in setting.items():
+ if module_name in module_names:
+ module_to_setting[module_name] = value
+ else:
+ logging.warning('Unknown module %r for %r', module_name, option)
+ return module_to_setting
+
+ # Create a dict with an entry for every module.
+ return {module_name: setting for module_name in module_names}
+
def main():
shutdown.install_signal_handlers()
diff --git a/google/appengine/tools/devappserver2/devappserver2_test.py b/google/appengine/tools/devappserver2/devappserver2_test.py
index f439342..87fdaf1 100644
--- a/google/appengine/tools/devappserver2/devappserver2_test.py
+++ b/google/appengine/tools/devappserver2/devappserver2_test.py
@@ -277,5 +277,49 @@
devappserver2.parse_threadsafe_override(':No'))
+class FakeApplicationConfiguration(object):
+
+ def __init__(self, modules):
+ self.modules = modules
+
+
+class FakeModuleConfiguration(object):
+
+ def __init__(self, module_name):
+ self.module_name = module_name
+
+
+class CreateModuleToSettingTest(unittest.TestCase):
+
+ def setUp(self):
+ self.application_configuration = FakeApplicationConfiguration([
+ FakeModuleConfiguration('m1'), FakeModuleConfiguration('m2'),
+ FakeModuleConfiguration('m3')])
+
+ def testNone(self):
+ self.assertEquals(
+ {},
+ devappserver2.DevelopmentServer._create_module_to_setting(
+ None, self.application_configuration, '--option'))
+
+ def testDict(self):
+ self.assertEquals(
+ {'m1': 3, 'm3': 1},
+ devappserver2.DevelopmentServer._create_module_to_setting(
+ {'m1': 3, 'm3': 1}, self.application_configuration, '--option'))
+
+ def testSingleValue(self):
+ self.assertEquals(
+ {'m1': True, 'm2': True, 'm3': True},
+ devappserver2.DevelopmentServer._create_module_to_setting(
+ True, self.application_configuration, '--option'))
+
+ def testDictWithUnknownModules(self):
+ self.assertEquals(
+ {'m1': 3.5},
+ devappserver2.DevelopmentServer._create_module_to_setting(
+ {'m1': 3.5, 'm4': 2.7}, self.application_configuration, '--option'))
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/google/appengine/tools/devappserver2/dispatcher.py b/google/appengine/tools/devappserver2/dispatcher.py
index e40fce6..f4496a0 100644
--- a/google/appengine/tools/devappserver2/dispatcher.py
+++ b/google/appengine/tools/devappserver2/dispatcher.py
@@ -31,6 +31,9 @@
from google.appengine.tools.devappserver2 import thread_executor
from google.appengine.tools.devappserver2 import wsgi_server
+# This file uses pep8 naming.
+# pylint: disable=invalid-name
+
_THREAD_POOL = thread_executor.ThreadExecutor()
ResponseTuple = collections.namedtuple('ResponseTuple',
@@ -355,6 +358,45 @@
self._executor.update_event(eta, (service, event_id))
def _get_module(self, module_name, version):
+ """Attempts to find the specified module.
+
+ Args:
+ module_name: The name of the module.
+ version: The version id.
+ Returns:
+ Module object.
+ Raises:
+ request_info.ModuleDoesNotExistError: The module doesn't exist.
+ request_info.VersionDoesNotExistError: The version doesn't exist.
+ """
+ if not module_name:
+ module_name = 'default'
+ if module_name not in self._module_name_to_module:
+ raise request_info.ModuleDoesNotExistError()
+ if (version is not None and
+ version != self._module_configurations[module_name].major_version):
+ raise request_info.VersionDoesNotExistError()
+ return self._module_name_to_module[module_name]
+
+ def _get_module_with_soft_routing(self, module_name, version):
+ """Uses soft-routing to find the specified module.
+
+ Soft-routing is an attempt to match the production resolution order, which
+ is slightly more permissive than the Modules API behavior. Here are the
+ rules allowed:
+ 1. If a module is requested that doesn't exist, use the default module.
+ 2. If a module is requested that doesn't exist, and there is no default
+ module, use any module.
+
+ Args:
+ module_name: The name of the module.
+ version: The version id.
+ Returns:
+ Module object.
+ Raises:
+ request_info.ModuleDoesNotExistError: The module doesn't exist.
+ request_info.VersionDoesNotExistError: The version doesn't exist.
+ """
if not module_name or module_name not in self._module_name_to_module:
if 'default' in self._module_name_to_module:
module_name = 'default'
@@ -365,7 +407,7 @@
module_name = self._module_name_to_module.keys()[0]
else:
raise request_info.ModuleDoesNotExistError(module_name)
- elif (version is not None and
+ if (version is not None and
version != self._module_configurations[module_name].major_version):
raise request_info.VersionDoesNotExistError()
return self._module_name_to_module[module_name]
@@ -452,7 +494,7 @@
BackgroundThreadLimitReachedError: The instance is at its background
thread capacity.
"""
- _module = self._get_module(module_name, version)
+ _module = self._get_module_with_soft_routing(module_name, version)
try:
inst.reserve_background_thread()
except instance.CannotAcceptRequests:
@@ -492,7 +534,7 @@
according to the load-balancing for the module and version.
"""
if module_name:
- _module = self._get_module(module_name, version)
+ _module = self._get_module_with_soft_routing(module_name, version)
else:
_module = self._module_for_request(urlparse.urlsplit(relative_url).path)
inst = _module.get_instance(instance_id) if instance_id else None
@@ -537,7 +579,7 @@
HTTP request.
"""
if module_name:
- _module = self._get_module(module_name, version)
+ _module = self._get_module_with_soft_routing(module_name, version)
inst = _module.get_instance(instance_id) if instance_id else None
else:
headers_dict = wsgiref.headers.Headers(headers)
@@ -551,8 +593,8 @@
else:
port = _module.balanced_port
environ = _module.build_request_environ(method, relative_url, headers, body,
- source_ip, port,
- fake_login=fake_login)
+ source_ip, port,
+ fake_login=fake_login)
start_response = start_response_utils.CapturingStartResponse()
response = self._handle_request(environ,
start_response,
@@ -591,7 +633,7 @@
prefix = hostname[:default_address_offset - 1]
if '.' in prefix:
raise request_info.ModuleDoesNotExistError(prefix)
- return self._get_module(prefix, None), None
+ return self._get_module_with_soft_routing(prefix, None), None
else:
if ':' in hostname:
@@ -645,5 +687,5 @@
for url, module_name in dispatch.dispatch:
if (url.path_exact and path == url.path or
not url.path_exact and path.startswith(url.path)):
- return self._get_module(module_name, None)
- return self._get_module(None, None)
+ return self._get_module_with_soft_routing(module_name, None)
+ return self._get_module_with_soft_routing(None, None)
diff --git a/google/appengine/tools/devappserver2/dispatcher_test.py b/google/appengine/tools/devappserver2/dispatcher_test.py
index e44c9a4..8cf575c 100644
--- a/google/appengine/tools/devappserver2/dispatcher_test.py
+++ b/google/appengine/tools/devappserver2/dispatcher_test.py
@@ -32,6 +32,9 @@
from google.appengine.tools.devappserver2 import scheduled_executor
from google.appengine.tools.devappserver2 import module
+# This file uses pep8 naming.
+# pylint: disable=invalid-name
+
class ApplicationConfigurationStub(object):
def __init__(self, modules):
@@ -229,6 +232,11 @@
self.assertRaises(request_info.InvalidInstanceIdError,
self.dispatcher.get_hostname, 'other', 'version2',
'invalid')
+ self.assertRaises(request_info.ModuleDoesNotExistError,
+ self.dispatcher.get_hostname,
+ 'nomodule',
+ 'version2',
+ None)
def test_get_module_by_name(self):
self.assertEqual(self.module1,
@@ -303,6 +311,24 @@
'body', '1.2.3.4', module_name='other')
self.mox.VerifyAll()
+ def test_add_async_request_soft_routing(self):
+ """Tests add_async_request with soft routing."""
+ dummy_environ = object()
+ self.mox.StubOutWithMock(dispatcher._THREAD_POOL, 'submit')
+ self.dispatcher._module_name_to_module['default'].build_request_environ(
+ 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
+ 'body', '1.2.3.4', 1).AndReturn(
+ dummy_environ)
+ dispatcher._THREAD_POOL.submit(
+ self.dispatcher._handle_request, dummy_environ, mox.IgnoreArg(),
+ self.dispatcher._module_name_to_module['default'],
+ None, catch_and_log_exceptions=True)
+ self.mox.ReplayAll()
+ self.dispatcher.add_async_request(
+ 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
+ 'body', '1.2.3.4', module_name='nomodule')
+ self.mox.VerifyAll()
+
def test_add_request(self):
dummy_environ = object()
self.mox.StubOutWithMock(self.dispatcher, '_resolve_target')
@@ -324,6 +350,25 @@
self.mox.VerifyAll()
self.assertEqual('Hello World', response.content)
+ def test_add_request_soft_routing(self):
+ """Tests soft routing to the default module."""
+ dummy_environ = object()
+ self.mox.StubOutWithMock(self.dispatcher, '_handle_request')
+ self.dispatcher._module_name_to_module['default'].build_request_environ(
+ 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
+ 'body', '1.2.3.4', 1, fake_login=True).AndReturn(
+ dummy_environ)
+ self.dispatcher._handle_request(
+ dummy_environ, mox.IgnoreArg(),
+ self.dispatcher._module_name_to_module['default'],
+ None).AndReturn(['Hello World'])
+ self.mox.ReplayAll()
+ response = self.dispatcher.add_request(
+ 'PUT', '/foo?bar=baz', [('Header', 'Value'), ('Other', 'Values')],
+ 'body', '1.2.3.4', fake_login=True, module_name='nomodule')
+ self.mox.VerifyAll()
+ self.assertEqual('Hello World', response.content)
+
def test_handle_request(self):
start_response = object()
servr = self.dispatcher._module_name_to_module['other']
@@ -431,9 +476,10 @@
def test_resolve_target_module_prefix(self):
self.mox.StubOutWithMock(self.dispatcher, '_module_for_request')
- self.mox.StubOutWithMock(self.dispatcher, '_get_module')
+ self.mox.StubOutWithMock(self.dispatcher, '_get_module_with_soft_routing')
servr = object()
- self.dispatcher._get_module('backend', None).AndReturn(servr)
+ self.dispatcher._get_module_with_soft_routing('backend', None).AndReturn(
+ servr)
self.mox.ReplayAll()
self.assertEqual((servr, None),
self.dispatcher._resolve_target('backend.localhost:1',
@@ -465,26 +511,133 @@
self.dispatcher._module_name_to_module['nondefault'] = self.module2
self.assertEqual(self.dispatcher._get_module(None, None), self.module1)
- # Test soft-routing. Querying for a non-existing module should return
- # default.
self.dispatcher._module_name_to_module = {'default': self.module1}
- self.assertEqual(self.dispatcher._get_module('nondefault', None),
+ self.assertRaises(request_info.ModuleDoesNotExistError,
+ self.dispatcher._get_module,
+ 'nondefault',
+ None)
+ # Test version handling.
+ self.dispatcher._module_configurations['default'] = MODULE_CONFIGURATIONS[0]
+ self.assertEqual(self.dispatcher._get_module('default', 'version'),
self.module1)
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module,
+ 'default',
+ 'version2')
def test_get_module_non_default(self):
"""Tests the _get_module method with a non-default module."""
self.dispatcher._module_name_to_module = {'default': self.module1,
- 'nondefault': self.module2}
- self.assertEqual(self.dispatcher._get_module('nondefault', None),
+ 'other': self.module2}
+ self.assertEqual(self.dispatcher._get_module('other', None),
self.module2)
+ # Test version handling.
+ self.dispatcher._module_configurations['default'] = MODULE_CONFIGURATIONS[0]
+ self.dispatcher._module_configurations['other'] = MODULE_CONFIGURATIONS[1]
+ self.assertEqual(self.dispatcher._get_module('other', 'version2'),
+ self.module2)
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module,
+ 'other',
+ 'version3')
def test_get_module_no_default(self):
"""Tests the _get_module method with no default module."""
- self.dispatcher._module_name_to_module = {'nondefault': self.module1}
- self.assertEqual(self.dispatcher._get_module('nondefault', None),
+ self.dispatcher._module_name_to_module = {'other': self.module1}
+ self.assertEqual(self.dispatcher._get_module('other', None),
self.module1)
- self.assertEqual(self.dispatcher._get_module(None, None), self.module1)
+ self.assertRaises(request_info.ModuleDoesNotExistError,
+ self.dispatcher._get_module,
+ None,
+ None)
+ # Test version handling.
+ self.dispatcher._module_configurations['other'] = MODULE_CONFIGURATIONS[0]
+ self.assertEqual(self.dispatcher._get_module('other', 'version'),
+ self.module1)
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module,
+ 'other',
+ 'version2')
+ def test_get_module_soft_routing_no_modules(self):
+ """Tests the _get_module_soft_routing method with no modules."""
+ self.dispatcher._module_name_to_module = {}
+ self.assertRaises(request_info.ModuleDoesNotExistError,
+ self.dispatcher._get_module_with_soft_routing,
+ None,
+ None)
+
+ def test_get_module_soft_routing_default_module(self):
+ """Tests the _get_module_soft_routing method with a default module."""
+ # Test default mopdule is returned for an empty query.
+ self.dispatcher._module_name_to_module = {'default': self.module1}
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing(None, None),
+ self.module1)
+
+ self.dispatcher._module_name_to_module['other'] = self.module2
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing(None, None),
+ self.module1)
+
+ # Test soft-routing. Querying for a non-existing module should return
+ # default.
+ self.dispatcher._module_name_to_module = {'default': self.module1}
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ None),
+ self.module1)
+
+ # Test version handling.
+ self.dispatcher._module_configurations['default'] = MODULE_CONFIGURATIONS[0]
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ 'version'),
+ self.module1)
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('default',
+ 'version'),
+ self.module1)
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module_with_soft_routing,
+ 'default',
+ 'version2')
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module_with_soft_routing,
+ 'other',
+ 'version2')
+
+ def test_get_module_soft_routing_non_default(self):
+ """Tests the _get_module_soft_routing method with a non-default module."""
+ self.dispatcher._module_name_to_module = {'default': self.module1,
+ 'other': self.module2}
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ None),
+ self.module2)
+ # Test version handling.
+ self.dispatcher._module_configurations['default'] = MODULE_CONFIGURATIONS[0]
+ self.dispatcher._module_configurations['other'] = MODULE_CONFIGURATIONS[1]
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ 'version2'),
+ self.module2)
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module_with_soft_routing,
+ 'other',
+ 'version3')
+
+ def test_get_module_soft_routing_no_default(self):
+ """Tests the _get_module_soft_routing method with no default module."""
+ self.dispatcher._module_name_to_module = {'other': self.module1}
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ None),
+ self.module1)
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ None),
+ self.module1)
+ # Test version handling.
+ self.dispatcher._module_configurations['other'] = MODULE_CONFIGURATIONS[0]
+ self.assertEqual(self.dispatcher._get_module_with_soft_routing('other',
+ 'version'),
+ self.module1)
+ self.assertRaises(request_info.VersionDoesNotExistError,
+ self.dispatcher._get_module_with_soft_routing,
+ 'other',
+ 'version2')
if __name__ == '__main__':
unittest.main()
diff --git a/google/appengine/tools/devappserver2/endpoints/endpoints_server.py b/google/appengine/tools/devappserver2/endpoints/endpoints_server.py
index 5ea4ef7..a0a10c1 100644
--- a/google/appengine/tools/devappserver2/endpoints/endpoints_server.py
+++ b/google/appengine/tools/devappserver2/endpoints/endpoints_server.py
@@ -65,7 +65,7 @@
class EndpointsDispatcher(object):
"""Dispatcher that handles requests to the built-in apiserver handlers."""
- _API_EXPLORER_URL = 'https://developers.google.com/apis-explorer/?base='
+ _API_EXPLORER_URL = 'https://apis-explorer.appspot.com/apis-explorer/?base='
def __init__(self, dispatcher, config_manager=None):
"""Constructor for EndpointsDispatcher.
diff --git a/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py b/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py
index e1dadca..a8b7365 100644
--- a/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py
+++ b/google/appengine/tools/devappserver2/endpoints/endpoints_server_regtest.py
@@ -80,7 +80,7 @@
"""Test that an RPC request works."""
body = json.dumps([{'jsonrpc': '2.0',
'id': 'gapiRpc',
- 'method': 'testservice.t2name',
+ 'method': 'test_service.t2name',
'params': {'name': 'MyName', 'number': 23},
'apiVersion': 'v1'}])
send_headers = {'content-type': 'application-rpc'}
@@ -114,7 +114,7 @@
body = json.dumps(body_json)
send_headers = {'content-type': 'application/json'}
status, content, headers = self.fetch_url(
- 'default', 'POST', '/_ah/api/test_service/v1/echo_datetime_field',
+ 'default', 'POST', '/_ah/api/test_service/v1/echo_dt_field',
body, send_headers)
self.assertEqual(200, status)
self.assertEqual('application/json', headers['Content-Type'])
@@ -202,7 +202,7 @@
"""Test that an RPC request to a second class in the API works."""
body = json.dumps([{'jsonrpc': '2.0',
'id': 'gapiRpc',
- 'method': 'testservice.extraname.test',
+ 'method': 'test_service.extraname.test',
'params': {},
'apiVersion': 'v1'}])
send_headers = {'content-type': 'application-rpc'}
diff --git a/google/appengine/tools/devappserver2/endpoints/endpoints_server_test.py b/google/appengine/tools/devappserver2/endpoints/endpoints_server_test.py
index 5c8c037..b9ce375 100644
--- a/google/appengine/tools/devappserver2/endpoints/endpoints_server_test.py
+++ b/google/appengine/tools/devappserver2/endpoints/endpoints_server_test.py
@@ -324,7 +324,7 @@
response = self.server.dispatch(request, self.start_response)
self.assert_http_match(response, 302,
[('Content-Length', '0'),
- ('Location', ('https://developers.google.com/'
+ ('Location', ('https://apis-explorer.appspot.com/'
'apis-explorer/?base='
'http://localhost:42/_ah/api'))],
'')
diff --git a/google/appengine/tools/devappserver2/endpoints/testdata/test_service.py b/google/appengine/tools/devappserver2/endpoints/testdata/test_service.py
index 0ad8272..ce1bcb5 100644
--- a/google/appengine/tools/devappserver2/endpoints/testdata/test_service.py
+++ b/google/appengine/tools/devappserver2/endpoints/testdata/test_service.py
@@ -63,14 +63,12 @@
"""ProtoRPC test class for Cloud Endpoints."""
@endpoints.method(message_types.VoidMessage, TestResponse,
- http_method='GET', name='test', path='test',
- scopes=[])
+ http_method='GET', scopes=[])
def test(self, unused_request):
return TestResponse(text='Test response')
@endpoints.method(message_types.VoidMessage, TestResponse,
- http_method='GET', name='empty_test', path='empty_test',
- scopes=[])
+ http_method='GET', scopes=[])
def empty_test(self, unused_request):
return TestResponse()
@@ -82,24 +80,20 @@
@endpoints.method(message_types.DateTimeMessage,
message_types.DateTimeMessage,
- http_method='POST', name='echodtmsg',
- path='echo_datetime_message',
- scopes=[])
+ http_method='POST', name='echodtmsg', scopes=[])
def echo_datetime_message(self, request):
return request
@endpoints.method(TestDateTime, TestDateTime,
http_method='POST', name='echodtfield',
- path='echo_datetime_field',
- scopes=[])
+ path='echo_dt_field', scopes=[])
def echo_datetime_field(self, request):
# Make sure we can access the fields of the datetime object.
logging.info('Year %d, Month %d', request.datetime_value.year,
request.datetime_value.month)
return request
- @endpoints.method(TestIntegers, TestIntegers, http_method='POST',
- path='increment_integers', scopes=[])
+ @endpoints.method(TestIntegers, TestIntegers, http_method='POST', scopes=[])
def increment_integers(self, request):
response = TestIntegers(
var_int32=request.var_int32 + 1,
@@ -109,8 +103,7 @@
var_uint64=request.var_uint64 + 1)
return response
- @endpoints.method(TestBytes, TestBytes,
- path='echo_bytes', scopes=[])
+ @endpoints.method(TestBytes, TestBytes, scopes=[])
def echo_bytes(self, request):
logging.info('Found bytes: %s', request.bytes_value)
return request
diff --git a/google/appengine/tools/devappserver2/inotify_file_watcher.py b/google/appengine/tools/devappserver2/inotify_file_watcher.py
index ba07b9e..3ea2f38 100644
--- a/google/appengine/tools/devappserver2/inotify_file_watcher.py
+++ b/google/appengine/tools/devappserver2/inotify_file_watcher.py
@@ -198,7 +198,7 @@
self._add_watch_for_path(path)
elif mask & IN_MOVED_TO:
self._add_watch_for_path(path)
- if path not in paths:
+ if path not in paths and not watcher_common.ignore_file(path):
paths.add(path)
return paths
diff --git a/google/appengine/tools/devappserver2/java_application.py b/google/appengine/tools/devappserver2/java_application.py
new file mode 100644
index 0000000..97ad012
--- /dev/null
+++ b/google/appengine/tools/devappserver2/java_application.py
@@ -0,0 +1,48 @@
+#!/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.
+#
+"""An abstraction around the source and classfiles for a Java application."""
+
+
+import os
+import os.path
+import google
+
+
+_SDKROOT = os.path.dirname(os.path.dirname(google.__file__))
+
+
+class JavaApplication(object):
+ """An abstraction around the compiled class files for a Java application."""
+
+ def __init__(self, module_configuration):
+ """Initializer for Module.
+
+ Args:
+ module_configuration: An application_configuration.ModuleConfiguration
+ instance storing the configuration data for a module.
+ """
+ self._module_configuration = module_configuration
+
+ def get_environment(self):
+ """Return the environment that should be used to run the Java executable."""
+ environ = {'SDKROOT': _SDKROOT,
+ 'PWD': self._module_configuration.application_root,
+ 'TZ': 'UTC'}
+ for var in ('PATH', 'SYSTEMROOT', 'USER'):
+ if var in os.environ:
+ environ[var] = os.environ[var]
+ return environ
diff --git a/google/appengine/tools/devappserver2/java_runtime.py b/google/appengine/tools/devappserver2/java_runtime.py
new file mode 100644
index 0000000..6b4d319
--- /dev/null
+++ b/google/appengine/tools/devappserver2/java_runtime.py
@@ -0,0 +1,128 @@
+#!/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.
+#
+"""Serves content for "script" handlers using the Java runtime."""
+
+
+import google
+import os
+import os.path
+import threading
+
+from google.appengine.api import appinfo
+from google.appengine.tools.devappserver2 import http_runtime
+from google.appengine.tools.devappserver2 import instance
+from google.appengine.tools.devappserver2 import java_application
+
+
+# TODO: figure out what's needed to react to file changes
+
+
+class JavaRuntimeInstanceFactory(instance.InstanceFactory):
+ """A factory that creates new Java runtime Instances."""
+
+ START_URL_MAP = appinfo.URLMap(
+ url='/_ah/start',
+ script='_java_app',
+ login='admin')
+ WARMUP_URL_MAP = appinfo.URLMap(
+ url='/_ah/warmup',
+ script='_java_app',
+ login='admin')
+ FILE_CHANGE_INSTANCE_RESTART_POLICY = instance.ALWAYS
+
+ def __init__(self, request_data, runtime_config_getter, module_configuration):
+ """Initializer for JavaRuntimeInstanceFactory.
+
+ Args:
+ request_data: A wsgi_request_info.WSGIRequestInfo that will be provided
+ with request information for use by API stubs.
+ runtime_config_getter: A function that can be called without arguments
+ and returns the runtime_config_pb2.RuntimeConfig containing the
+ configuration for the runtime.
+ module_configuration: An application_configuration.ModuleConfiguration
+ instance respresenting the configuration of the module that owns the
+ runtime.
+ """
+ super(JavaRuntimeInstanceFactory, self).__init__(request_data, 1)
+ self._runtime_config_getter = runtime_config_getter
+ self._module_configuration = module_configuration
+ self._application_lock = threading.Lock()
+ self._java_application = java_application.JavaApplication(
+ self._module_configuration)
+
+ def get_restart_directories(self):
+ """Returns a list of directories where changes trigger a restart.
+
+ Returns:
+ A list of directories where changes trigger a restart.
+ """
+ # TODO: implement
+ return []
+
+ def files_changed(self):
+ """Called when a file relevant to the factory *might* have changed."""
+ # TODO: implement
+
+ def configuration_changed(self, config_changes):
+ """Called when the configuration of the module has changed.
+
+ Args:
+ config_changes: A set containing the changes that occured. See the
+ *_CHANGED constants in the application_configuration module.
+ """
+ # TODO: implement
+
+ def new_instance(self, instance_id, expect_ready_request=False):
+ """Create and return a new Instance.
+
+ Args:
+ instance_id: A string or integer representing the unique (per module) id
+ of the instance.
+ expect_ready_request: If True then the instance will be sent a special
+ request (i.e. /_ah/warmup or /_ah/start) before it can handle external
+ requests.
+
+ Returns:
+ The newly created instance.Instance.
+ """
+
+ def instance_config_getter():
+ runtime_config = self._runtime_config_getter()
+ runtime_config.instance_id = str(instance_id)
+ return runtime_config
+
+ env = self._java_application.get_environment()
+ instance_jar = os.path.abspath(os.path.join(
+ os.path.dirname(google.__file__),
+ 'appengine/tools/devappserver2/java/lib/StandaloneInstance_deploy.jar'))
+ assert os.path.exists(instance_jar), instance_jar
+ # TODO: replace this with something smaller and releasable
+
+ with self._application_lock:
+ proxy = http_runtime.HttpRuntimeProxy(
+ ['java', '-jar', instance_jar],
+ instance_config_getter,
+ self._module_configuration,
+ env=env,
+ start_process_flavor=http_runtime.START_PROCESS_FILE)
+
+ return instance.Instance(self.request_data,
+ instance_id,
+ proxy,
+ self.max_concurrent_requests,
+ self.max_background_threads,
+ expect_ready_request)
diff --git a/google/appengine/tools/devappserver2/module.py b/google/appengine/tools/devappserver2/module.py
index 82ed3cc..c1ca237 100644
--- a/google/appengine/tools/devappserver2/module.py
+++ b/google/appengine/tools/devappserver2/module.py
@@ -50,6 +50,10 @@
from google.appengine.tools.devappserver2 import go_runtime
from google.appengine.tools.devappserver2 import http_runtime_constants
from google.appengine.tools.devappserver2 import instance
+try:
+ from google.appengine.tools.devappserver2 import java_runtime
+except ImportError:
+ java_runtime = None
from google.appengine.tools.devappserver2 import login
from google.appengine.tools.devappserver2 import php_runtime
from google.appengine.tools.devappserver2 import python_runtime
@@ -145,6 +149,11 @@
'python': python_runtime.PythonRuntimeInstanceFactory,
'python27': python_runtime.PythonRuntimeInstanceFactory,
}
+ if java_runtime:
+ _RUNTIME_INSTANCE_FACTORIES.update({
+ 'java': java_runtime.JavaRuntimeInstanceFactory,
+ 'java7': java_runtime.JavaRuntimeInstanceFactory,
+ })
def _create_instance_factory(self,
module_configuration):
diff --git a/google/appengine/tools/devappserver2/mtime_file_watcher.py b/google/appengine/tools/devappserver2/mtime_file_watcher.py
index a8c387c..b705ffe 100644
--- a/google/appengine/tools/devappserver2/mtime_file_watcher.py
+++ b/google/appengine/tools/devappserver2/mtime_file_watcher.py
@@ -79,6 +79,7 @@
for dirname, dirnames, filenames in os.walk(self._directory,
followlinks=True):
watcher_common.remove_ignored_dirs(dirnames)
+ filenames = [f for f in filenames if not watcher_common.ignore_file(f)]
for filename in filenames + dirnames:
if num_files == 10000:
warnings.warn(
diff --git a/google/appengine/tools/devappserver2/php/runtime.py b/google/appengine/tools/devappserver2/php/runtime.py
index ddbcec1..d1b8e61 100644
--- a/google/appengine/tools/devappserver2/php/runtime.py
+++ b/google/appengine/tools/devappserver2/php/runtime.py
@@ -34,6 +34,7 @@
from google.appengine.tools.devappserver2 import php
from google.appengine.tools.devappserver2 import request_rewriter
from google.appengine.tools.devappserver2 import runtime_config_pb2
+from google.appengine.tools.devappserver2 import safe_subprocess
from google.appengine.tools.devappserver2 import wsgi_server
SDK_PATH = os.path.abspath(
@@ -124,7 +125,7 @@
user_environ['HTTP_CONTENT_LENGTH'] = environ['CONTENT_LENGTH']
content = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
else:
- content = None
+ content = ''
# On Windows, in order to run a side-by-side assembly the specified env
# must include a valid SystemRoot.
@@ -151,13 +152,13 @@
user_environ[http_runtime_constants.REQUEST_TYPE_HEADER] = request_type
try:
- p = subprocess.Popen(args,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=user_environ,
- cwd=self.config.application_root)
- stdout, stderr = p.communicate(content)
+ p = safe_subprocess.start_process(args,
+ input_string=content,
+ env=user_environ,
+ cwd=self.config.application_root,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
except Exception as e:
logging.exception('Failure to start PHP with: %s', args)
start_response('500 Internal Server Error',
@@ -174,11 +175,10 @@
p.returncode, stdout, stderr)
start_response('500 Internal Server Error',
[(http_runtime_constants.ERROR_CODE_HEADER, '1')])
- return ['php failure (%r) with:\nstdout:%s\nstderr:\n%s' %
- (p.returncode, stdout, stderr)]
+ message = httplib.HTTPMessage(cStringIO.StringIO(stdout))
+ return [message.fp.read()]
message = httplib.HTTPMessage(cStringIO.StringIO(stdout))
- assert 'Content-Type' in message, 'invalid CGI response: %r' % stdout
if 'Status' in message:
status = message['Status']
diff --git a/google/appengine/tools/devappserver2/python/sandbox.py b/google/appengine/tools/devappserver2/python/sandbox.py
index 688670c..4a6aba5 100644
--- a/google/appengine/tools/devappserver2/python/sandbox.py
+++ b/google/appengine/tools/devappserver2/python/sandbox.py
@@ -27,8 +27,6 @@
import google
-import protorpc
-
from google.appengine import dist
from google.appengine.api import app_logging
from google.appengine.api.logservice import logservice
@@ -114,7 +112,7 @@
"""
devnull = open(os.path.devnull)
- modules = [os, traceback, google, protorpc]
+ modules = [os, traceback, google]
c_module = _find_shared_object_c_module()
if c_module:
modules.append(c_module)
diff --git a/google/appengine/tools/devappserver2/runtime_config_pb2.py b/google/appengine/tools/devappserver2/runtime_config_pb2.py
index 726ba3d..5c153af 100644
--- a/google/appengine/tools/devappserver2/runtime_config_pb2.py
+++ b/google/appengine/tools/devappserver2/runtime_config_pb2.py
@@ -29,7 +29,7 @@
DESCRIPTOR = _descriptor.FileDescriptor(
name='apphosting/tools/devappserver2/runtime_config.proto',
package='apphosting.tools.devappserver2',
- serialized_pb='\n3apphosting/tools/devappserver2/runtime_config.proto\x12\x1e\x61pphosting.tools.devappserver2\"\xf2\x04\n\x06\x43onfig\x12\x0e\n\x06\x61pp_id\x18\x01 \x02(\x0c\x12\x12\n\nversion_id\x18\x02 \x02(\x0c\x12\x18\n\x10\x61pplication_root\x18\x03 \x02(\x0c\x12\x19\n\nthreadsafe\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x08\x61pi_host\x18\x11 \x01(\t:\tlocalhost\x12\x10\n\x08\x61pi_port\x18\x05 \x02(\x05\x12:\n\tlibraries\x18\x06 \x03(\x0b\x32\'.apphosting.tools.devappserver2.Library\x12\x16\n\nskip_files\x18\x07 \x01(\t:\x02^$\x12\x18\n\x0cstatic_files\x18\x08 \x01(\t:\x02^$\x12\x43\n\rpython_config\x18\x0e \x01(\x0b\x32,.apphosting.tools.devappserver2.PythonConfig\x12=\n\nphp_config\x18\t \x01(\x0b\x32).apphosting.tools.devappserver2.PhpConfig\x12\x38\n\x07\x65nviron\x18\n \x03(\x0b\x32\'.apphosting.tools.devappserver2.Environ\x12\x42\n\x10\x63loud_sql_config\x18\x0b \x01(\x0b\x32(.apphosting.tools.devappserver2.CloudSQL\x12\x12\n\ndatacenter\x18\x0c \x02(\t\x12\x13\n\x0binstance_id\x18\r \x02(\t\x12\x1b\n\x10stderr_log_level\x18\x0f \x01(\x03:\x01\x31\x12\x13\n\x0b\x61uth_domain\x18\x10 \x02(\t\x12\x15\n\rmax_instances\x18\x12 \x01(\x05\"A\n\tPhpConfig\x12\x1b\n\x13php_executable_path\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65nable_debugger\x18\x03 \x02(\x08\"<\n\x0cPythonConfig\x12\x16\n\x0estartup_script\x18\x01 \x01(\t\x12\x14\n\x0cstartup_args\x18\x02 \x01(\t\"t\n\x08\x43loudSQL\x12\x12\n\nmysql_host\x18\x01 \x02(\t\x12\x12\n\nmysql_port\x18\x02 \x02(\x05\x12\x12\n\nmysql_user\x18\x03 \x02(\t\x12\x16\n\x0emysql_password\x18\x04 \x02(\t\x12\x14\n\x0cmysql_socket\x18\x05 \x01(\t\"(\n\x07Library\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t\"%\n\x07\x45nviron\x12\x0b\n\x03key\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c\x42\x02 \x02')
+ serialized_pb='\n3apphosting/tools/devappserver2/runtime_config.proto\x12\x1e\x61pphosting.tools.devappserver2\"\xf2\x04\n\x06\x43onfig\x12\x0e\n\x06\x61pp_id\x18\x01 \x02(\x0c\x12\x12\n\nversion_id\x18\x02 \x02(\x0c\x12\x18\n\x10\x61pplication_root\x18\x03 \x02(\x0c\x12\x19\n\nthreadsafe\x18\x04 \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x08\x61pi_host\x18\x11 \x01(\t:\tlocalhost\x12\x10\n\x08\x61pi_port\x18\x05 \x02(\x05\x12:\n\tlibraries\x18\x06 \x03(\x0b\x32\'.apphosting.tools.devappserver2.Library\x12\x16\n\nskip_files\x18\x07 \x01(\t:\x02^$\x12\x18\n\x0cstatic_files\x18\x08 \x01(\t:\x02^$\x12\x43\n\rpython_config\x18\x0e \x01(\x0b\x32,.apphosting.tools.devappserver2.PythonConfig\x12=\n\nphp_config\x18\t \x01(\x0b\x32).apphosting.tools.devappserver2.PhpConfig\x12\x38\n\x07\x65nviron\x18\n \x03(\x0b\x32\'.apphosting.tools.devappserver2.Environ\x12\x42\n\x10\x63loud_sql_config\x18\x0b \x01(\x0b\x32(.apphosting.tools.devappserver2.CloudSQL\x12\x12\n\ndatacenter\x18\x0c \x02(\t\x12\x13\n\x0binstance_id\x18\r \x02(\t\x12\x1b\n\x10stderr_log_level\x18\x0f \x01(\x03:\x01\x31\x12\x13\n\x0b\x61uth_domain\x18\x10 \x02(\t\x12\x15\n\rmax_instances\x18\x12 \x01(\x05\"A\n\tPhpConfig\x12\x1b\n\x13php_executable_path\x18\x01 \x01(\x0c\x12\x17\n\x0f\x65nable_debugger\x18\x03 \x02(\x08\"<\n\x0cPythonConfig\x12\x16\n\x0estartup_script\x18\x01 \x01(\t\x12\x14\n\x0cstartup_args\x18\x02 \x01(\t\"t\n\x08\x43loudSQL\x12\x12\n\nmysql_host\x18\x01 \x02(\t\x12\x12\n\nmysql_port\x18\x02 \x02(\x05\x12\x12\n\nmysql_user\x18\x03 \x02(\t\x12\x16\n\x0emysql_password\x18\x04 \x02(\t\x12\x14\n\x0cmysql_socket\x18\x05 \x01(\t\"(\n\x07Library\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0f\n\x07version\x18\x02 \x02(\t\"%\n\x07\x45nviron\x12\x0b\n\x03key\x18\x01 \x02(\x0c\x12\r\n\x05value\x18\x02 \x02(\x0c\x42,\n&com.google.appengine.tools.development \x02P\x01')
@@ -426,5 +426,5 @@
DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), ' \002')
+DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), '\n&com.google.appengine.tools.development \002P\001')
# @@protoc_insertion_point(module_scope)
diff --git a/google/appengine/tools/devappserver2/safe_subprocess.py b/google/appengine/tools/devappserver2/safe_subprocess.py
index c35762e..5f5bdbf 100644
--- a/google/appengine/tools/devappserver2/safe_subprocess.py
+++ b/google/appengine/tools/devappserver2/safe_subprocess.py
@@ -58,8 +58,17 @@
with _popen_lock:
logging.debug('Starting process %r with input=%r, env=%r, cwd=%r',
args, input_string, env, cwd)
+
+ if sys.platform == 'win32':
+ # Suppress the display of the console window on Windows.
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
+ startupinfo.wShowWindow = subprocess.SW_HIDE
+ else:
+ startupinfo = None
+
p = subprocess.Popen(args, env=env, cwd=cwd, stdout=stdout, stderr=stderr,
- stdin=subprocess.PIPE)
+ stdin=subprocess.PIPE, startupinfo=startupinfo)
if _SUBPROCESS_STDIN_IS_THREAD_HOSTILE:
p.stdin.write(input_string)
p.stdin.close()
diff --git a/google/appengine/tools/devappserver2/watcher_common.py b/google/appengine/tools/devappserver2/watcher_common.py
index ddc0c51..26c2f52 100644
--- a/google/appengine/tools/devappserver2/watcher_common.py
+++ b/google/appengine/tools/devappserver2/watcher_common.py
@@ -19,6 +19,24 @@
# Directories that we should not watch at all.
_IGNORED_DIRS = ('.git', '.hg', '.svn')
+# File suffixes that should be ignored.
+_IGNORED_FILE_SUFFIXES = (
+ # Python temporaries
+ '.pyc',
+ '.pyo',
+ # Backups
+ '~',
+ # Emacs
+ '#',
+ # Vim
+ '.swp',
+ '.swo',
+)
+
+
+def ignore_file(filename):
+ """Report whether a file should not be watched."""
+ return any(filename.endswith(suffix) for suffix in _IGNORED_FILE_SUFFIXES)
def remove_ignored_dirs(dirs):
diff --git a/google/appengine/tools/devappserver2/wsgi_server.py b/google/appengine/tools/devappserver2/wsgi_server.py
index bd64ad5..14fd3f8 100644
--- a/google/appengine/tools/devappserver2/wsgi_server.py
+++ b/google/appengine/tools/devappserver2/wsgi_server.py
@@ -37,6 +37,17 @@
_HAS_POLL = hasattr(select, 'poll')
+# TODO: the only reason we need to timeout is to pick up added or remove
+# descriptors. But AFAICT, we only add descriptors at startup and remove them at
+# shutdown so for the bulk of the run, the timeout is useless and just simply
+# wastes CPU. For startup, if we wait to start the thread until after all
+# WSGI servers are created, we are good (although may need to be careful in the
+# runtime instances depending on when servers are created relative to the
+# sandbox being enabled). For shutdown, more research is needed (one idea is
+# simply not remove descriptors as the process is about to exit).
+_READINESS_TIMEOUT_SECONDS = 1
+_SECONDS_TO_MILLISECONDS = 1000
+
# Due to reports of failure to find a consistent port, trying a higher value
# to see if that reduces the problem sufficiently. If it doesn't we can try
# increasing it (on my circa 2010 desktop, it takes about 1/2 second per 1024
@@ -145,14 +156,16 @@
poll = select.poll()
for fd in fds:
poll.register(fd, select.POLLIN)
- ready_file_descriptors = [fd for fd, _ in poll.poll(1)]
+ ready_file_descriptors = [fd for fd, _ in poll.poll(
+ _READINESS_TIMEOUT_SECONDS * _SECONDS_TO_MILLISECONDS)]
else:
- ready_file_descriptors, _, _ = select.select(fds, [], [], 1)
+ ready_file_descriptors, _, _ = select.select(fds, [], [],
+ _READINESS_TIMEOUT_SECONDS)
for fd in ready_file_descriptors:
fd_to_callback[fd]()
else:
# select([], [], [], 1) is not supported on Windows.
- time.sleep(1)
+ time.sleep(_READINESS_TIMEOUT_SECONDS)
_SELECT_THREAD = SelectThread()
_SELECT_THREAD.start()
diff --git a/google/appengine/tools/devappserver2/wsgi_server_test.py b/google/appengine/tools/devappserver2/wsgi_server_test.py
index f8b086e..8187d93 100644
--- a/google/appengine/tools/devappserver2/wsgi_server_test.py
+++ b/google/appengine/tools/devappserver2/wsgi_server_test.py
@@ -262,7 +262,7 @@
select.poll().AndReturn(poll)
poll.register(s.fileno(), select.POLLIN)
- poll.poll(1).AndReturn([(s.fileno(), select.POLLIN)])
+ poll.poll(1000).AndReturn([(s.fileno(), select.POLLIN)])
callback()
self.mox.ReplayAll()
@@ -288,7 +288,7 @@
select.poll().AndReturn(poll)
poll.register(s.fileno(), select.POLLIN)
- poll.poll(1).AndReturn([])
+ poll.poll(1000).AndReturn([])
self.mox.ReplayAll()
self.select_thread.add_socket(s, callback)
diff --git a/google/appengine/tools/endpointscfg.py b/google/appengine/tools/endpointscfg.py
index 3a00283..d754188 100644
--- a/google/appengine/tools/endpointscfg.py
+++ b/google/appengine/tools/endpointscfg.py
@@ -33,7 +33,7 @@
The get_client_lib subcommand does both of the above commands at once.
Example:
- endpointscfg.py get_client_lib java -o . -f rest postservice.GreetingsV1
+ endpointscfg.py get_client_lib java -o . postservice.GreetingsV1
The gen_api_config command outputs an .api configuration file for a service.
@@ -60,6 +60,7 @@
from endpoints import api_config
from protorpc import remote
+import yaml
from google.appengine.tools.devappserver2 import api_server
@@ -116,7 +117,8 @@
return path
-def GenApiConfig(service_class_names, generator=None, hostname=None):
+def GenApiConfig(service_class_names, generator=None, hostname=None,
+ application_path=None):
"""Write an API configuration for endpoints annotated ProtoRPC services.
Args:
@@ -126,6 +128,7 @@
hostname: A string hostname which will be used as the default version
hostname. If no hostname is specificied in the @endpoints.api decorator,
this value is the fallback. Defaults to None.
+ application_path: A string with the path to the AppEngine application.
Raises:
TypeError: If any service classes don't inherit from remote.Service.
@@ -152,12 +155,16 @@
[])
services.append(service)
+
+
+ app_yaml_hostname = _GetAppYamlHostname(application_path)
+
service_map = collections.OrderedDict()
generator = generator or api_config.ApiConfigGenerator()
for api_info, services in api_service_map.iteritems():
- hostname = services[0].api_info.hostname or hostname
+ hostname = services[0].api_info.hostname or hostname or app_yaml_hostname
service_map['%s-%s' % api_info] = generator.pretty_print_config_to_json(
@@ -166,8 +173,72 @@
return service_map
-def GenDiscoveryDoc(service_class_names, doc_format,
- output_path, hostname=None):
+def _GetAppYamlHostname(application_path, open_func=open):
+ """Build the hostname for this app based on the name in app.yaml.
+
+ Args:
+ application_path: A string with the path to the AppEngine application. This
+ should be the directory containing the app.yaml file.
+ open_func: Function to call to open a file. Used to override the default
+ open function in unit tests.
+
+ Returns:
+ A hostname, usually in the form of "myapp.appspot.com", based on the
+ application name in the app.yaml file. If the file can't be found or
+ there's a problem building the name, this will return None.
+ """
+ try:
+ app_yaml_file = open_func(os.path.join(application_path or '.', 'app.yaml'))
+ config = yaml.safe_load(app_yaml_file.read())
+ except IOError:
+
+ return None
+
+ application = config.get('application')
+ if not application:
+ return None
+
+ if ':' in application:
+
+ return None
+
+
+ tilde_index = application.rfind('~')
+ if tilde_index >= 0:
+ application = application[tilde_index + 1:]
+ if not application:
+ return None
+
+ return '%s.appspot.com' % application
+
+
+def _FetchDiscoveryDoc(config, doc_format):
+ """Fetch discovery documents generated from a cloud service.
+
+ Args:
+ config: An API config.
+ doc_format: The requested format for the discovery doc. (rest|rpc)
+
+ Raises:
+ ServerRequestException: If fetching the generated discovery doc fails.
+
+ Returns:
+ A list of discovery doc strings.
+ """
+ body = json.dumps({'config': config}, indent=2, sort_keys=True)
+ request = urllib2.Request(DISCOVERY_DOC_BASE + doc_format, body)
+ request.add_header('content-type', 'application/json')
+
+ try:
+ with contextlib.closing(urllib2.urlopen(request)) as response:
+ return response.read()
+ except urllib2.HTTPError, error:
+ raise ServerRequestException(error)
+
+
+def _GenDiscoveryDoc(service_class_names, doc_format,
+ output_path, hostname=None,
+ application_path=None):
"""Write discovery documents generated from a cloud service to file.
Args:
@@ -177,6 +248,7 @@
hostname: A string hostname which will be used as the default version
hostname. If no hostname is specificied in the @endpoints.api decorator,
this value is the fallback. Defaults to None.
+ application_path: A string containing the path to the AppEngine app.
Raises:
ServerRequestException: If fetching the generated discovery doc fails.
@@ -185,24 +257,17 @@
A list of discovery doc filenames.
"""
output_files = []
- service_configs = GenApiConfig(service_class_names, hostname=hostname)
+ service_configs = GenApiConfig(service_class_names, hostname=hostname,
+ application_path=application_path)
for api_name_version, config in service_configs.iteritems():
- body = json.dumps({'config': config}, indent=2, sort_keys=True)
- request = urllib2.Request(DISCOVERY_DOC_BASE + doc_format, body)
- request.add_header('content-type', 'application/json')
-
- try:
- with contextlib.closing(urllib2.urlopen(request)) as response:
- content = response.read()
- discovery_name = api_name_version + '.discovery'
- output_files.append(_WriteFile(output_path, discovery_name, content))
- except urllib2.HTTPError, error:
- raise ServerRequestException(error)
+ discovery_doc = _FetchDiscoveryDoc(config, doc_format)
+ discovery_name = api_name_version + '.discovery'
+ output_files.append(_WriteFile(output_path, discovery_name, discovery_doc))
return output_files
-def GenClientLib(discovery_path, language, output_path, build_system):
+def _GenClientLib(discovery_path, language, output_path, build_system):
"""Write a client library from a discovery doc, using a cloud service to file.
Args:
@@ -225,8 +290,8 @@
client_name = re.sub(r'\.discovery$', '.zip',
os.path.basename(discovery_path))
- _GenClientLibFromContents(discovery_doc, language, output_path, build_system,
- client_name)
+ return _GenClientLibFromContents(discovery_doc, language, output_path,
+ build_system, client_name)
def _GenClientLibFromContents(discovery_doc, language, output_path,
@@ -260,32 +325,33 @@
raise ServerRequestException(error)
-def GetClientLib(service_class_names, doc_format, language,
- output_path, build_system, hostname=None):
- """Fetch discovery documents and client libraries from a cloud service.
+def _GetClientLib(service_class_names, language, output_path, build_system,
+ hostname=None, application_path=None):
+ """Fetch client libraries from a cloud service.
Args:
service_class_names: A list of fully qualified ProtoRPC service names.
- doc_format: The requested format for the discovery doc. (rest|rpc)
language: The client library language to generate. (java)
output_path: The directory to output the discovery docs to.
build_system: The target build system for the client library language.
hostname: A string hostname which will be used as the default version
hostname. If no hostname is specificied in the @endpoints.api decorator,
this value is the fallback. Defaults to None.
+ application_path: A string containing the path to the AppEngine app.
Returns:
- A tuple (discovery_files, client_libs):
- discovery_files: A list of paths to discovery documents.
- client_libs: A list of paths to client libraries.
+ A list of paths to client libraries.
"""
- discovery_files = GenDiscoveryDoc(service_class_names, doc_format,
- output_path, hostname=hostname)
client_libs = []
- for discovery_path in discovery_files:
+ service_configs = GenApiConfig(service_class_names, hostname=hostname,
+ application_path=application_path)
+ for api_name_version, config in service_configs.iteritems():
+ discovery_doc = _FetchDiscoveryDoc(config, 'rest')
+ client_name = api_name_version + '.zip'
client_libs.append(
- GenClientLib(discovery_path, language, output_path, build_system))
- return discovery_files, client_libs
+ _GenClientLibFromContents(discovery_doc, language, output_path,
+ build_system, client_name))
+ return client_libs
def _GenApiConfigCallback(args, api_func=GenApiConfig):
@@ -296,44 +362,34 @@
api_func: A function that generates and returns an API configuration
for a list of services.
"""
- service_class_names, output_path, hostname = (
- args.service, args.output, args.hostname)
- service_configs = api_func(service_class_names, hostname=hostname)
+ service_configs = api_func(args.service,
+ hostname=args.hostname,
+ application_path=args.application)
for api_name_version, config in service_configs.iteritems():
api_name = api_name_version + '.api'
- _WriteFile(output_path, api_name, config)
+ _WriteFile(args.output, api_name, config)
-def _GetClientLibCallback(args,
- client_func=GetClientLib):
+def _GetClientLibCallback(args, client_func=_GetClientLib):
"""Generate discovery docs and client libraries to files.
Args:
args: An argparse.Namespace object to extract parameters from.
client_func: A function that generates client libraries and stores them to
- files, accepting a list of service names, a discovery doc format, a client
- library language, an output directory, a build system for the client
- library language, and a hostname.
+ files, accepting a list of service names, a client library language,
+ an output directory, a build system for the client library language, and
+ a hostname.
"""
- service_class_names = args.service
- doc_format = args.format
- language = args.language
- output_path = args.output
- hostname = args.hostname
- build_system = args.build_system
- discovery_paths, client_paths = client_func(
- service_class_names, doc_format, language, output_path, build_system,
- hostname=hostname)
-
- for discovery_path in discovery_paths:
- print 'API discovery document written to %s' % discovery_path
+ client_paths = client_func(
+ args.service, args.language, args.output, args.build_system,
+ hostname=args.hostname, application_path=args.application)
for client_path in client_paths:
print 'API client library written to %s' % client_path
-def _GenDiscoveryDocCallback(args, discovery_func=GenDiscoveryDoc):
+def _GenDiscoveryDocCallback(args, discovery_func=_GenDiscoveryDoc):
"""Generate discovery docs to files.
Args:
@@ -342,15 +398,14 @@
files, accepting a list of service names, a discovery doc format, and an
output directory.
"""
- services, doc_format, output_path, hostname = (
- args.service, args.format, args.output, args.hostname)
- discovery_paths = discovery_func(services, doc_format,
- output_path, hostname=hostname)
+ discovery_paths = discovery_func(args.service, args.format,
+ args.output, hostname=args.hostname,
+ application_path=args.application)
for discovery_path in discovery_paths:
print 'API discovery document written to %s' % discovery_path
-def _GenClientLibCallback(args, client_func=GenClientLib):
+def _GenClientLibCallback(args, client_func=_GenClientLib):
"""Generate a client library to file.
Args:
@@ -359,9 +414,8 @@
files, accepting a path to a discovery doc, a client library language, an
output directory, and a build system for the client library language.
"""
- discovery_path, language, output_path, build_system = (
- args.discovery_doc[0], args.language, args.output, args.build_system)
- client_path = client_func(discovery_path, language, output_path, build_system)
+ client_path = client_func(args.discovery_doc[0], args.language, args.output,
+ args.build_system)
print 'API client library written to %s' % client_path
@@ -422,8 +476,8 @@
'get_client_lib', help=('Generates discovery documents and client '
'libraries from service classes'))
get_client_lib.set_defaults(callback=_GetClientLibCallback)
- AddStandardOptions(get_client_lib, 'application', 'format', 'hostname',
- 'output', 'language', 'service', 'build_system')
+ AddStandardOptions(get_client_lib, 'application', 'hostname', 'output',
+ 'language', 'service', 'build_system')
get_discovery_doc = subparsers.add_parser(
'get_discovery_doc',
diff --git a/google/net/proto2/proto/descriptor_pb2.py b/google/net/proto2/proto/descriptor_pb2.py
index 47f79f9..6db4892 100644
--- a/google/net/proto2/proto/descriptor_pb2.py
+++ b/google/net/proto2/proto/descriptor_pb2.py
@@ -28,7 +28,7 @@
DESCRIPTOR = _descriptor.FileDescriptor(
name='net/proto2/proto/descriptor.proto',
package='proto2',
- serialized_pb='\n!net/proto2/proto/descriptor.proto\x12\x06proto2\">\n\x11\x46ileDescriptorSet\x12)\n\x04\x66ile\x18\x01 \x03(\x0b\x32\x1b.proto2.FileDescriptorProto\"\x95\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12-\n\x0cmessage_type\x18\x04 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x05 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12/\n\x07service\x18\x06 \x03(\x0b\x32\x1e.proto2.ServiceDescriptorProto\x12/\n\textension\x18\x07 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12$\n\x07options\x18\x08 \x01(\x0b\x32\x13.proto2.FileOptions\x12\x30\n\x10source_code_info\x18\t \x01(\x0b\x32\x16.proto2.SourceCodeInfo\"\xa5\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12/\n\textension\x18\x06 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12,\n\x0bnested_type\x18\x03 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x04 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12?\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32&.proto2.DescriptorProto.ExtensionRange\x12\x30\n\noneof_decl\x18\x08 \x03(\x0b\x32\x1c.proto2.OneofDescriptorProto\x12\'\n\x07options\x18\x07 \x01(\x0b\x32\x16.proto2.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\x8e\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x31\n\x05label\x18\x04 \x01(\x0e\x32\".proto2.FieldDescriptorProto.Label\x12/\n\x04type\x18\x05 \x01(\x0e\x32!.proto2.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12%\n\x07options\x18\x08 \x01(\x0b\x32\x14.proto2.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"z\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x03(\x0b\x32 .proto2.EnumValueDescriptorProto\x12$\n\x07options\x18\x03 \x01(\x0b\x32\x13.proto2.EnumOptions\"c\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12)\n\x07options\x18\x03 \x01(\x0b\x32\x18.proto2.EnumValueOptions\"\xad\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\x06method\x18\x02 \x03(\x0b\x32\x1d.proto2.MethodDescriptorProto\x12-\n\x06stream\x18\x04 \x03(\x0b\x32\x1d.proto2.StreamDescriptorProto\x12\'\n\x07options\x18\x03 \x01(\x0b\x32\x16.proto2.ServiceOptions\"v\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.MethodOptions\"\x87\x01\n\x15StreamDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x63lient_message_type\x18\x02 \x01(\t\x12\x1b\n\x13server_message_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.StreamOptions\"\xba\t\n\x0b\x46ileOptions\x12\x19\n\x0e\x63\x63_api_version\x18\x02 \x01(\x05:\x01\x32\x12V\n\x14\x63\x63_api_compatibility\x18\x0f \x01(\x0e\x32&.proto2.FileOptions.CompatibilityLevel:\x10NO_COMPATIBILITY\x12\'\n\x19\x63\x63_proto_array_compatible\x18\x16 \x01(\x08:\x04true\x12\"\n\x14\x63\x63_utf8_verification\x18\x18 \x01(\x08:\x04true\x12$\n\x15\x63\x63_proto1_text_format\x18\x19 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x19\n\x0epy_api_version\x18\x04 \x01(\x05:\x01\x32\x12\x1b\n\x10java_api_version\x18\x05 \x01(\x05:\x01\x32\x12!\n\x13java_use_javaproto2\x18\x06 \x01(\x08:\x04true\x12\x1e\n\x10java_java5_enums\x18\x07 \x01(\x08:\x04true\x12)\n\x1ajava_generate_rpc_baseimpl\x18\r \x01(\x08:\x05\x66\x61lse\x12#\n\x14java_use_javastrings\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_alt_api_package\x18\x13 \x01(\t\x12\x33\n%java_enable_dual_generate_mutable_api\x18\x1a \x01(\x08:\x04true\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12=\n\x0coptimize_for\x18\t \x01(\x0e\x32 .proto2.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\x1a\n\x12javascript_package\x18\x0c \x01(\t\x12\x1a\n\x0fszl_api_version\x18\x0e \x01(\x05:\x01\x31\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"c\n\x12\x43ompatibilityLevel\x12\x14\n\x10NO_COMPATIBILITY\x10\x00\x12\x15\n\x11PROTO1_COMPATIBLE\x10\x64\x12 \n\x1c\x44\x45PRECATED_PROTO1_COMPATIBLE\x10\x32\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x02\n\x0eMessageOptions\x12+\n#experimental_java_message_interface\x18\x04 \x03(\t\x12+\n#experimental_java_builder_interface\x18\x05 \x03(\t\x12+\n#experimental_java_interface_extends\x18\x06 \x03(\t\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xf9\x04\n\x0c\x46ieldOptions\x12\x31\n\x05\x63type\x18\x01 \x01(\x0e\x32\x1a.proto2.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x31\n\x05jtype\x18\x04 \x01(\x0e\x32\x1a.proto2.FieldOptions.JType:\x06NORMAL\x12\x36\n\x06jstype\x18\x06 \x01(\x0e\x32\x1b.proto2.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14\x65xperimental_map_key\x18\t \x01(\t\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12<\n\x0fupgraded_option\x18\x0b \x03(\x0b\x32#.proto2.FieldOptions.UpgradedOption\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\x1a-\n\x0eUpgradedOption\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"<\n\x05JType\x12\n\n\x06NORMAL\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\x1c\n\x18\x45XPERIMENTAL_BYTE_BUFFER\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x99\x01\n\x0b\x45numOptions\x12\x13\n\x0bproto1_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"t\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xb6\x01\n\x0eServiceOptions\x12\x1d\n\x0emulticast_stub\x18\x14 \x01(\x08:\x05\x66\x61lse\x12#\n\x17\x66\x61ilure_detection_delay\x18\x10 \x01(\x01:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xa8\x07\n\rMethodOptions\x12\x35\n\x08protocol\x18\x07 \x01(\x0e\x32\x1e.proto2.MethodOptions.Protocol:\x03TCP\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12$\n\x15\x64uplicate_suppression\x18\t \x01(\x08:\x05\x66\x61lse\x12\x18\n\tfail_fast\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0e\x63lient_logging\x18\x0b \x01(\x11:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x0c \x01(\x11:\x03\x32\x35\x36\x12\x41\n\x0esecurity_level\x18\r \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x43\n\x0fresponse_format\x18\x0f \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x42\n\x0erequest_format\x18\x11 \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x13\n\x0bstream_type\x18\x12 \x01(\t\x12\x16\n\x0esecurity_label\x18\x13 \x01(\t\x12\x18\n\x10\x63lient_streaming\x18\x14 \x01(\x08\x12\x18\n\x10server_streaming\x18\x15 \x01(\x08\x12\x1a\n\x12legacy_stream_type\x18\x16 \x01(\t\x12\x1a\n\x12legacy_result_type\x18\x17 \x01(\t\x12(\n\x1clegacy_client_initial_tokens\x18\x18 \x01(\x03:\x02-1\x12(\n\x1clegacy_server_initial_tokens\x18\x19 \x01(\x03:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\x1c\n\x08Protocol\x12\x07\n\x03TCP\x10\x00\x12\x07\n\x03UDP\x10\x01\"e\n\rSecurityLevel\x12\x08\n\x04NONE\x10\x00\x12\r\n\tINTEGRITY\x10\x01\x12\x19\n\x15PRIVACY_AND_INTEGRITY\x10\x02\x12 \n\x1cSTRONG_PRIVACY_AND_INTEGRITY\x10\x03\"0\n\x06\x46ormat\x12\x10\n\x0cUNCOMPRESSED\x10\x00\x12\x14\n\x10ZIPPY_COMPRESSED\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xde\x03\n\rStreamOptions\x12!\n\x15\x63lient_initial_tokens\x18\x01 \x01(\x03:\x02-1\x12!\n\x15server_initial_tokens\x18\x02 \x01(\x03:\x02-1\x12<\n\ntoken_unit\x18\x03 \x01(\x0e\x32\x1f.proto2.StreamOptions.TokenUnit:\x07MESSAGE\x12\x41\n\x0esecurity_level\x18\x04 \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x16\n\x0esecurity_label\x18\x05 \x01(\t\x12\x1b\n\x0e\x63lient_logging\x18\x06 \x01(\x05:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x07 \x01(\x05:\x03\x32\x35\x36\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12\x18\n\tfail_fast\x18\t \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\"\n\tTokenUnit\x12\x0b\n\x07MESSAGE\x10\x00\x12\x08\n\x04\x42YTE\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x95\x02\n\x13UninterpretedOption\x12\x32\n\x04name\x18\x02 \x03(\x0b\x32$.proto2.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xa8\x01\n\x0eSourceCodeInfo\x12\x31\n\x08location\x18\x01 \x03(\x0b\x32\x1f.proto2.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB)\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01')
+ serialized_pb='\n!net/proto2/proto/descriptor.proto\x12\x06proto2\">\n\x11\x46ileDescriptorSet\x12)\n\x04\x66ile\x18\x01 \x03(\x0b\x32\x1b.proto2.FileDescriptorProto\"\x95\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12-\n\x0cmessage_type\x18\x04 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x05 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12/\n\x07service\x18\x06 \x03(\x0b\x32\x1e.proto2.ServiceDescriptorProto\x12/\n\textension\x18\x07 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12$\n\x07options\x18\x08 \x01(\x0b\x32\x13.proto2.FileOptions\x12\x30\n\x10source_code_info\x18\t \x01(\x0b\x32\x16.proto2.SourceCodeInfo\"\xa5\x03\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12+\n\x05\x66ield\x18\x02 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12/\n\textension\x18\x06 \x03(\x0b\x32\x1c.proto2.FieldDescriptorProto\x12,\n\x0bnested_type\x18\x03 \x03(\x0b\x32\x17.proto2.DescriptorProto\x12.\n\tenum_type\x18\x04 \x03(\x0b\x32\x1b.proto2.EnumDescriptorProto\x12?\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32&.proto2.DescriptorProto.ExtensionRange\x12\x30\n\noneof_decl\x18\x08 \x03(\x0b\x32\x1c.proto2.OneofDescriptorProto\x12\'\n\x07options\x18\x07 \x01(\x0b\x32\x16.proto2.MessageOptions\x1a,\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"\x8e\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x31\n\x05label\x18\x04 \x01(\x0e\x32\".proto2.FieldDescriptorProto.Label\x12/\n\x04type\x18\x05 \x01(\x0e\x32!.proto2.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12%\n\x07options\x18\x08 \x01(\x0b\x32\x14.proto2.FieldOptions\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"$\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\"z\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x03(\x0b\x32 .proto2.EnumValueDescriptorProto\x12$\n\x07options\x18\x03 \x01(\x0b\x32\x13.proto2.EnumOptions\"c\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12)\n\x07options\x18\x03 \x01(\x0b\x32\x18.proto2.EnumValueOptions\"\xad\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\x06method\x18\x02 \x03(\x0b\x32\x1d.proto2.MethodDescriptorProto\x12-\n\x06stream\x18\x04 \x03(\x0b\x32\x1d.proto2.StreamDescriptorProto\x12\'\n\x07options\x18\x03 \x01(\x0b\x32\x16.proto2.ServiceOptions\"v\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.MethodOptions\"\x87\x01\n\x15StreamDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1b\n\x13\x63lient_message_type\x18\x02 \x01(\t\x12\x1b\n\x13server_message_type\x18\x03 \x01(\t\x12&\n\x07options\x18\x04 \x01(\x0b\x32\x15.proto2.StreamOptions\"\xba\t\n\x0b\x46ileOptions\x12\x19\n\x0e\x63\x63_api_version\x18\x02 \x01(\x05:\x01\x32\x12V\n\x14\x63\x63_api_compatibility\x18\x0f \x01(\x0e\x32&.proto2.FileOptions.CompatibilityLevel:\x10NO_COMPATIBILITY\x12\'\n\x19\x63\x63_proto_array_compatible\x18\x16 \x01(\x08:\x04true\x12\"\n\x14\x63\x63_utf8_verification\x18\x18 \x01(\x08:\x04true\x12$\n\x15\x63\x63_proto1_text_format\x18\x19 \x01(\x08:\x05\x66\x61lse\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x19\n\x0epy_api_version\x18\x04 \x01(\x05:\x01\x32\x12\x1b\n\x10java_api_version\x18\x05 \x01(\x05:\x01\x32\x12!\n\x13java_use_javaproto2\x18\x06 \x01(\x08:\x04true\x12\x1e\n\x10java_java5_enums\x18\x07 \x01(\x08:\x04true\x12)\n\x1ajava_generate_rpc_baseimpl\x18\r \x01(\x08:\x05\x66\x61lse\x12#\n\x14java_use_javastrings\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14java_alt_api_package\x18\x13 \x01(\t\x12\x33\n%java_enable_dual_generate_mutable_api\x18\x1a \x01(\x08:\x04true\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12,\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08:\x05\x66\x61lse\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12=\n\x0coptimize_for\x18\t \x01(\x0e\x32 .proto2.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\x1a\n\x12javascript_package\x18\x0c \x01(\t\x12\x1a\n\x0fszl_api_version\x18\x0e \x01(\x05:\x01\x31\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"c\n\x12\x43ompatibilityLevel\x12\x14\n\x10NO_COMPATIBILITY\x10\x00\x12\x15\n\x11PROTO1_COMPATIBLE\x10\x64\x12 \n\x1c\x44\x45PRECATED_PROTO1_COMPATIBLE\x10\x32\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x02\n\x0eMessageOptions\x12+\n#experimental_java_message_interface\x18\x04 \x03(\t\x12+\n#experimental_java_builder_interface\x18\x05 \x03(\t\x12+\n#experimental_java_interface_extends\x18\x06 \x03(\t\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xf9\x04\n\x0c\x46ieldOptions\x12\x31\n\x05\x63type\x18\x01 \x01(\x0e\x32\x1a.proto2.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12\x31\n\x05jtype\x18\x04 \x01(\x0e\x32\x1a.proto2.FieldOptions.JType:\x06NORMAL\x12\x36\n\x06jstype\x18\x06 \x01(\x0e\x32\x1b.proto2.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x1c\n\x14\x65xperimental_map_key\x18\t \x01(\t\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12<\n\x0fupgraded_option\x18\x0b \x03(\x0b\x32#.proto2.FieldOptions.UpgradedOption\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\x1a-\n\x0eUpgradedOption\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"<\n\x05JType\x12\n\n\x06NORMAL\x10\x00\x12\t\n\x05\x42YTES\x10\x01\x12\x1c\n\x18\x45XPERIMENTAL_BYTE_BUFFER\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x99\x01\n\x0b\x45numOptions\x12\x13\n\x0bproto1_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"t\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xb6\x01\n\x0eServiceOptions\x12\x1d\n\x0emulticast_stub\x18\x14 \x01(\x08:\x05\x66\x61lse\x12#\n\x17\x66\x61ilure_detection_delay\x18\x10 \x01(\x01:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd1\x07\n\rMethodOptions\x12\x35\n\x08protocol\x18\x07 \x01(\x0e\x32\x1e.proto2.MethodOptions.Protocol:\x03TCP\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12$\n\x15\x64uplicate_suppression\x18\t \x01(\x08:\x05\x66\x61lse\x12\x18\n\tfail_fast\x18\n \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0e\x63lient_logging\x18\x0b \x01(\x11:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x0c \x01(\x11:\x03\x32\x35\x36\x12\x41\n\x0esecurity_level\x18\r \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x43\n\x0fresponse_format\x18\x0f \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x42\n\x0erequest_format\x18\x11 \x01(\x0e\x32\x1c.proto2.MethodOptions.Format:\x0cUNCOMPRESSED\x12\x13\n\x0bstream_type\x18\x12 \x01(\t\x12\x16\n\x0esecurity_label\x18\x13 \x01(\t\x12\x18\n\x10\x63lient_streaming\x18\x14 \x01(\x08\x12\x18\n\x10server_streaming\x18\x15 \x01(\x08\x12\x1a\n\x12legacy_stream_type\x18\x16 \x01(\t\x12\x1a\n\x12legacy_result_type\x18\x17 \x01(\t\x12(\n\x1clegacy_client_initial_tokens\x18\x18 \x01(\x03:\x02-1\x12(\n\x1clegacy_server_initial_tokens\x18\x19 \x01(\x03:\x02-1\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\x1c\n\x08Protocol\x12\x07\n\x03TCP\x10\x00\x12\x07\n\x03UDP\x10\x01\"e\n\rSecurityLevel\x12\x08\n\x04NONE\x10\x00\x12\r\n\tINTEGRITY\x10\x01\x12\x19\n\x15PRIVACY_AND_INTEGRITY\x10\x02\x12 \n\x1cSTRONG_PRIVACY_AND_INTEGRITY\x10\x03\"0\n\x06\x46ormat\x12\x10\n\x0cUNCOMPRESSED\x10\x00\x12\x14\n\x10ZIPPY_COMPRESSED\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x87\x04\n\rStreamOptions\x12!\n\x15\x63lient_initial_tokens\x18\x01 \x01(\x03:\x02-1\x12!\n\x15server_initial_tokens\x18\x02 \x01(\x03:\x02-1\x12<\n\ntoken_unit\x18\x03 \x01(\x0e\x32\x1f.proto2.StreamOptions.TokenUnit:\x07MESSAGE\x12\x41\n\x0esecurity_level\x18\x04 \x01(\x0e\x32#.proto2.MethodOptions.SecurityLevel:\x04NONE\x12\x16\n\x0esecurity_label\x18\x05 \x01(\t\x12\x1b\n\x0e\x63lient_logging\x18\x06 \x01(\x05:\x03\x32\x35\x36\x12\x1b\n\x0eserver_logging\x18\x07 \x01(\x05:\x03\x32\x35\x36\x12\x14\n\x08\x64\x65\x61\x64line\x18\x08 \x01(\x01:\x02-1\x12\x18\n\tfail_fast\x18\t \x01(\x08:\x05\x66\x61lse\x12\'\n\x18\x65nd_user_creds_requested\x18\n \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12:\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32\x1b.proto2.UninterpretedOption\"\"\n\tTokenUnit\x12\x0b\n\x07MESSAGE\x10\x00\x12\x08\n\x04\x42YTE\x10\x01*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x95\x02\n\x13UninterpretedOption\x12\x32\n\x04name\x18\x02 \x03(\x0b\x32$.proto2.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xa8\x01\n\x0eSourceCodeInfo\x12\x31\n\x08location\x18\x01 \x03(\x0b\x32\x1f.proto2.SourceCodeInfo.Location\x1a\x63\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\tB)\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01')
@@ -284,8 +284,8 @@
],
containing_type=None,
options=None,
- serialized_start=5688,
- serialized_end=5716,
+ serialized_start=5729,
+ serialized_end=5757,
)
_METHODOPTIONS_SECURITYLEVEL = _descriptor.EnumDescriptor(
@@ -313,8 +313,8 @@
],
containing_type=None,
options=None,
- serialized_start=5718,
- serialized_end=5819,
+ serialized_start=5759,
+ serialized_end=5860,
)
_METHODOPTIONS_FORMAT = _descriptor.EnumDescriptor(
@@ -334,8 +334,8 @@
],
containing_type=None,
options=None,
- serialized_start=5821,
- serialized_end=5869,
+ serialized_start=5862,
+ serialized_end=5910,
)
_STREAMOPTIONS_TOKENUNIT = _descriptor.EnumDescriptor(
@@ -355,8 +355,8 @@
],
containing_type=None,
options=None,
- serialized_start=6316,
- serialized_end=6350,
+ serialized_start=6398,
+ serialized_end=6432,
)
@@ -1521,105 +1521,112 @@
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='client_logging', full_name='proto2.MethodOptions.client_logging', index=4,
+ name='end_user_creds_requested', full_name='proto2.MethodOptions.end_user_creds_requested', index=4,
+ number=26, type=8, cpp_type=7, label=1,
+ has_default_value=True, default_value=False,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='client_logging', full_name='proto2.MethodOptions.client_logging', index=5,
number=11, type=17, cpp_type=1, label=1,
has_default_value=True, default_value=256,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='server_logging', full_name='proto2.MethodOptions.server_logging', index=5,
+ name='server_logging', full_name='proto2.MethodOptions.server_logging', index=6,
number=12, type=17, cpp_type=1, label=1,
has_default_value=True, default_value=256,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='security_level', full_name='proto2.MethodOptions.security_level', index=6,
+ name='security_level', full_name='proto2.MethodOptions.security_level', index=7,
number=13, type=14, cpp_type=8, label=1,
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='response_format', full_name='proto2.MethodOptions.response_format', index=7,
+ name='response_format', full_name='proto2.MethodOptions.response_format', index=8,
number=15, type=14, cpp_type=8, label=1,
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='request_format', full_name='proto2.MethodOptions.request_format', index=8,
+ name='request_format', full_name='proto2.MethodOptions.request_format', index=9,
number=17, type=14, cpp_type=8, label=1,
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='stream_type', full_name='proto2.MethodOptions.stream_type', index=9,
+ name='stream_type', full_name='proto2.MethodOptions.stream_type', index=10,
number=18, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='security_label', full_name='proto2.MethodOptions.security_label', index=10,
+ name='security_label', full_name='proto2.MethodOptions.security_label', index=11,
number=19, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='client_streaming', full_name='proto2.MethodOptions.client_streaming', index=11,
+ name='client_streaming', full_name='proto2.MethodOptions.client_streaming', index=12,
number=20, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='server_streaming', full_name='proto2.MethodOptions.server_streaming', index=12,
+ name='server_streaming', full_name='proto2.MethodOptions.server_streaming', index=13,
number=21, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='legacy_stream_type', full_name='proto2.MethodOptions.legacy_stream_type', index=13,
+ name='legacy_stream_type', full_name='proto2.MethodOptions.legacy_stream_type', index=14,
number=22, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='legacy_result_type', full_name='proto2.MethodOptions.legacy_result_type', index=14,
+ name='legacy_result_type', full_name='proto2.MethodOptions.legacy_result_type', index=15,
number=23, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=unicode("", "utf-8"),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='legacy_client_initial_tokens', full_name='proto2.MethodOptions.legacy_client_initial_tokens', index=15,
+ name='legacy_client_initial_tokens', full_name='proto2.MethodOptions.legacy_client_initial_tokens', index=16,
number=24, type=3, cpp_type=2, label=1,
has_default_value=True, default_value=-1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='legacy_server_initial_tokens', full_name='proto2.MethodOptions.legacy_server_initial_tokens', index=16,
+ name='legacy_server_initial_tokens', full_name='proto2.MethodOptions.legacy_server_initial_tokens', index=17,
number=25, type=3, cpp_type=2, label=1,
has_default_value=True, default_value=-1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='deprecated', full_name='proto2.MethodOptions.deprecated', index=17,
+ name='deprecated', full_name='proto2.MethodOptions.deprecated', index=18,
number=33, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='uninterpreted_option', full_name='proto2.MethodOptions.uninterpreted_option', index=18,
+ name='uninterpreted_option', full_name='proto2.MethodOptions.uninterpreted_option', index=19,
number=999, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
@@ -1638,7 +1645,7 @@
is_extendable=True,
extension_ranges=[(1000, 536870912), ],
serialized_start=4944,
- serialized_end=5880,
+ serialized_end=5921,
)
@@ -1713,14 +1720,21 @@
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='deprecated', full_name='proto2.StreamOptions.deprecated', index=9,
+ name='end_user_creds_requested', full_name='proto2.StreamOptions.end_user_creds_requested', index=9,
+ number=10, type=8, cpp_type=7, label=1,
+ has_default_value=True, default_value=False,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='deprecated', full_name='proto2.StreamOptions.deprecated', index=10,
number=33, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
- name='uninterpreted_option', full_name='proto2.StreamOptions.uninterpreted_option', index=10,
+ name='uninterpreted_option', full_name='proto2.StreamOptions.uninterpreted_option', index=11,
number=999, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
@@ -1736,8 +1750,8 @@
options=None,
is_extendable=True,
extension_ranges=[(1000, 536870912), ],
- serialized_start=5883,
- serialized_end=6361,
+ serialized_start=5924,
+ serialized_end=6443,
)
@@ -1771,8 +1785,8 @@
options=None,
is_extendable=False,
extension_ranges=[],
- serialized_start=6590,
- serialized_end=6641,
+ serialized_start=6672,
+ serialized_end=6723,
)
_UNINTERPRETEDOPTION = _descriptor.Descriptor(
@@ -1840,8 +1854,8 @@
options=None,
is_extendable=False,
extension_ranges=[],
- serialized_start=6364,
- serialized_end=6641,
+ serialized_start=6446,
+ serialized_end=6723,
)
@@ -1889,8 +1903,8 @@
options=None,
is_extendable=False,
extension_ranges=[],
- serialized_start=6713,
- serialized_end=6812,
+ serialized_start=6795,
+ serialized_end=6894,
)
_SOURCECODEINFO = _descriptor.Descriptor(
@@ -1916,8 +1930,8 @@
options=None,
is_extendable=False,
extension_ranges=[],
- serialized_start=6644,
- serialized_end=6812,
+ serialized_start=6726,
+ serialized_end=6894,
)
_FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO
diff --git a/google/net/proto2/python/public/descriptor.py b/google/net/proto2/python/public/descriptor.py
index 10959e1..e035105 100644
--- a/google/net/proto2/python/public/descriptor.py
+++ b/google/net/proto2/python/public/descriptor.py
@@ -652,7 +652,7 @@
serialized_pb: (str) Byte string of serialized
descriptor_pb2.FileDescriptorProto.
dependencies: List of other files this file depends on.
- messages_types_by_name: Dict of message names of their descriptors.
+ message_types_by_name: Dict of message names of their descriptors.
enum_types_by_name: Dict of enum names and their descriptors.
extensions_by_name: Dict of extension names and their descriptors.
"""
diff --git a/google/net/proto2/python/public/text_encoding.py b/google/net/proto2/python/public/text_encoding.py
index 6519b32..f586fbf 100644
--- a/google/net/proto2/python/public/text_encoding.py
+++ b/google/net/proto2/python/public/text_encoding.py
@@ -19,6 +19,28 @@
import re
+_cescape_utf8_to_str = [chr(i) for i in xrange(0, 256)]
+_cescape_utf8_to_str[9] = r'\t'
+_cescape_utf8_to_str[10] = r'\n'
+_cescape_utf8_to_str[13] = r'\r'
+_cescape_utf8_to_str[39] = r"\'"
+
+_cescape_utf8_to_str[34] = r'\"'
+_cescape_utf8_to_str[92] = r'\\'
+
+
+_cescape_byte_to_str = ([r'\%03o' % i for i in xrange(0, 32)] +
+ [chr(i) for i in xrange(32, 127)] +
+ [r'\%03o' % i for i in xrange(127, 256)])
+_cescape_byte_to_str[9] = r'\t'
+_cescape_byte_to_str[10] = r'\n'
+_cescape_byte_to_str[13] = r'\r'
+_cescape_byte_to_str[39] = r"\'"
+
+_cescape_byte_to_str[34] = r'\"'
+_cescape_byte_to_str[92] = r'\\'
+
+
def CEscape(text, as_utf8):
"""Escape a string for use in an ascii protocol buffer.
@@ -35,22 +57,9 @@
Escaped string
"""
- def EscapeChar(c):
- """Escape one character."""
- o = ord(c)
- if o == 10: return r'\n'
- if o == 13: return r'\r'
- if o == 9: return r'\t'
- if o == 39: return r"\'"
-
- if o == 34: return r'\"'
- if o == 92: return r'\\'
-
-
- if not as_utf8 and (o >= 127 or o < 32):
- return r'\%03o' % o
- return c
- return ''.join([EscapeChar(c) for c in text])
+ if as_utf8:
+ return ''.join(_cescape_utf8_to_str[ord(c)] for c in text)
+ return ''.join(_cescape_byte_to_str[ord(c)] for c in text)
_CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])')
diff --git a/google_sql.py b/google_sql.py
index fa956c5..b15365d 100644
--- a/google_sql.py
+++ b/google_sql.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/lib/cacerts/urlfetch_cacerts.txt b/lib/cacerts/urlfetch_cacerts.txt
index 7f9571c..fde914f 100644
--- a/lib/cacerts/urlfetch_cacerts.txt
+++ b/lib/cacerts/urlfetch_cacerts.txt
@@ -5984,34 +5984,6 @@
qAD38hw=
-----END CERTIFICATE-----
-subject= /C=pt/O=MULTICERT-CA/CN=MULTICERT-CA 02
-serial=07273CE5
------BEGIN CERTIFICATE-----
-MIIEITCCA4qgAwIBAgIEByc85TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
-UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
-cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
-b2JhbCBSb290MB4XDTA5MDYwMzEzNTAzN1oXDTEzMTAwMjEzNDkyMFowPjELMAkG
-A1UEBhMCcHQxFTATBgNVBAoTDE1VTFRJQ0VSVC1DQTEYMBYGA1UEAxMPTVVMVElD
-RVJULUNBIDAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3Q3a05I5
-4HOZM1bnZnSUXx1qL73+PtERgiIl+MbYasP+UL+l3yQEdplm1hBijpC6gPxHPXLo
-zP35tkSCb2v4TsdY6gE8h65UXZDdOiX/iBycG8165sLAy2NAlifPbMeKGjyI9Fea
-0Mt9aRfNXM+1SCyOihmHXDV4+v3m12IXj5+O/l5Rxif/bs7GPQbL4XAJT2cApnac
-7yD/3Y2Alk4/W5CBqnj4YGUArfErIaSPRaiGSEAZiES69+iXxp5LseCr0SZ4Kq+l
-Ugglz6M5EewcDMb7r3ECJM34pl/cuwlzjtANCJvNTiRyJ9ot8GT/6+5l/0Zf9BgB
-yJ8Hdy8oGo/iNwIDAQABo4IBbzCCAWswEgYDVR0TAQH/BAgwBgEB/wIBATBTBgNV
-HSAETDBKMEgGCSsGAQQBsT4BADA7MDkGCCsGAQUFBwIBFi1odHRwOi8vY3liZXJ0
-cnVzdC5vbW5pcm9vdC5jb20vcmVwb3NpdG9yeS5jZm0wDgYDVR0PAQH/BAQDAgEG
-MIGJBgNVHSMEgYEwf6F5pHcwdTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD0dURSBD
-b3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RFIEN5YmVyVHJ1c3QgU29sdXRpb25zLCBJ
-bmMuMSMwIQYDVQQDExpHVEUgQ3liZXJUcnVzdCBHbG9iYWwgUm9vdIICAaUwRQYD
-VR0fBD4wPDA6oDigNoY0aHR0cDovL3d3dy5wdWJsaWMtdHJ1c3QuY29tL2NnaS1i
-aW4vQ1JMLzIwMTgvY2RwLmNybDAdBgNVHQ4EFgQUHcO5iKUYvmCnLKZjymYq/Awn
-wb0wDQYJKoZIhvcNAQEFBQADgYEAZ16j8FV3R0Uca2iOvdkZxcx/X67ve999imk5
-Fv2DI0NDP8IQxayBACt074v8ALQxVtcRLnXpK2aQ9CEsS9/FSDZm9eC7cV7fz+Xx
-3iv5ePSU/EKnXO+B3REqPaLSRrCc/9VVv+RRfGkRUWhQHrgcU6E9EC3Jp7PKSGaF
-EKqbdJU=
------END CERTIFICATE-----
-
subject= /C=US/ST=Illinois/L=Chicago/O=Trustwave Holdings, Inc./CN=Trustwave Domain Validation CA, Level 1/emailAddress=ca@trustwave.com
serial=4786F656
-----BEGIN CERTIFICATE-----
diff --git a/lib/django-1.5/AUTHORS b/lib/django-1.5/AUTHORS
index 070ec70..357533d 100644
--- a/lib/django-1.5/AUTHORS
+++ b/lib/django-1.5/AUTHORS
@@ -141,6 +141,7 @@
crankycoder@gmail.com
Paul Collier <paul@paul-collier.com>
Robert Coup
+ Brian Fabian Crain <http://www.bfc.do/>
Pete Crosier <pete.crosier@gmail.com>
Matt Croydon <http://www.postneo.com/>
Jure Cuhalev <gandalf@owca.info>
diff --git a/lib/django-1.5/LICENSE b/lib/django-1.5/LICENSE
index 5f4f225..d0c3f28 100644
--- a/lib/django-1.5/LICENSE
+++ b/lib/django-1.5/LICENSE
@@ -4,10 +4,10 @@
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice,
+ 1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
+
+ 2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
diff --git a/lib/django-1.5/MANIFEST.in b/lib/django-1.5/MANIFEST.in
index fbda541..081bcb9 100644
--- a/lib/django-1.5/MANIFEST.in
+++ b/lib/django-1.5/MANIFEST.in
@@ -10,7 +10,9 @@
recursive-include scripts *
recursive-include extras *
recursive-include tests *
+recursive-include django/conf/app_template *
recursive-include django/conf/locale *
+recursive-include django/conf/project_template *
recursive-include django/contrib/*/locale *
recursive-include django/contrib/admin/templates *
recursive-include django/contrib/admin/static *
diff --git a/lib/django-1.5/PKG-INFO b/lib/django-1.5/PKG-INFO
index 4357719..5b0db4f 100644
--- a/lib/django-1.5/PKG-INFO
+++ b/lib/django-1.5/PKG-INFO
@@ -1,12 +1,12 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
Name: Django
-Version: 1.5
+Version: 1.5.4
Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
Home-page: http://www.djangoproject.com/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD
-Download-URL: https://www.djangoproject.com/m/releases/1.5/Django-1.5.tar.gz
+Download-URL: https://www.djangoproject.com/m/releases/1.5/Django-1.5.4.tar.gz
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
@@ -16,8 +16,10 @@
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Topic :: Internet :: WWW/HTTP
diff --git a/lib/django-1.5/django/__init__.py b/lib/django-1.5/django/__init__.py
index adf74a5..00166a4 100644
--- a/lib/django-1.5/django/__init__.py
+++ b/lib/django-1.5/django/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (1, 5, 0, 'final', 0)
+VERSION = (1, 5, 4, 'final', 0)
def get_version(*args, **kwargs):
# Don't litter django/__init__.py with all the get_version stuff.
diff --git a/lib/django-1.5/django/conf/__init__.py b/lib/django-1.5/django/conf/__init__.py
index b00c8d5..051ab70 100644
--- a/lib/django-1.5/django/conf/__init__.py
+++ b/lib/django-1.5/django/conf/__init__.py
@@ -8,6 +8,7 @@
import logging
import os
+import sys
import time # Needed for Windows
import warnings
@@ -56,14 +57,15 @@
"""
Setup logging from LOGGING_CONFIG and LOGGING settings.
"""
- try:
- # Route warnings through python logging
- logging.captureWarnings(True)
- # Allow DeprecationWarnings through the warnings filters
- warnings.simplefilter("default", DeprecationWarning)
- except AttributeError:
- # No captureWarnings on Python 2.6, DeprecationWarnings are on anyway
- pass
+ if not sys.warnoptions:
+ try:
+ # Route warnings through python logging
+ logging.captureWarnings(True)
+ # Allow DeprecationWarnings through the warnings filters
+ warnings.simplefilter("default", DeprecationWarning)
+ except AttributeError:
+ # No captureWarnings on Python 2.6, DeprecationWarnings are on anyway
+ pass
if self.LOGGING_CONFIG:
from django.utils.log import DEFAULT_LOGGING
diff --git a/lib/django-1.5/django/conf/global_settings.py b/lib/django-1.5/django/conf/global_settings.py
index d4d4732..4cf2ba7 100644
--- a/lib/django-1.5/django/conf/global_settings.py
+++ b/lib/django-1.5/django/conf/global_settings.py
@@ -467,6 +467,7 @@
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether a user's session cookie expires when the Web browser is closed.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If None, the backend will use a sensible default.
+SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' # class to serialize session data
#########
# CACHE #
diff --git a/lib/django-1.5/django/conf/locale/af/LC_MESSAGES/django.po b/lib/django-1.5/django/conf/locale/af/LC_MESSAGES/django.po
index 9648f76..7d3ed8d 100644
--- a/lib/django-1.5/django/conf/locale/af/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/conf/locale/af/LC_MESSAGES/django.po
@@ -1,14 +1,14 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
-# Piet Delport <pjdelport@gmail.com>, 2012.
+# Piet Delport <pjdelport@gmail.com>, 2012-2013.
# Stephen Cox <scox@rems2.com>, 2011, 2012.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-02 08:47+0000\n"
+"PO-Revision-Date: 2013-03-26 06:50+0000\n"
"Last-Translator: Piet Delport <pjdelport@gmail.com>\n"
"Language-Team: Afrikaans (http://www.transifex.com/projects/p/django/"
"language/af/)\n"
@@ -20,7 +20,7 @@
#: conf/global_settings.py:48
msgid "Afrikaans"
-msgstr ""
+msgstr "Afrikaans"
#: conf/global_settings.py:49
msgid "Arabic"
@@ -36,7 +36,7 @@
#: conf/global_settings.py:52
msgid "Belarusian"
-msgstr ""
+msgstr "Wit-Russies"
#: conf/global_settings.py:53
msgid "Bengali"
@@ -44,7 +44,7 @@
#: conf/global_settings.py:54
msgid "Breton"
-msgstr ""
+msgstr "Bretons"
#: conf/global_settings.py:55
msgid "Bosnian"
@@ -84,7 +84,7 @@
#: conf/global_settings.py:64
msgid "Esperanto"
-msgstr ""
+msgstr "Esperanto"
#: conf/global_settings.py:65
msgid "Spanish"
@@ -104,7 +104,7 @@
#: conf/global_settings.py:69
msgid "Venezuelan Spanish"
-msgstr ""
+msgstr "Venezolaanse Spaans"
#: conf/global_settings.py:70
msgid "Estonian"
@@ -116,7 +116,7 @@
#: conf/global_settings.py:72
msgid "Persian"
-msgstr "Persiese"
+msgstr "Persies"
#: conf/global_settings.py:73
msgid "Finnish"
@@ -132,7 +132,7 @@
#: conf/global_settings.py:76
msgid "Irish"
-msgstr "Ierse"
+msgstr "Iers"
#: conf/global_settings.py:77
msgid "Galician"
@@ -144,7 +144,7 @@
#: conf/global_settings.py:79
msgid "Hindi"
-msgstr "Hindi"
+msgstr "Hindoe"
#: conf/global_settings.py:80
msgid "Croatian"
@@ -156,7 +156,7 @@
#: conf/global_settings.py:82
msgid "Interlingua"
-msgstr ""
+msgstr "Interlingua"
#: conf/global_settings.py:83
msgid "Indonesian"
@@ -180,7 +180,7 @@
#: conf/global_settings.py:88
msgid "Kazakh"
-msgstr ""
+msgstr "Kazakh"
#: conf/global_settings.py:89
msgid "Khmer"
@@ -196,7 +196,7 @@
#: conf/global_settings.py:92
msgid "Luxembourgish"
-msgstr ""
+msgstr "Luxemburgs"
#: conf/global_settings.py:93
msgid "Lithuanian"
@@ -224,7 +224,7 @@
#: conf/global_settings.py:99
msgid "Nepali"
-msgstr ""
+msgstr "Nepalees"
#: conf/global_settings.py:100
msgid "Dutch"
@@ -284,7 +284,7 @@
#: conf/global_settings.py:114
msgid "Swahili"
-msgstr ""
+msgstr "Swahili"
#: conf/global_settings.py:115
msgid "Tamil"
@@ -304,11 +304,11 @@
#: conf/global_settings.py:119
msgid "Tatar"
-msgstr ""
+msgstr "Tataars"
#: conf/global_settings.py:120
msgid "Udmurt"
-msgstr ""
+msgstr "Oedmoerts"
#: conf/global_settings.py:121
msgid "Ukrainian"
@@ -336,7 +336,7 @@
#: core/validators.py:104 forms/fields.py:464
msgid "Enter a valid email address."
-msgstr ""
+msgstr "Sleutel 'n geldige e-pos adres in."
#: core/validators.py:107 forms/fields.py:1013
msgid ""
@@ -508,7 +508,7 @@
#: db/models/fields/__init__.py:908
msgid "Email address"
-msgstr ""
+msgstr "E-pos adres"
#: db/models/fields/__init__.py:927
msgid "File path"
@@ -1255,7 +1255,7 @@
#: views/generic/list.py:56
#, python-format
msgid "Invalid page (%(page_number)s): %(message)s"
-msgstr ""
+msgstr "Ongeldige bladsy (%(page_number)s): %(message)s"
#: views/generic/list.py:137
#, python-format
diff --git a/lib/django-1.5/django/conf/locale/az/LC_MESSAGES/django.po b/lib/django-1.5/django/conf/locale/az/LC_MESSAGES/django.po
index 351c460..ab4b660 100644
--- a/lib/django-1.5/django/conf/locale/az/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/conf/locale/az/LC_MESSAGES/django.po
@@ -1,14 +1,14 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
-# Ali Ismayilov <ali@ismailov.info>, 2011-2012.
+# Ali Ismayilov <ali@ismailov.info>, 2011-2013.
# Metin Amiroff <amiroff@gmail.com>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-02 08:47+0000\n"
+"PO-Revision-Date: 2013-03-05 06:00+0000\n"
"Last-Translator: Ali Ismayilov <ali@ismailov.info>\n"
"Language-Team: Azerbaijani (http://www.transifex.com/projects/p/django/"
"language/az/)\n"
@@ -20,7 +20,7 @@
#: conf/global_settings.py:48
msgid "Afrikaans"
-msgstr ""
+msgstr "Afrikaansca"
#: conf/global_settings.py:49
msgid "Arabic"
@@ -36,7 +36,7 @@
#: conf/global_settings.py:52
msgid "Belarusian"
-msgstr ""
+msgstr "Belarusca"
#: conf/global_settings.py:53
msgid "Bengali"
@@ -44,7 +44,7 @@
#: conf/global_settings.py:54
msgid "Breton"
-msgstr ""
+msgstr "Bretonca"
#: conf/global_settings.py:55
msgid "Bosnian"
@@ -104,7 +104,7 @@
#: conf/global_settings.py:69
msgid "Venezuelan Spanish"
-msgstr ""
+msgstr "Venesuela İspancası"
#: conf/global_settings.py:70
msgid "Estonian"
@@ -156,7 +156,7 @@
#: conf/global_settings.py:82
msgid "Interlingua"
-msgstr ""
+msgstr "İnterlinqua"
#: conf/global_settings.py:83
msgid "Indonesian"
@@ -196,7 +196,7 @@
#: conf/global_settings.py:92
msgid "Luxembourgish"
-msgstr ""
+msgstr "Lüksemburqca"
#: conf/global_settings.py:93
msgid "Lithuanian"
@@ -308,7 +308,7 @@
#: conf/global_settings.py:120
msgid "Udmurt"
-msgstr ""
+msgstr "Udmurtca"
#: conf/global_settings.py:121
msgid "Ukrainian"
@@ -1245,7 +1245,7 @@
#: views/generic/list.py:56
#, python-format
msgid "Invalid page (%(page_number)s): %(message)s"
-msgstr ""
+msgstr "Qeyri-düzgün səhifə (%(page_number)s): %(message)s"
#: views/generic/list.py:137
#, python-format
diff --git a/lib/django-1.5/django/conf/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/conf/locale/ka/LC_MESSAGES/django.po
index bbfcc6b..23a00de 100644
--- a/lib/django-1.5/django/conf/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/conf/locale/ka/LC_MESSAGES/django.po
@@ -9,7 +9,7 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-05 09:03+0000\n"
+"PO-Revision-Date: 2013-03-02 08:50+0000\n"
"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
@@ -434,12 +434,12 @@
#: db/models/fields/__init__.py:521 db/models/fields/__init__.py:983
#, python-format
msgid "'%s' value must be an integer."
-msgstr ""
+msgstr "მნიშვნელობა '%s' უნდა იყოს მთელი რიცხვი."
#: db/models/fields/__init__.py:569
#, python-format
msgid "'%s' value must be either True or False."
-msgstr ""
+msgstr "მნიშვნელობა '%s' უნდა იყოს True ან False."
#: db/models/fields/__init__.py:571
msgid "Boolean (Either True or False)"
@@ -490,7 +490,7 @@
#: db/models/fields/__init__.py:849
#, python-format
msgid "'%s' value must be a decimal number."
-msgstr ""
+msgstr "მნიშვნელობა '%s' უნდა იყოს ათობითი რიცხვი."
#: db/models/fields/__init__.py:851
msgid "Decimal number"
@@ -507,7 +507,7 @@
#: db/models/fields/__init__.py:954
#, python-format
msgid "'%s' value must be a float."
-msgstr ""
+msgstr "მნიშვნელობა '%s' უნდა იყოს ათწილადი."
#: db/models/fields/__init__.py:956
msgid "Floating point number"
@@ -528,7 +528,7 @@
#: db/models/fields/__init__.py:1090
#, python-format
msgid "'%s' value must be either None, True or False."
-msgstr ""
+msgstr "მნიშვნელობა '%s' უნდა იყოს None, True ან False."
#: db/models/fields/__init__.py:1092
msgid "Boolean (Either True, False or None)"
@@ -540,16 +540,16 @@
#: db/models/fields/__init__.py:1152
msgid "Positive small integer"
-msgstr ""
+msgstr "დადებითი პატარა მთელი რიცხვი"
#: db/models/fields/__init__.py:1163
#, python-format
msgid "Slug (up to %(max_length)s)"
-msgstr ""
+msgstr "სლაგი (%(max_length)s-მდე)"
#: db/models/fields/__init__.py:1181
msgid "Small integer"
-msgstr ""
+msgstr "პატარა მთელი რიცხვი"
#: db/models/fields/__init__.py:1187
msgid "Text"
@@ -1110,7 +1110,7 @@
#, python-format
msgctxt "String to return when truncating text"
msgid "%(truncated_text)s..."
-msgstr ""
+msgstr "%(truncated_text)s..."
#: utils/text.py:239
msgid "or"
@@ -1172,7 +1172,7 @@
#: views/static.py:58
#, python-format
msgid "\"%(path)s\" does not exist"
-msgstr ""
+msgstr "\"%(path)s\" არ არსებობს"
#: views/static.py:98
#, python-format
@@ -1198,7 +1198,7 @@
#: views/generic/dates.py:368 views/generic/dates.py:393
#, python-format
msgid "No %(verbose_name_plural)s available"
-msgstr ""
+msgstr "%(verbose_name_plural)s არ არსებობს"
#: views/generic/dates.py:646
#, python-format
@@ -1206,16 +1206,19 @@
"Future %(verbose_name_plural)s not available because %(class_name)s."
"allow_future is False."
msgstr ""
+"მომავალი %(verbose_name_plural)s არ არსებობს იმიტომ, რომ %(class_name)s."
+"allow_future არის False."
#: views/generic/dates.py:678
#, python-format
msgid "Invalid date string '%(datestr)s' given format '%(format)s'"
msgstr ""
+"არასწორი თარიღის სტრიქონი '%(datestr)s' გამომდინარე ფორმატიდან '%(format)s'"
#: views/generic/detail.py:54
#, python-format
msgid "No %(verbose_name)s found matching the query"
-msgstr ""
+msgstr "არ მოიძებნა არცერთი მოთხოვნის თანმხვედრი %(verbose_name)s"
#: views/generic/list.py:51
msgid "Page is not 'last', nor can it be converted to an int."
@@ -1229,4 +1232,4 @@
#: views/generic/list.py:137
#, python-format
msgid "Empty list and '%(class_name)s.allow_empty' is False."
-msgstr ""
+msgstr "ცარიელი სია და '%(class_name)s.allow_empty' არის False."
diff --git a/lib/django-1.5/django/conf/locale/pl/LC_MESSAGES/django.po b/lib/django-1.5/django/conf/locale/pl/LC_MESSAGES/django.po
index 7d1b889..5f8e634 100644
--- a/lib/django-1.5/django/conf/locale/pl/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/conf/locale/pl/LC_MESSAGES/django.po
@@ -2,6 +2,7 @@
#
# Translators:
# angularcircle <angular.circle@gmail.com>, 2011.
+# <angular.circle@gmail.com>, 2013.
# <angular.circle@gmail.com>, 2012-2013.
# Jannis Leidel <jannis@leidel.info>, 2011.
# Karol <kfuks2@o2.pl>, 2012.
@@ -14,7 +15,7 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-05 23:30+0000\n"
+"PO-Revision-Date: 2013-02-28 13:10+0000\n"
"Last-Translator: angularcircle <angular.circle@gmail.com>\n"
"Language-Team: Polish (http://www.transifex.com/projects/p/django/language/"
"pl/)\n"
@@ -27,7 +28,7 @@
#: conf/global_settings.py:48
msgid "Afrikaans"
-msgstr ""
+msgstr "Afryknerski"
#: conf/global_settings.py:49
msgid "Arabic"
@@ -43,7 +44,7 @@
#: conf/global_settings.py:52
msgid "Belarusian"
-msgstr ""
+msgstr "białoruski"
#: conf/global_settings.py:53
msgid "Bengali"
@@ -51,7 +52,7 @@
#: conf/global_settings.py:54
msgid "Breton"
-msgstr ""
+msgstr "bretoński"
#: conf/global_settings.py:55
msgid "Bosnian"
@@ -111,7 +112,7 @@
#: conf/global_settings.py:69
msgid "Venezuelan Spanish"
-msgstr ""
+msgstr "hiszpański wenezuelski"
#: conf/global_settings.py:70
msgid "Estonian"
@@ -163,7 +164,7 @@
#: conf/global_settings.py:82
msgid "Interlingua"
-msgstr ""
+msgstr "interlingua"
#: conf/global_settings.py:83
msgid "Indonesian"
@@ -203,7 +204,7 @@
#: conf/global_settings.py:92
msgid "Luxembourgish"
-msgstr ""
+msgstr "luksemburski"
#: conf/global_settings.py:93
msgid "Lithuanian"
@@ -315,7 +316,7 @@
#: conf/global_settings.py:120
msgid "Udmurt"
-msgstr ""
+msgstr "udmurcki"
#: conf/global_settings.py:121
msgid "Ukrainian"
@@ -1260,7 +1261,7 @@
#: views/generic/list.py:56
#, python-format
msgid "Invalid page (%(page_number)s): %(message)s"
-msgstr ""
+msgstr "Nieprawidłowy numer strony (%(page_number)s): %(message)s "
#: views/generic/list.py:137
#, python-format
diff --git a/lib/django-1.5/django/conf/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/conf/locale/th/LC_MESSAGES/django.po
index 3aef02a..a4a106a 100644
--- a/lib/django-1.5/django/conf/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/conf/locale/th/LC_MESSAGES/django.po
@@ -2,7 +2,7 @@
#
# Translators:
# Jannis Leidel <jannis@leidel.info>, 2011.
-# Kowit Charoenratchatabhan <kowito@gmail.com>, 2012.
+# Kowit Charoenratchatabhan <kowito@gmail.com>, 2012-2013.
# Suteepat Damrongyingsupab <monkeycrew_topza@hotmail.com>, 2011, 2012.
# Vichai Vongvorakul <vongvichai@gmail.com>, 2013.
msgid ""
@@ -10,8 +10,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-13 08:02+0000\n"
-"Last-Translator: Vichai Vongvorakul <vongvichai@gmail.com>\n"
+"PO-Revision-Date: 2013-03-04 22:30+0000\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
@@ -158,7 +158,7 @@
#: conf/global_settings.py:82
msgid "Interlingua"
-msgstr ""
+msgstr "ภาษากลาง"
#: conf/global_settings.py:83
msgid "Indonesian"
@@ -310,7 +310,7 @@
#: conf/global_settings.py:120
msgid "Udmurt"
-msgstr ""
+msgstr "อัดเมิร์ท"
#: conf/global_settings.py:121
msgid "Ukrainian"
diff --git a/lib/django-1.5/django/conf/project_template/project_name/settings.py b/lib/django-1.5/django/conf/project_template/project_name/settings.py
index 35117dd..331be43 100644
--- a/lib/django-1.5/django/conf/project_template/project_name/settings.py
+++ b/lib/django-1.5/django/conf/project_template/project_name/settings.py
@@ -126,6 +126,8 @@
# 'django.contrib.admindocs',
)
+SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
+
# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
diff --git a/lib/django-1.5/django/contrib/admin/locale/af/LC_MESSAGES/djangojs.po b/lib/django-1.5/django/contrib/admin/locale/af/LC_MESSAGES/djangojs.po
index 3eb0bfd..0ed16de 100644
--- a/lib/django-1.5/django/contrib/admin/locale/af/LC_MESSAGES/djangojs.po
+++ b/lib/django-1.5/django/contrib/admin/locale/af/LC_MESSAGES/djangojs.po
@@ -1,13 +1,15 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# Piet Delport <pjdelport@gmail.com>, 2013.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-23 02:35+0100\n"
-"PO-Revision-Date: 2011-01-19 15:01+0000\n"
-"Last-Translator: Django team\n"
+"PO-Revision-Date: 2013-03-26 06:30+0000\n"
+"Last-Translator: Piet Delport <pjdelport@gmail.com>\n"
"Language-Team: Afrikaans (http://www.transifex.com/projects/p/django/"
"language/af/)\n"
"MIME-Version: 1.0\n"
@@ -19,7 +21,7 @@
#: static/admin/js/SelectFilter2.js:45
#, c-format
msgid "Available %s"
-msgstr ""
+msgstr "Beskikbaar %s"
#: static/admin/js/SelectFilter2.js:46
#, c-format
@@ -35,11 +37,11 @@
#: static/admin/js/SelectFilter2.js:57
msgid "Filter"
-msgstr ""
+msgstr "Filter"
#: static/admin/js/SelectFilter2.js:61
msgid "Choose all"
-msgstr ""
+msgstr "Kies alle"
#: static/admin/js/SelectFilter2.js:61
#, c-format
@@ -48,7 +50,7 @@
#: static/admin/js/SelectFilter2.js:67
msgid "Choose"
-msgstr ""
+msgstr "Kies"
#: static/admin/js/SelectFilter2.js:69
msgid "Remove"
@@ -68,7 +70,7 @@
#: static/admin/js/SelectFilter2.js:80
msgid "Remove all"
-msgstr ""
+msgstr "Verwyder alle"
#: static/admin/js/SelectFilter2.js:80
#, c-format
@@ -114,55 +116,55 @@
#: static/admin/js/collapse.js:8 static/admin/js/collapse.js.c:19
#: static/admin/js/collapse.min.js:1
msgid "Show"
-msgstr ""
+msgstr "Wys"
#: static/admin/js/collapse.js:15 static/admin/js/collapse.min.js:1
msgid "Hide"
-msgstr ""
+msgstr "Versteek"
#: static/admin/js/admin/DateTimeShortcuts.js:49
#: static/admin/js/admin/DateTimeShortcuts.js:85
msgid "Now"
-msgstr ""
+msgstr "Nou"
#: static/admin/js/admin/DateTimeShortcuts.js:53
msgid "Clock"
-msgstr ""
+msgstr "Klok"
#: static/admin/js/admin/DateTimeShortcuts.js:81
msgid "Choose a time"
-msgstr ""
+msgstr "Kies 'n tyd"
#: static/admin/js/admin/DateTimeShortcuts.js:86
msgid "Midnight"
-msgstr ""
+msgstr "Middernag"
#: static/admin/js/admin/DateTimeShortcuts.js:87
msgid "6 a.m."
-msgstr ""
+msgstr "6 v.m."
#: static/admin/js/admin/DateTimeShortcuts.js:88
msgid "Noon"
-msgstr ""
+msgstr "Middag"
#: static/admin/js/admin/DateTimeShortcuts.js:92
#: static/admin/js/admin/DateTimeShortcuts.js:204
msgid "Cancel"
-msgstr ""
+msgstr "Kanselleer"
#: static/admin/js/admin/DateTimeShortcuts.js:144
#: static/admin/js/admin/DateTimeShortcuts.js:197
msgid "Today"
-msgstr ""
+msgstr "Vandag"
#: static/admin/js/admin/DateTimeShortcuts.js:148
msgid "Calendar"
-msgstr ""
+msgstr "Kalender"
#: static/admin/js/admin/DateTimeShortcuts.js:195
msgid "Yesterday"
-msgstr ""
+msgstr "Gister"
#: static/admin/js/admin/DateTimeShortcuts.js:199
msgid "Tomorrow"
-msgstr ""
+msgstr "Môre"
diff --git a/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po
index 93f4000..2cb53ac 100644
--- a/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/django.po
@@ -10,8 +10,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-02-18 17:50+0000\n"
-"Last-Translator: ljguzman <ljguzman@gmail.com>\n"
+"PO-Revision-Date: 2013-03-21 02:50+0000\n"
+"Last-Translator: Ramiro Morales <cramm0@gmail.com>\n"
"Language-Team: Spanish (Argentina) (http://www.transifex.com/projects/p/"
"django/language/es_AR/)\n"
"MIME-Version: 1.0\n"
@@ -841,7 +841,7 @@
"instructions for setting a new one."
msgstr ""
"¿Olvidó su contraseña? Introduzca su dirección de email abajo y le "
-"enviaremos instrucciones para estblecer una nueva."
+"enviaremos instrucciones para establecer una nueva."
#: templates/registration/password_reset_form.html:21
msgid "Email address:"
diff --git a/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/djangojs.po b/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/djangojs.po
index 240f790..f97b67d 100644
--- a/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/djangojs.po
+++ b/lib/django-1.5/django/contrib/admin/locale/es_AR/LC_MESSAGES/djangojs.po
@@ -1,14 +1,15 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# Jannis Leidel <jannis@leidel.info>, 2011.
-# Ramiro Morales <cramm0@gmail.com>, 2011, 2012.
+# Ramiro Morales <cramm0@gmail.com>, 2011-2013.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-23 02:35+0100\n"
-"PO-Revision-Date: 2012-03-14 20:02+0000\n"
+"PO-Revision-Date: 2013-03-22 20:00+0000\n"
"Last-Translator: Ramiro Morales <cramm0@gmail.com>\n"
"Language-Team: Spanish (Argentina) (http://www.transifex.com/projects/p/"
"django/language/es_AR/)\n"
@@ -29,9 +30,9 @@
"This is the list of available %s. You may choose some by selecting them in "
"the box below and then clicking the \"Choose\" arrow between the two boxes."
msgstr ""
-"Esta es la lista de %s disponibles. Puede elegir algunos seleccionándolos en "
-"el cuadro de abajo y luego haciendo click en la flecha \"Elegir\" ubicada "
-"entre las dos listas."
+"Esta es la lista de %s disponibles. Puede elegir algunos/as seleccionándolos/"
+"as en el cuadro de abajo y luego haciendo click en la flecha \"Seleccionar\" "
+"ubicada entre las dos listas."
#: static/admin/js/SelectFilter2.js:53
#, c-format
@@ -49,11 +50,11 @@
#: static/admin/js/SelectFilter2.js:61
#, c-format
msgid "Click to choose all %s at once."
-msgstr "Haga click para seleccionar todos los %s."
+msgstr "Haga click para seleccionar todos/as los/as %s."
#: static/admin/js/SelectFilter2.js:67
msgid "Choose"
-msgstr "Seleccione"
+msgstr "Seleccionar"
#: static/admin/js/SelectFilter2.js:69
msgid "Remove"
@@ -62,7 +63,7 @@
#: static/admin/js/SelectFilter2.js:75
#, c-format
msgid "Chosen %s"
-msgstr "%s seleccionados"
+msgstr "%s seleccionados/as"
#: static/admin/js/SelectFilter2.js:76
#, c-format
@@ -70,9 +71,9 @@
"This is the list of chosen %s. You may remove some by selecting them in the "
"box below and then clicking the \"Remove\" arrow between the two boxes."
msgstr ""
-"Esta es la lista de %s elegidos. Puede eliminar algunos de ellos "
-"selecciónando los mismos en la lista de abajo y luego haciendo click en la "
-"flecha \"Eliminar\" ubicada entre las dos listas."
+"Esta es la lista de %s seleccionados. Puede deseleccionar algunos de ellos "
+"activándolos en la lista de abajo y luego haciendo click en la flecha "
+"\"Eliminar\" ubicada entre las dos listas."
#: static/admin/js/SelectFilter2.js:80
msgid "Remove all"
@@ -81,7 +82,7 @@
#: static/admin/js/SelectFilter2.js:80
#, c-format
msgid "Click to remove all chosen %s at once."
-msgstr "Haga clic para deselecionar todos los %s."
+msgstr "Haga clic para deselecionar todos/as los/as %s."
#: static/admin/js/actions.js:18 static/admin/js/actions.min.js:1
msgid "%(sel)s of %(cnt)s selected"
diff --git a/lib/django-1.5/django/contrib/admin/locale/eu/LC_MESSAGES/djangojs.po b/lib/django-1.5/django/contrib/admin/locale/eu/LC_MESSAGES/djangojs.po
index 6008b55..a019912 100644
--- a/lib/django-1.5/django/contrib/admin/locale/eu/LC_MESSAGES/djangojs.po
+++ b/lib/django-1.5/django/contrib/admin/locale/eu/LC_MESSAGES/djangojs.po
@@ -1,15 +1,17 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# Aitzol Naberan <anaberan@codesyntax.com>, 2011.
# Jannis Leidel <jannis@leidel.info>, 2011.
+# <julenx@gmail.com>, 2013.
# <julenx@gmail.com>, 2012.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-23 02:35+0100\n"
-"PO-Revision-Date: 2012-07-10 08:52+0000\n"
+"PO-Revision-Date: 2013-03-12 13:40+0000\n"
"Last-Translator: julen <julenx@gmail.com>\n"
"Language-Team: Basque (http://www.transifex.com/projects/p/django/language/"
"eu/)\n"
@@ -30,14 +32,14 @@
"This is the list of available %s. You may choose some by selecting them in "
"the box below and then clicking the \"Choose\" arrow between the two boxes."
msgstr ""
-"Hau da aukeran dauden %s zerrenda. Hauetako zenbait aukeratu ditzazkezu "
+"Hau da aukeran dauden %s zerrenda. Hauetako zenbait aukera ditzakezu "
"azpiko \n"
-"kaxan klik egin eta kutxen artean dagoen \"aukeratu\" gezian klik eginez"
+"kaxan hautatu eta kutxen artean dagoen \"Aukeratu\" gezian klik eginez."
#: static/admin/js/SelectFilter2.js:53
#, c-format
msgid "Type into this box to filter down the list of available %s."
-msgstr "Idatzi kutxa honetan, aukeran %s objektuak iragazteko."
+msgstr "Idatzi kutxa honetan erabilgarri dauden %s objektuak iragazteko."
#: static/admin/js/SelectFilter2.js:57
msgid "Filter"
@@ -50,7 +52,7 @@
#: static/admin/js/SelectFilter2.js:61
#, c-format
msgid "Click to choose all %s at once."
-msgstr "Klik egin %s guztiak batera aukeratzeko"
+msgstr "Egin klik %s guztiak batera aukeratzeko."
#: static/admin/js/SelectFilter2.js:67
msgid "Choose"
@@ -71,18 +73,17 @@
"This is the list of chosen %s. You may remove some by selecting them in the "
"box below and then clicking the \"Remove\" arrow between the two boxes."
msgstr ""
-"Hau da aukeratutako %s zerrenda. Hauetazko zenbait ezabatu ditzazkezu azpiko "
-"kutxan\n"
-"klik egin eta kutxen artean dagoen \"Ezabatu\" gezian klik eginez"
+"Hau da aukeratutako %s zerrenda. Hauetako zenbait ezaba ditzakezu azpiko "
+"kutxan hautatu eta bi kutxen artean dagoen \"Ezabatu\" gezian klik eginez."
#: static/admin/js/SelectFilter2.js:80
msgid "Remove all"
-msgstr "Ezabatu guztiak"
+msgstr "Kendu guztiak"
#: static/admin/js/SelectFilter2.js:80
#, c-format
msgid "Click to remove all chosen %s at once."
-msgstr "Klik egin aukeratuako %s guztiak ezabatzeko."
+msgstr "Egin klik aukeratutako %s guztiak kentzeko."
#: static/admin/js/actions.js:18 static/admin/js/actions.min.js:1
msgid "%(sel)s of %(cnt)s selected"
@@ -104,8 +105,8 @@
"individual fields yet. Please click OK to save. You'll need to re-run the "
"action."
msgstr ""
-"Ekintza bat aukeratu duzu, baina oraindik ez duzu eremuetako aldaketak "
-"gorde. Mesedez, sakatu OK gordetzeko. Ekintza berriro exekutatu beharko duzu."
+"Ekintza bat hautatu duzu, baina oraindik ez duzu eremuetako aldaketak gorde. "
+"Mesedez, sakatu OK gordetzeko. Ekintza berriro exekutatu beharko duzu."
#: static/admin/js/actions.js:123 static/admin/js/actions.min.js:6
msgid ""
@@ -113,7 +114,7 @@
"fields. You're probably looking for the Go button rather than the Save "
"button."
msgstr ""
-"Ekintza bat aukeratu duzu, baina ez duzu inongo aldaketarik egin eremuetan. "
+"Ekintza bat hautatu duzu, baina ez duzu inongo aldaketarik egin eremuetan. "
"Litekeena da, Gorde botoia beharrean Aurrera botoiaren bila aritzea."
#: static/admin/js/calendar.js:26
diff --git a/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/django.po
index 55df1fe..a413eb7 100644
--- a/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/django.po
@@ -1,6 +1,8 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# avsd05 <avsd05@gmail.com>, 2011.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
@@ -8,8 +10,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-02 08:52+0000\n"
-"Last-Translator: Ramiro Morales <cramm0@gmail.com>\n"
+"PO-Revision-Date: 2013-03-01 16:20+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -79,6 +81,8 @@
"Please enter the correct %(username)s and password for a staff account. Note "
"that both fields may be case-sensitive."
msgstr ""
+"გთხოვთ, შეიყვანოთ სწორი %(username)s და პაროლი პერსონალის ანგარიშისთვის. "
+"იქონიეთ მხედველობაში, რომ ორივე ველი ითვალისწინებს მთავრულს."
#: forms.py:19
msgid "Please log in again, because your session has expired."
@@ -119,21 +123,21 @@
#: models.py:45
#, python-format
msgid "Added \"%(object)s\"."
-msgstr ""
+msgstr "დამატებულია \"%(object)s\"."
#: models.py:47
#, python-format
msgid "Changed \"%(object)s\" - %(changes)s"
-msgstr ""
+msgstr "შეცვლილია \"%(object)s\" - %(changes)s"
#: models.py:52
#, python-format
msgid "Deleted \"%(object)s.\""
-msgstr ""
+msgstr "წაშლილია \"%(object)s.\""
#: models.py:54
msgid "LogEntry Object"
-msgstr ""
+msgstr "ჟურნალის ჩანაწერის ობიექტი"
#: options.py:156 options.py:172
msgid "None"
@@ -181,6 +185,8 @@
"The %(name)s \"%(obj)s\" was added successfully. You may add another "
"%(name)s below."
msgstr ""
+"%(name)s \"%(obj)s\" წარმატებით იქნა დამატებული. თქვენ შეგიძლიათ დაამატოთ "
+"სხვა %(name)s ქვემოთ."
#: options.py:839
#, python-format
@@ -193,6 +199,8 @@
"The %(name)s \"%(obj)s\" was changed successfully. You may edit it again "
"below."
msgstr ""
+"%(name)s \"%(obj)s\" წარმატებით შეიცვალა. თქვენ შეგიძლიათ ისევ დაარედაქტიროთ "
+"ის ქვემოთ."
#: options.py:867
#, python-format
@@ -200,6 +208,8 @@
"The %(name)s \"%(obj)s\" was changed successfully. You may add another "
"%(name)s below."
msgstr ""
+"%(name)s \"%(obj)s\" წარმატებით შეიცვალა. თქვენ შეგიძლიათ დაამატოთ სხვა "
+"%(name)s ქვემოთ."
#: options.py:873
#, python-format
@@ -297,11 +307,11 @@
#: widgets.py:316
msgid "Currently:"
-msgstr ""
+msgstr "ამჟამად:"
#: widgets.py:317
msgid "Change:"
-msgstr ""
+msgstr "შეცვლა:"
#: templates/admin/404.html:4 templates/admin/404.html.py:8
msgid "Page not found"
@@ -345,6 +355,8 @@
"There's been an error. It's been reported to the site administrators via "
"email and should be fixed shortly. Thanks for your patience."
msgstr ""
+"მოხდა შეცდომა. ინფორმაცია მასზე გადაეცა საიტის ადმინისტრატორებს ელ. ფოსტით "
+"და ის უნდა შესწორდეს უმოკლეს ვადებში. გმადლობთ მოთმინებისთვის."
#: templates/admin/actions.html:4
msgid "Run the selected action"
@@ -437,16 +449,16 @@
#: templates/admin/change_list_results.html:17
msgid "Remove from sorting"
-msgstr ""
+msgstr "დალაგებიდან მოშორება"
#: templates/admin/change_list_results.html:18
#, python-format
msgid "Sorting priority: %(priority_number)s"
-msgstr ""
+msgstr "დალაგების პრიორიტეტი: %(priority_number)s"
#: templates/admin/change_list_results.html:19
msgid "Toggle sorting"
-msgstr ""
+msgstr "დალაგების გადართვა"
#: templates/admin/delete_confirmation.html:11
#: templates/admin/submit_line.html:4
@@ -527,7 +539,7 @@
#: templates/admin/index.html:20
#, python-format
msgid "Models in the %(name)s application"
-msgstr ""
+msgstr "მოდელები %(name)s აპლიკაციაში"
#: templates/admin/index.html:39
msgid "Change"
@@ -569,7 +581,7 @@
#: templates/admin/login.html:44
msgid "Forgotten your password or username?"
-msgstr ""
+msgstr "დაგავიწყდათ თქვენი პაროლი ან მომხმარებლის სახელი?"
#: templates/admin/object_history.html:23
msgid "Date/time"
@@ -783,6 +795,8 @@
"We've emailed you instructions for setting your password to the email "
"address you submitted. You should be receiving it shortly."
msgstr ""
+"ჩვენ გამოვაგზავნეთ მითითებები პაროლის დასაყენებლად ელ. ფოსტის მისამართზე, "
+"რომელიც თქვენ შეიყვანეთ. თქვენ მალე უნდა მიიღოთ ისინი."
#: templates/registration/password_reset_email.html:2
#, python-format
@@ -790,6 +804,8 @@
"You're receiving this email because you requested a password reset for your "
"user account at %(site_name)s."
msgstr ""
+"თქვენ მიიღეთ ეს წერილი იმიტომ, რომ გააკეთეთ პაროლის თავიდან დაყენების "
+"მოთხოვნა თქვენი მომხმარებლის ანგარიშისთვის %(site_name)s-ზე."
#: templates/registration/password_reset_email.html:4
msgid "Please go to the following page and choose a new password:"
@@ -813,10 +829,12 @@
"Forgotten your password? Enter your email address below, and we'll email "
"instructions for setting a new one."
msgstr ""
+"დაგავიწყდათ თქვენი პაროლი? შეიყვანეთ თქვენი ელ. ფოსტის მისამართი ქვემოთ და "
+"ჩვენ გამოგიგზავნით მითითებებს ახალი პაროლის დასაყენებლად."
#: templates/registration/password_reset_form.html:21
msgid "Email address:"
-msgstr ""
+msgstr "ელ. ფოსტის მისამართი:"
#: templates/registration/password_reset_form.html:21
msgid "Reset my password"
@@ -828,7 +846,7 @@
#: views/main.py:33
msgid "(None)"
-msgstr ""
+msgstr "(არცერთი)"
#: views/main.py:76
#, python-format
diff --git a/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/djangojs.po b/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/djangojs.po
index 8f369fc..d41375d 100644
--- a/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/djangojs.po
+++ b/lib/django-1.5/django/contrib/admin/locale/ka/LC_MESSAGES/djangojs.po
@@ -1,6 +1,8 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# avsd05 <avsd05@gmail.com>, 2011.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
@@ -8,8 +10,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-23 02:35+0100\n"
-"PO-Revision-Date: 2012-03-08 10:42+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 11:40+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -29,11 +31,13 @@
"This is the list of available %s. You may choose some by selecting them in "
"the box below and then clicking the \"Choose\" arrow between the two boxes."
msgstr ""
+"ეს არის მისაწვდომი %s-ის სია. ზოგიერთი მათგანის ასარჩევად, მონიშვნით ისინი "
+"ქვედა სარკმელში და დააწკაპუნეთ ორ სარკმელს შორის მდებარე ისარზე \"არჩევა\" ."
#: static/admin/js/SelectFilter2.js:53
#, c-format
msgid "Type into this box to filter down the list of available %s."
-msgstr ""
+msgstr "აკრიფეთ ამ სარკმელში მისაწვდომი %s-ის სიის გასაფილტრად."
#: static/admin/js/SelectFilter2.js:57
msgid "Filter"
@@ -46,11 +50,11 @@
#: static/admin/js/SelectFilter2.js:61
#, c-format
msgid "Click to choose all %s at once."
-msgstr ""
+msgstr "დააწკაპუნეთ ერთდროულად ყველა %s-ის ასარჩევად."
#: static/admin/js/SelectFilter2.js:67
msgid "Choose"
-msgstr ""
+msgstr "არჩევა"
#: static/admin/js/SelectFilter2.js:69
msgid "Remove"
@@ -67,15 +71,18 @@
"This is the list of chosen %s. You may remove some by selecting them in the "
"box below and then clicking the \"Remove\" arrow between the two boxes."
msgstr ""
+"ეს არის არჩეული %s-ის სია. ზოგიერთი მათგანის მოსაშორებლად, მონიშვნით ისინი "
+"ქვედა სარკმელში და დააწკაპუნეთ ორ სარკმელს შორის მდებარე ისარზე \"მოშორება"
+"\" ."
#: static/admin/js/SelectFilter2.js:80
msgid "Remove all"
-msgstr ""
+msgstr "ყველას მოშორება"
#: static/admin/js/SelectFilter2.js:80
#, c-format
msgid "Click to remove all chosen %s at once."
-msgstr ""
+msgstr "დააწკაპუნეთ ყველა არჩეული %s-ის ერთდროულად მოსაშორებლად."
#: static/admin/js/actions.js:18 static/admin/js/actions.min.js:1
msgid "%(sel)s of %(cnt)s selected"
diff --git a/lib/django-1.5/django/contrib/admin/locale/nb/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/admin/locale/nb/LC_MESSAGES/django.po
index 8533b55..4f28599 100644
--- a/lib/django-1.5/django/contrib/admin/locale/nb/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/admin/locale/nb/LC_MESSAGES/django.po
@@ -1,7 +1,9 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# Jannis Leidel <jannis@leidel.info>, 2011.
+# <jonklo@gmail.com>, 2013.
# <jonklo@gmail.com>, 2012-2013.
# jonklo <jonklo@gmail.com>, 2011.
# <sigurdga-transifex@sigurdga.no>, 2012.
@@ -10,7 +12,7 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-09 22:55+0000\n"
+"PO-Revision-Date: 2013-03-06 17:00+0000\n"
"Last-Translator: jonklo <jonklo@gmail.com>\n"
"Language-Team: Norwegian Bokmål (http://www.transifex.com/projects/p/django/"
"language/nb/)\n"
@@ -783,7 +785,7 @@
#: templates/registration/password_reset_done.html:11
#: templates/registration/password_reset_done.html:15
msgid "Password reset successful"
-msgstr "Passordet ble nullstilt"
+msgstr "Passord-nullstilling klar"
#: templates/registration/password_reset_done.html:17
msgid ""
diff --git a/lib/django-1.5/django/contrib/admin/locale/te/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/admin/locale/te/LC_MESSAGES/django.po
index 7c71510..91e313c 100644
--- a/lib/django-1.5/django/contrib/admin/locale/te/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/admin/locale/te/LC_MESSAGES/django.po
@@ -1,17 +1,18 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# bhaskar teja yerneni <prudhviy@gmail.com>, 2011.
# Jannis Leidel <jannis@leidel.info>, 2011.
# Veeven <veeven@gmail.com>, 2011.
-# ప్రవీణ్ ఇళ్ళ <mail2ipn@gmail.com>, 2011.
+# ప్రవీణ్ ఇళ్ళ <mail2ipn@gmail.com>, 2011,2013.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-02 08:52+0000\n"
-"Last-Translator: Ramiro Morales <cramm0@gmail.com>\n"
+"PO-Revision-Date: 2013-03-01 20:10+0000\n"
+"Last-Translator: ప్రవీణ్ ఇళ్ళ <mail2ipn@gmail.com>\n"
"Language-Team: Telugu (http://www.transifex.com/projects/p/django/language/"
"te/)\n"
"MIME-Version: 1.0\n"
@@ -28,7 +29,7 @@
#: actions.py:60 options.py:1347
#, python-format
msgid "Cannot delete %(name)s"
-msgstr ""
+msgstr "%(name)s తొలగించుట వీలుకాదు"
#: actions.py:62 options.py:1349
msgid "Are you sure?"
@@ -350,7 +351,7 @@
#: templates/admin/actions.html:4
msgid "Run the selected action"
-msgstr ""
+msgstr "ఎంచుకున్న చర్యను నడుపు"
#: templates/admin/actions.html:4
msgid "Go"
@@ -367,7 +368,7 @@
#: templates/admin/actions.html:13
msgid "Clear selection"
-msgstr ""
+msgstr "ఎంపికను తుడిచివేయి"
#: templates/admin/app_index.html:10 templates/admin/index.html:21
#, python-format
@@ -556,7 +557,7 @@
#: templates/admin/login.html:44
msgid "Forgotten your password or username?"
-msgstr ""
+msgstr "మీ సంకేతపదం లేదా వాడుకరిపేరును మర్చిపోయారా?"
#: templates/admin/object_history.html:23
msgid "Date/time"
diff --git a/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/django.po
index c8ea590..f534a34 100644
--- a/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/django.po
@@ -1,8 +1,9 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# Jannis Leidel <jannis@leidel.info>, 2011.
-# Kowit Charoenratchatabhan <kowito@gmail.com>, 2011, 2012.
+# Kowit Charoenratchatabhan <kowito@gmail.com>, 2011-2013.
# Piti Ongmongkolkul <piti118@gmail.com>, 2012.
# Suteepat Damrongyingsupab <monkeycrew_topza@hotmail.com>, 2011, 2012.
msgid ""
@@ -10,8 +11,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-01 16:10+0100\n"
-"PO-Revision-Date: 2013-01-02 08:52+0000\n"
-"Last-Translator: Piti Ongmongkolkul <piti118@gmail.com>\n"
+"PO-Revision-Date: 2013-03-04 22:30+0000\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
@@ -80,7 +81,7 @@
msgid ""
"Please enter the correct %(username)s and password for a staff account. Note "
"that both fields may be case-sensitive."
-msgstr ""
+msgstr "กรุณาใส่ %(username)s และรหัสผ่านให้ถูกต้อง มีการแยกแยะตัวพิมพ์ใหญ่-เล็ก"
#: forms.py:19
msgid "Please log in again, because your session has expired."
@@ -180,7 +181,7 @@
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may add another "
"%(name)s below."
-msgstr ""
+msgstr "เพิ่ม %(name)s \"%(obj)s\" เรียบร้อยแล้ว เพิ่ม %(name)s ได้อีกที่ด้านล่าง"
#: options.py:839
#, python-format
@@ -192,14 +193,14 @@
msgid ""
"The %(name)s \"%(obj)s\" was changed successfully. You may edit it again "
"below."
-msgstr ""
+msgstr "เปลี่ยนแปลง %(name)s \"%(obj)s\" เรียบร้อยแล้ว แก้ไขได้อีกที่ด้านล่าง"
#: options.py:867
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was changed successfully. You may add another "
"%(name)s below."
-msgstr ""
+msgstr "เปลี่ยนแปลง %(name)s \"%(obj)s\" เรียบร้อยแล้ว เพิ่ม %(name)s ได้อีกที่ด้านล่าง"
#: options.py:873
#, python-format
@@ -525,7 +526,7 @@
#: templates/admin/index.html:20
#, python-format
msgid "Models in the %(name)s application"
-msgstr ""
+msgstr "โมเดลในแอป %(name)s"
#: templates/admin/index.html:39
msgid "Change"
diff --git a/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/djangojs.po b/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/djangojs.po
index ecd2c13..82cbb6b 100644
--- a/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/djangojs.po
+++ b/lib/django-1.5/django/contrib/admin/locale/th/LC_MESSAGES/djangojs.po
@@ -10,7 +10,7 @@
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-03-23 02:35+0100\n"
"PO-Revision-Date: 2012-03-09 03:31+0000\n"
-"Last-Translator: Kowit Charoenratchatabhan <kowito@gmail.com>\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
diff --git a/lib/django-1.5/django/contrib/admin/options.py b/lib/django-1.5/django/contrib/admin/options.py
index e08cd9c..6204e0b 100644
--- a/lib/django-1.5/django/contrib/admin/options.py
+++ b/lib/django-1.5/django/contrib/admin/options.py
@@ -248,7 +248,7 @@
parts.pop()
# Special case -- foo__id__exact and foo__id queries are implied
- # if foo has been specificially included in the lookup list; so
+ # if foo has been specifically included in the lookup list; so
# drop __id if it is the last part. However, first we need to find
# the pk attribute name.
rel_name = None
@@ -256,7 +256,7 @@
try:
field, _, _, _ = model._meta.get_field_by_name(part)
except FieldDoesNotExist:
- # Lookups on non-existants fields are ok, since they're ignored
+ # Lookups on non-existent fields are ok, since they're ignored
# later.
return True
if hasattr(field, 'rel'):
@@ -278,7 +278,7 @@
def has_add_permission(self, request):
"""
Returns True if the given request has permission to add an object.
- Can be overriden by the user in subclasses.
+ Can be overridden by the user in subclasses.
"""
opts = self.opts
return request.user.has_perm(opts.app_label + '.' + opts.get_add_permission())
@@ -289,7 +289,7 @@
Django model instance, the default implementation doesn't examine the
`obj` parameter.
- Can be overriden by the user in subclasses. In such case it should
+ Can be overridden by the user in subclasses. In such case it should
return True if the given request has permission to change the `obj`
model instance. If `obj` is None, this should return True if the given
request has permission to change *any* object of the given type.
@@ -303,7 +303,7 @@
Django model instance, the default implementation doesn't examine the
`obj` parameter.
- Can be overriden by the user in subclasses. In such case it should
+ Can be overridden by the user in subclasses. In such case it should
return True if the given request has permission to delete the `obj`
model instance. If `obj` is None, this should return True if the given
request has permission to delete *any* object of the given type.
@@ -575,7 +575,7 @@
Return a dictionary mapping the names of all actions for this
ModelAdmin to a tuple of (callable, name, description) for each action.
"""
- # If self.actions is explicitally set to None that means that we don't
+ # If self.actions is explicitly set to None that means that we don't
# want *any* actions enabled on this page.
from django.contrib.admin.views.main import IS_POPUP_VAR
if self.actions is None or IS_POPUP_VAR in request.GET:
diff --git a/lib/django-1.5/django/contrib/admin/sites.py b/lib/django-1.5/django/contrib/admin/sites.py
index 72e0b9a..ab76a8b 100644
--- a/lib/django-1.5/django/contrib/admin/sites.py
+++ b/lib/django-1.5/django/contrib/admin/sites.py
@@ -129,7 +129,7 @@
def get_action(self, name):
"""
- Explicitally get a registered global action wheather it's enabled or
+ Explicitly get a registered global action whether it's enabled or
not. Raises KeyError for invalid names.
"""
return self._global_actions[name]
diff --git a/lib/django-1.5/django/contrib/admin/widgets.py b/lib/django-1.5/django/contrib/admin/widgets.py
index 1e6277f..1635ea0 100644
--- a/lib/django-1.5/django/contrib/admin/widgets.py
+++ b/lib/django-1.5/django/contrib/admin/widgets.py
@@ -310,9 +310,9 @@
html = super(AdminURLFieldWidget, self).render(name, value, attrs)
if value:
value = force_text(self._format_value(value))
- final_attrs = {'href': mark_safe(smart_urlquote(value))}
+ final_attrs = {'href': smart_urlquote(value)}
html = format_html(
- '<p class="url">{0} <a {1}>{2}</a><br />{3} {4}</p>',
+ '<p class="url">{0} <a{1}>{2}</a><br />{3} {4}</p>',
_('Currently:'), flatatt(final_attrs), value,
_('Change:'), html
)
diff --git a/lib/django-1.5/django/contrib/admindocs/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/admindocs/locale/ka/LC_MESSAGES/django.po
index cefda07..9fbec1e 100644
--- a/lib/django-1.5/django/contrib/admindocs/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/admindocs/locale/ka/LC_MESSAGES/django.po
@@ -1,14 +1,16 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-22 09:28+0200\n"
-"PO-Revision-Date: 2012-10-22 08:46+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 08:40+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -168,24 +170,24 @@
#: templates/admin_doc/model_detail.html:16
#: templates/admin_doc/model_index.html:10
msgid "Models"
-msgstr ""
+msgstr "მოდელები"
#: templates/admin_doc/template_detail.html:8
msgid "Templates"
-msgstr ""
+msgstr "შაბლონები"
#: templates/admin_doc/template_filter_index.html:9
msgid "Filters"
-msgstr ""
+msgstr "ფილტრები"
#: templates/admin_doc/template_tag_index.html:9
msgid "Tags"
-msgstr ""
+msgstr "ტეგები"
#: templates/admin_doc/view_detail.html:8
#: templates/admin_doc/view_index.html:9
msgid "Views"
-msgstr ""
+msgstr "წარმოდგენები"
#: tests/__init__.py:23
msgid "Boolean (Either True or False)"
diff --git a/lib/django-1.5/django/contrib/auth/__init__.py b/lib/django-1.5/django/contrib/auth/__init__.py
index dbcc28c..d4cf945 100644
--- a/lib/django-1.5/django/contrib/auth/__init__.py
+++ b/lib/django-1.5/django/contrib/auth/__init__.py
@@ -2,6 +2,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module
+from django.middleware.csrf import rotate_token
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
SESSION_KEY = '_auth_user_id'
@@ -92,6 +93,7 @@
request.session[BACKEND_SESSION_KEY] = user.backend
if hasattr(request, 'user'):
request.user = user
+ rotate_token(request)
user_logged_in.send(sender=user.__class__, request=request, user=user)
diff --git a/lib/django-1.5/django/contrib/auth/admin.py b/lib/django-1.5/django/contrib/auth/admin.py
index 7b81667..3c87830 100644
--- a/lib/django-1.5/django/contrib/auth/admin.py
+++ b/lib/django-1.5/django/contrib/auth/admin.py
@@ -16,6 +16,7 @@
from django.views.decorators.debug import sensitive_post_parameters
csrf_protect_m = method_decorator(csrf_protect)
+sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
class GroupAdmin(admin.ModelAdmin):
@@ -83,7 +84,13 @@
self.admin_site.admin_view(self.user_change_password))
) + super(UserAdmin, self).get_urls()
- @sensitive_post_parameters()
+ def lookup_allowed(self, lookup, value):
+ # See #20078: we don't want to allow any lookups involving passwords.
+ if lookup.startswith('password'):
+ return False
+ return super(UserAdmin, self).lookup_allowed(lookup, value)
+
+ @sensitive_post_parameters_m
@csrf_protect_m
@transaction.commit_on_success
def add_view(self, request, form_url='', extra_context=None):
@@ -114,7 +121,7 @@
return super(UserAdmin, self).add_view(request, form_url,
extra_context)
- @sensitive_post_parameters()
+ @sensitive_post_parameters_m
def user_change_password(self, request, id, form_url=''):
if not self.has_change_permission(request):
raise PermissionDenied
diff --git a/lib/django-1.5/django/contrib/auth/backends.py b/lib/django-1.5/django/contrib/auth/backends.py
index 05e9bfd..6b31f72 100644
--- a/lib/django-1.5/django/contrib/auth/backends.py
+++ b/lib/django-1.5/django/contrib/auth/backends.py
@@ -5,7 +5,7 @@
class ModelBackend(object):
"""
- Authenticates against django.contrib.auth.models.User.
+ Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, username=None, password=None, **kwargs):
diff --git a/lib/django-1.5/django/contrib/auth/forms.py b/lib/django-1.5/django/contrib/auth/forms.py
index cbce8ad..d191635 100644
--- a/lib/django-1.5/django/contrib/auth/forms.py
+++ b/lib/django-1.5/django/contrib/auth/forms.py
@@ -12,7 +12,9 @@
from django.contrib.auth import authenticate, get_user_model
from django.contrib.auth.models import User
-from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
+from django.contrib.auth.hashers import (
+ MAXIMUM_PASSWORD_LENGTH, UNUSABLE_PASSWORD, identify_hasher,
+)
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site
@@ -75,9 +77,10 @@
'invalid': _("This value may contain only letters, numbers and "
"@/./+/-/_ characters.")})
password1 = forms.CharField(label=_("Password"),
- widget=forms.PasswordInput)
+ widget=forms.PasswordInput, max_length=MAXIMUM_PASSWORD_LENGTH)
password2 = forms.CharField(label=_("Password confirmation"),
widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
help_text=_("Enter the same password as above, for verification."))
class Meta:
@@ -145,7 +148,11 @@
username/password logins.
"""
username = forms.CharField(max_length=254)
- password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
+ password = forms.CharField(
+ label=_("Password"),
+ widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
+ )
error_messages = {
'invalid_login': _("Please enter a correct %(username)s and password. "
@@ -269,10 +276,16 @@
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
- new_password1 = forms.CharField(label=_("New password"),
- widget=forms.PasswordInput)
- new_password2 = forms.CharField(label=_("New password confirmation"),
- widget=forms.PasswordInput)
+ new_password1 = forms.CharField(
+ label=_("New password"),
+ widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
+ )
+ new_password2 = forms.CharField(
+ label=_("New password confirmation"),
+ widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
+ )
def __init__(self, user, *args, **kwargs):
self.user = user
@@ -303,8 +316,11 @@
'password_incorrect': _("Your old password was entered incorrectly. "
"Please enter it again."),
})
- old_password = forms.CharField(label=_("Old password"),
- widget=forms.PasswordInput)
+ old_password = forms.CharField(
+ label=_("Old password"),
+ widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
+ )
def clean_old_password(self):
"""
@@ -329,10 +345,16 @@
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
- password1 = forms.CharField(label=_("Password"),
- widget=forms.PasswordInput)
- password2 = forms.CharField(label=_("Password (again)"),
- widget=forms.PasswordInput)
+ password1 = forms.CharField(
+ label=_("Password"),
+ widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
+ )
+ password2 = forms.CharField(
+ label=_("Password (again)"),
+ widget=forms.PasswordInput,
+ max_length=MAXIMUM_PASSWORD_LENGTH,
+ )
def __init__(self, user, *args, **kwargs):
self.user = user
diff --git a/lib/django-1.5/django/contrib/auth/hashers.py b/lib/django-1.5/django/contrib/auth/hashers.py
index b49362f..a9d5d7b 100644
--- a/lib/django-1.5/django/contrib/auth/hashers.py
+++ b/lib/django-1.5/django/contrib/auth/hashers.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
import base64
+import functools
import hashlib
from django.dispatch import receiver
@@ -16,6 +17,7 @@
UNUSABLE_PASSWORD = '!' # This will never be a valid encoded hash
+MAXIMUM_PASSWORD_LENGTH = 4096 # The maximum length a password can be to prevent DoS
HASHERS = None # lazily loaded from PASSWORD_HASHERS
PREFERRED_HASHER = None # defaults to first item in PASSWORD_HASHERS
@@ -27,6 +29,18 @@
PREFERRED_HASHER = None
+def password_max_length(max_length):
+ def inner(fn):
+ @functools.wraps(fn)
+ def wrapper(self, password, *args, **kwargs):
+ if len(password) > max_length:
+ raise ValueError("Invalid password; Must be less than or equal"
+ " to %d bytes" % max_length)
+ return fn(self, password, *args, **kwargs)
+ return wrapper
+ return inner
+
+
def is_password_usable(encoded):
if encoded is None or encoded == UNUSABLE_PASSWORD:
return False
@@ -225,6 +239,7 @@
iterations = 10000
digest = hashlib.sha256
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt, iterations=None):
assert password
assert salt and '$' not in salt
@@ -234,6 +249,7 @@
hash = base64.b64encode(hash).decode('ascii').strip()
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
algorithm, iterations, salt, hash = encoded.split('$', 3)
assert algorithm == self.algorithm
@@ -279,6 +295,7 @@
bcrypt = self._load_library()
return bcrypt.gensalt(self.rounds)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt):
bcrypt = self._load_library()
# Need to reevaluate the force_bytes call once bcrypt is supported on
@@ -286,6 +303,7 @@
data = bcrypt.hashpw(force_bytes(password), salt)
return "%s$%s" % (self.algorithm, data)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
algorithm, data = encoded.split('$', 1)
assert algorithm == self.algorithm
@@ -310,12 +328,14 @@
"""
algorithm = "sha1"
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt):
assert password
assert salt and '$' not in salt
hash = hashlib.sha1(force_bytes(salt + password)).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
@@ -338,12 +358,14 @@
"""
algorithm = "md5"
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt):
assert password
assert salt and '$' not in salt
hash = hashlib.md5(force_bytes(salt + password)).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
@@ -374,11 +396,13 @@
def salt(self):
return ''
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt):
assert salt == ''
hash = hashlib.sha1(force_bytes(password)).hexdigest()
return 'sha1$$%s' % hash
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
encoded_2 = self.encode(password, '')
return constant_time_compare(encoded, encoded_2)
@@ -408,10 +432,12 @@
def salt(self):
return ''
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt):
assert salt == ''
return hashlib.md5(force_bytes(password)).hexdigest()
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
if len(encoded) == 37 and encoded.startswith('md5$$'):
encoded = encoded[5:]
@@ -437,6 +463,7 @@
def salt(self):
return get_random_string(2)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def encode(self, password, salt):
crypt = self._load_library()
assert len(salt) == 2
@@ -444,6 +471,7 @@
# we don't need to store the salt, but Django used to do this
return "%s$%s$%s" % (self.algorithm, '', data)
+ @password_max_length(MAXIMUM_PASSWORD_LENGTH)
def verify(self, password, encoded):
crypt = self._load_library()
algorithm, salt, data = encoded.split('$', 2)
@@ -458,4 +486,3 @@
(_('salt'), salt),
(_('hash'), mask_hash(data, show=3)),
])
-
diff --git a/lib/django-1.5/django/contrib/auth/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/auth/locale/ka/LC_MESSAGES/django.po
index 6e00396..ac9db9e 100644
--- a/lib/django-1.5/django/contrib/auth/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/auth/locale/ka/LC_MESSAGES/django.po
@@ -1,14 +1,16 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-15 23:27+0100\n"
-"PO-Revision-Date: 2012-12-16 08:51+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 16:20+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -40,11 +42,11 @@
#: forms.py:31 tests/forms.py:251 tests/forms.py:256 tests/forms.py:384
msgid "No password set."
-msgstr ""
+msgstr "არ არის დაყენებული პაროლი."
#: forms.py:37 tests/forms.py:261 tests/forms.py:267
msgid "Invalid password format or unknown hashing algorithm."
-msgstr ""
+msgstr "არასწორი პაროლის ფორმატი ან უცნობი ჰეშირების ალგორითმი."
#: forms.py:67
msgid "A user with that username already exists."
@@ -87,6 +89,9 @@
"password, but you can change the password using <a href=\"password/\">this "
"form</a>."
msgstr ""
+"დაუმუშავებელი პაროლები არ ინახება, ასე რომ, არ არსებობს ამ მომხმარებლის "
+"პაროლის ნახვის საშუალება, მაგრამ თქვენ შეგიძლიათ შეცვალოთ პაროლი <a href="
+"\"password/\">ამ ფორმის</a> მეშვეობით."
#: forms.py:151
#, python-format
@@ -94,6 +99,8 @@
"Please enter a correct %(username)s and password. Note that both fields may "
"be case-sensitive."
msgstr ""
+"გთხოვთ, შეიყვანოთ სწორი %(username)s და პაროლი. იქონიეთ მხედველობაში, რომ "
+"ორივე ველი ითვალისწინებს მთავრულს."
#: forms.py:153
msgid ""
@@ -111,16 +118,20 @@
"That email address doesn't have an associated user account. Are you sure "
"you've registered?"
msgstr ""
+"ამ ელ. ფოსტის მისამართს არ გააჩნია ასოცირებული მომხმარებლის ანგარიში. "
+"დარწმუნებული ხართ, რომ დარეგისტრირდით?"
#: forms.py:208 tests/forms.py:374
msgid ""
"The user account associated with this email address cannot reset the "
"password."
msgstr ""
+"ამ ელ. ფოსტასთან ასოცირებულ მომხმარებლის ანგარიშს არ გააჩნია პაროლის თავიდან "
+"დაყენების უფლება."
#: forms.py:211
msgid "Email"
-msgstr ""
+msgstr "ელ. ფოსტა"
#: forms.py:271
msgid "New password"
@@ -145,27 +156,27 @@
#: hashers.py:241 hashers.py:292 hashers.py:321 hashers.py:349 hashers.py:378
#: hashers.py:412
msgid "algorithm"
-msgstr ""
+msgstr "ალგორითმი"
#: hashers.py:242
msgid "iterations"
-msgstr ""
+msgstr "იტერაციები"
#: hashers.py:243 hashers.py:294 hashers.py:322 hashers.py:350 hashers.py:413
msgid "salt"
-msgstr ""
+msgstr "მარილი"
#: hashers.py:244 hashers.py:323 hashers.py:351 hashers.py:379 hashers.py:414
msgid "hash"
-msgstr ""
+msgstr "ჰეში"
#: hashers.py:293
msgid "work factor"
-msgstr ""
+msgstr "სამუშაო ფაქტორი"
#: hashers.py:295
msgid "checksum"
-msgstr ""
+msgstr "საკონტროლო ჯამი"
#: models.py:72 models.py:121
msgid "name"
@@ -214,6 +225,8 @@
"The groups this user belongs to. A user will get all permissions granted to "
"each of his/her group."
msgstr ""
+"ჯგუფები, რომლებსაც მიეკუთვნება ეს მომხმარებელი. მომხმარებელს ენიჭება მისი "
+"ყოველი ჯგუფის ყველა უფლება."
#: models.py:306
msgid "user permissions"
@@ -232,7 +245,7 @@
#: models.py:381
msgid "Enter a valid username."
-msgstr ""
+msgstr "შეიყვანეთ სწორი მომხმარებლის სახელი."
#: models.py:383
msgid "first name"
@@ -244,7 +257,7 @@
#: models.py:385
msgid "email address"
-msgstr ""
+msgstr "ელ. ფოსტის მისამართი"
#: models.py:386
msgid "staff status"
@@ -286,4 +299,4 @@
#: templates/registration/password_reset_subject.txt:2
#, python-format
msgid "Password reset on %(site_name)s"
-msgstr ""
+msgstr "პაროლის თავიდან დაყენება %(site_name)s-ზე"
diff --git a/lib/django-1.5/django/contrib/auth/locale/nl/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/auth/locale/nl/LC_MESSAGES/django.po
index ec41e51..0706038 100644
--- a/lib/django-1.5/django/contrib/auth/locale/nl/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/auth/locale/nl/LC_MESSAGES/django.po
@@ -1,7 +1,9 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# <bart@dispectu.com>, 2012-2013.
+# Erik Romijn <eromijn@solidlinks.nl>, 2013.
# Harro van der Klauw <hvdklauw@gmail.com>, 2012.
# Jannis Leidel <jannis@leidel.info>, 2011.
# Jeffrey Gelens <jeffrey@noppo.pro>, 2011, 2012.
@@ -11,8 +13,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-15 23:27+0100\n"
-"PO-Revision-Date: 2013-01-03 11:14+0000\n"
-"Last-Translator: bartdegoede <bart@dispectu.com>\n"
+"PO-Revision-Date: 2013-03-19 15:55+0000\n"
+"Last-Translator: erikr <eromijn@solidlinks.nl>\n"
"Language-Team: Dutch (http://www.transifex.com/projects/p/django/language/"
"nl/)\n"
"MIME-Version: 1.0\n"
@@ -100,8 +102,8 @@
"Please enter a correct %(username)s and password. Note that both fields may "
"be case-sensitive."
msgstr ""
-"Voer de correcte %(username)s en wachtwoord voor een stafaccount in. Let op "
-"dat beide velden hoofdlettergevoelig zijn."
+"Voer een correcte %(username)s en wachtwoord in. Let op dat beide velden "
+"hoofdlettergevoelig zijn."
#: forms.py:153
msgid ""
diff --git a/lib/django-1.5/django/contrib/auth/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/auth/locale/th/LC_MESSAGES/django.po
index 2076227..d83c200 100644
--- a/lib/django-1.5/django/contrib/auth/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/auth/locale/th/LC_MESSAGES/django.po
@@ -1,15 +1,16 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
# Jannis Leidel <jannis@leidel.info>, 2011.
-# Kowit Charoenratchatabhan <kowito@gmail.com>, 2012.
+# Kowit Charoenratchatabhan <kowito@gmail.com>, 2012-2013.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-15 23:27+0100\n"
-"PO-Revision-Date: 2012-12-16 08:51+0000\n"
-"Last-Translator: Kowit Charoenratchatabhan <kowito@gmail.com>\n"
+"PO-Revision-Date: 2013-03-04 22:30+0000\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
@@ -41,11 +42,11 @@
#: forms.py:31 tests/forms.py:251 tests/forms.py:256 tests/forms.py:384
msgid "No password set."
-msgstr ""
+msgstr "ไม่ได้ตั้งค่ารหัสผ่าน"
#: forms.py:37 tests/forms.py:261 tests/forms.py:267
msgid "Invalid password format or unknown hashing algorithm."
-msgstr ""
+msgstr "รูปแบบรหัสผ่านไม่ถูกต้อง หรือไม่รู้จักแฮชอัลกอริทึมนี้"
#: forms.py:67
msgid "A user with that username already exists."
@@ -94,7 +95,7 @@
msgid ""
"Please enter a correct %(username)s and password. Note that both fields may "
"be case-sensitive."
-msgstr ""
+msgstr "กรุณาใส่ %(username)s และรหัสผ่านที่ถูกต้อง มีการแยกแยะตัวพิมพ์ใหญ่-เล็ก"
#: forms.py:153
msgid ""
@@ -110,17 +111,17 @@
msgid ""
"That email address doesn't have an associated user account. Are you sure "
"you've registered?"
-msgstr ""
+msgstr "อีเมลนี้ไม่ตรงกับบัญชีใดๆในระบบ คุณแน่ใจหรือว่าคุณได้ลงทะเบียนแล้ว"
#: forms.py:208 tests/forms.py:374
msgid ""
"The user account associated with this email address cannot reset the "
"password."
-msgstr ""
+msgstr "บัญชีผู้ใช้ที่เกี่ยวข้องกับที่อยู่อีเมล์นี้ไม่สามารถตั้งค่ารหัสผ่าน"
#: forms.py:211
msgid "Email"
-msgstr ""
+msgstr "อีเมล"
#: forms.py:271
msgid "New password"
@@ -230,7 +231,7 @@
#: models.py:381
msgid "Enter a valid username."
-msgstr ""
+msgstr "กรุณาใส่ชื่อผู้ใช้ที่ถูกต้อง"
#: models.py:383
msgid "first name"
@@ -242,7 +243,7 @@
#: models.py:385
msgid "email address"
-msgstr ""
+msgstr "ที่อยู่อีเมล"
#: models.py:386
msgid "staff status"
diff --git a/lib/django-1.5/django/contrib/auth/management/__init__.py b/lib/django-1.5/django/contrib/auth/management/__init__.py
index a77bba0..34492d2 100644
--- a/lib/django-1.5/django/contrib/auth/management/__init__.py
+++ b/lib/django-1.5/django/contrib/auth/management/__init__.py
@@ -133,7 +133,10 @@
# (a very restricted chroot environment, for example).
return ''
if not six.PY3:
- default_locale = locale.getdefaultlocale()[1]
+ try:
+ default_locale = locale.getdefaultlocale()[1]
+ except ValueError:
+ return ''
if not default_locale:
return ''
try:
diff --git a/lib/django-1.5/django/contrib/auth/tests/hashers.py b/lib/django-1.5/django/contrib/auth/tests/hashers.py
index 2b2243c..8c35f54 100644
--- a/lib/django-1.5/django/contrib/auth/tests/hashers.py
+++ b/lib/django-1.5/django/contrib/auth/tests/hashers.py
@@ -4,7 +4,8 @@
from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
from django.contrib.auth.hashers import (is_password_usable,
check_password, make_password, PBKDF2PasswordHasher, load_hashers,
- PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD)
+ PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD,
+ MAXIMUM_PASSWORD_LENGTH, password_max_length)
from django.utils import unittest
from django.utils.unittest import skipUnless
@@ -31,6 +32,12 @@
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password('lètmein', encoded))
self.assertFalse(check_password('lètmeinz', encoded))
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ )
def test_pkbdf2(self):
encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256')
@@ -40,6 +47,14 @@
self.assertTrue(check_password('lètmein', encoded))
self.assertFalse(check_password('lètmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256")
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ "seasalt",
+ "pbkdf2_sha256",
+ )
def test_sha1(self):
encoded = make_password('lètmein', 'seasalt', 'sha1')
@@ -49,6 +64,14 @@
self.assertTrue(check_password('lètmein', encoded))
self.assertFalse(check_password('lètmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "sha1")
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ "seasalt",
+ "sha1",
+ )
def test_md5(self):
encoded = make_password('lètmein', 'seasalt', 'md5')
@@ -58,6 +81,14 @@
self.assertTrue(check_password('lètmein', encoded))
self.assertFalse(check_password('lètmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "md5")
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ "seasalt",
+ "md5",
+ )
def test_unsalted_md5(self):
encoded = make_password('lètmein', '', 'unsalted_md5')
@@ -71,6 +102,14 @@
self.assertTrue(is_password_usable(alt_encoded))
self.assertTrue(check_password('lètmein', alt_encoded))
self.assertFalse(check_password('lètmeinz', alt_encoded))
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ "",
+ "unsalted_md5",
+ )
def test_unsalted_sha1(self):
encoded = make_password('lètmein', '', 'unsalted_sha1')
@@ -82,6 +121,14 @@
# Raw SHA1 isn't acceptable
alt_encoded = encoded[6:]
self.assertFalse(check_password('lètmein', alt_encoded))
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ "",
+ "unslated_sha1",
+ )
@skipUnless(crypt, "no crypt module to generate password.")
def test_crypt(self):
@@ -91,6 +138,14 @@
self.assertTrue(check_password('lètmei', encoded))
self.assertFalse(check_password('lètmeiz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "crypt")
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ "seasalt",
+ "crypt",
+ )
@skipUnless(bcrypt, "py-bcrypt not installed")
def test_bcrypt(self):
@@ -100,6 +155,13 @@
self.assertTrue(check_password('lètmein', encoded))
self.assertFalse(check_password('lètmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt")
+ # Long password
+ self.assertRaises(
+ ValueError,
+ make_password,
+ b"1" * (MAXIMUM_PASSWORD_LENGTH + 1),
+ hasher="bcrypt",
+ )
def test_unusable(self):
encoded = make_password(None)
@@ -121,6 +183,14 @@
self.assertFalse(is_password_usable('lètmein_badencoded'))
self.assertFalse(is_password_usable(''))
+ def test_max_password_length_decorator(self):
+ @password_max_length(10)
+ def encode(s, password, salt):
+ return True
+
+ self.assertTrue(encode(None, b"1234", b"1234"))
+ self.assertRaises(ValueError, encode, None, b"1234567890A", b"1234")
+
def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('lètmein', 'seasalt')
diff --git a/lib/django-1.5/django/contrib/auth/tests/urls_admin.py b/lib/django-1.5/django/contrib/auth/tests/urls_admin.py
new file mode 100644
index 0000000..14a38e4
--- /dev/null
+++ b/lib/django-1.5/django/contrib/auth/tests/urls_admin.py
@@ -0,0 +1,18 @@
+"""
+Test URLs for auth admins.
+"""
+
+from django.conf.urls import patterns, include
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin, GroupAdmin
+from django.contrib.auth.models import User, Group
+from django.contrib.auth.urls import urlpatterns
+
+# Create a silo'd admin site for just the user/group admins.
+site = admin.AdminSite(name='auth_test_admin')
+site.register(User, UserAdmin)
+site.register(Group, GroupAdmin)
+
+urlpatterns = urlpatterns + patterns('',
+ (r'^admin/', include(site.urls)),
+)
diff --git a/lib/django-1.5/django/contrib/auth/tests/views.py b/lib/django-1.5/django/contrib/auth/tests/views.py
index 1fa933b..754fa35 100644
--- a/lib/django-1.5/django/contrib/auth/tests/views.py
+++ b/lib/django-1.5/django/contrib/auth/tests/views.py
@@ -7,18 +7,21 @@
from django.core import mail
from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse, NoReverseMatch
-from django.http import QueryDict
+from django.http import QueryDict, HttpRequest
from django.utils.encoding import force_text
from django.utils.html import escape
from django.utils.http import urlquote
from django.utils._os import upath
from django.test import TestCase
from django.test.utils import override_settings
+from django.middleware.csrf import CsrfViewMiddleware
+from django.contrib.sessions.middleware import SessionMiddleware
from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
SetPasswordForm, PasswordResetForm)
from django.contrib.auth.tests.utils import skipIfCustomUser
+from django.contrib.auth.views import login as login_view
@override_settings(
@@ -325,7 +328,8 @@
for bad_url in ('http://example.com',
'https://example.com',
'ftp://exampel.com',
- '//example.com'):
+ '//example.com',
+ 'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url,
@@ -346,6 +350,7 @@
'/view?param=ftp://exampel.com',
'view/?param=//example.com',
'https:///',
+ 'HTTPS:///',
'//testserver/',
'/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
@@ -361,6 +366,41 @@
self.assertTrue(good_url in response['Location'],
"%s should be allowed" % good_url)
+ def test_login_csrf_rotate(self, password='password'):
+ """
+ Makes sure that a login rotates the currently-used CSRF token.
+ """
+ # Do a GET to establish a CSRF token
+ # TestClient isn't used here as we're testing middleware, essentially.
+ req = HttpRequest()
+ CsrfViewMiddleware().process_view(req, login_view, (), {})
+ req.META["CSRF_COOKIE_USED"] = True
+ resp = login_view(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
+ token1 = csrf_cookie.coded_value
+
+ # Prepare the POST request
+ req = HttpRequest()
+ req.COOKIES[settings.CSRF_COOKIE_NAME] = token1
+ req.method = "POST"
+ req.POST = {'username': 'testclient', 'password': password, 'csrfmiddlewaretoken': token1}
+ req.REQUEST = req.POST
+
+ # Use POST request to log in
+ SessionMiddleware().process_request(req)
+ CsrfViewMiddleware().process_view(req, login_view, (), {})
+ req.META["SERVER_NAME"] = "testserver" # Required to have redirect work in login view
+ req.META["SERVER_PORT"] = 80
+ req.META["CSRF_COOKIE_USED"] = True
+ resp = login_view(req)
+ resp2 = CsrfViewMiddleware().process_response(req, resp)
+ csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
+ token2 = csrf_cookie.coded_value
+
+ # Check the CSRF token switched
+ self.assertNotEqual(token1, token2)
+
@skipIfCustomUser
class LoginURLSettings(AuthViewsTestCase):
@@ -484,7 +524,8 @@
for bad_url in ('http://example.com',
'https://example.com',
'ftp://exampel.com',
- '//example.com'):
+ '//example.com',
+ 'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
@@ -503,6 +544,7 @@
'/view?param=ftp://exampel.com',
'view/?param=//example.com',
'https:///',
+ 'HTTPS:///',
'//testserver/',
'/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
@@ -516,3 +558,18 @@
self.assertTrue(good_url in response['Location'],
"%s should be allowed" % good_url)
self.confirm_logged_out()
+
+@skipIfCustomUser
+class ChangelistTests(AuthViewsTestCase):
+ urls = 'django.contrib.auth.tests.urls_admin'
+
+ # #20078 - users shouldn't be allowed to guess password hashes via
+ # repeated password__startswith queries.
+ def test_changelist_disallows_password_lookups(self):
+ # Make me a superuser before loging in.
+ User.objects.filter(username='testclient').update(is_staff=True, is_superuser=True)
+ self.login()
+
+ # A lookup that tries to filter on password isn't OK
+ with self.assertRaises(SuspiciousOperation):
+ response = self.client.get('/admin/auth/user/?password__startswith=sha1$')
diff --git a/lib/django-1.5/django/contrib/contenttypes/generic.py b/lib/django-1.5/django/contrib/contenttypes/generic.py
index 5690e6b..e83c83a 100644
--- a/lib/django-1.5/django/contrib/contenttypes/generic.py
+++ b/lib/django-1.5/django/contrib/contenttypes/generic.py
@@ -432,8 +432,8 @@
"""
Returns a ``GenericInlineFormSet`` for the given kwargs.
- You must provide ``ct_field`` and ``object_id`` if they different from the
- defaults ``content_type`` and ``object_id`` respectively.
+ You must provide ``ct_field`` and ``fk_field`` if they are different from
+ the defaults ``content_type`` and ``object_id`` respectively.
"""
opts = model._meta
# Avoid a circular import.
diff --git a/lib/django-1.5/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po
index f31fc2a..5f9a319 100644
--- a/lib/django-1.5/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/contenttypes/locale/ka/LC_MESSAGES/django.po
@@ -1,14 +1,16 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
-"PO-Revision-Date: 2012-03-08 11:45+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 08:10+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -32,14 +34,14 @@
#: views.py:17
#, python-format
msgid "Content type %(ct_id)s object has no associated model"
-msgstr ""
+msgstr "კონტენტის ტიპის %(ct_id)s ობიექტს არ გააჩნია ასოცირებული მოდელი"
#: views.py:21
#, python-format
msgid "Content type %(ct_id)s object %(obj_id)s doesn't exist"
-msgstr ""
+msgstr "კონტენტის ტიპის %(ct_id)s ობიექტი %(obj_id)s არ არსებობს"
#: views.py:27
#, python-format
msgid "%(ct_name)s objects don't have a get_absolute_url() method"
-msgstr ""
+msgstr "%(ct_name)s ობიექტებს არ გააჩნიათ მეთოდი get_absolute_url()"
diff --git a/lib/django-1.5/django/contrib/contenttypes/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/contenttypes/locale/th/LC_MESSAGES/django.po
index b5ce73d..0176247 100644
--- a/lib/django-1.5/django/contrib/contenttypes/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/contenttypes/locale/th/LC_MESSAGES/django.po
@@ -9,7 +9,7 @@
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
"PO-Revision-Date: 2012-03-09 03:09+0000\n"
-"Last-Translator: Kowit Charoenratchatabhan <kowito@gmail.com>\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
diff --git a/lib/django-1.5/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po
index 4f1108f..004add3 100644
--- a/lib/django-1.5/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/flatpages/locale/ka/LC_MESSAGES/django.po
@@ -1,6 +1,8 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# avsd05 <avsd05@gmail.com>, 2011.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
@@ -8,8 +10,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
-"PO-Revision-Date: 2012-10-18 10:56+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 08:31+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -43,16 +45,16 @@
#: forms.py:19
msgid "URL is missing a leading slash."
-msgstr ""
+msgstr "URL-ს დასაწყისში აკლია დახრილი ხაზი."
#: forms.py:23
msgid "URL is missing a trailing slash."
-msgstr ""
+msgstr "URL-ს ბოლოში აკლია დახრილი ხაზი."
#: forms.py:38
#, python-format
msgid "Flatpage with url %(url)s already exists for site %(site)s"
-msgstr ""
+msgstr "უბრალო გვერდი url-ით %(url)s უკვე არსებობს საიტისთვის %(site)s"
#: models.py:12
msgid "title"
diff --git a/lib/django-1.5/django/contrib/flatpages/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/flatpages/locale/th/LC_MESSAGES/django.po
index 53f4bcb..6f84bfb 100644
--- a/lib/django-1.5/django/contrib/flatpages/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/flatpages/locale/th/LC_MESSAGES/django.po
@@ -9,7 +9,7 @@
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
"PO-Revision-Date: 2012-10-18 10:56+0000\n"
-"Last-Translator: Kowit Charoenratchatabhan <kowito@gmail.com>\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
diff --git a/lib/django-1.5/django/contrib/flatpages/tests/csrf.py b/lib/django-1.5/django/contrib/flatpages/tests/csrf.py
index e64c4bb..59996d9 100644
--- a/lib/django-1.5/django/contrib/flatpages/tests/csrf.py
+++ b/lib/django-1.5/django/contrib/flatpages/tests/csrf.py
@@ -1,6 +1,7 @@
import os
from django.conf import settings
from django.contrib.auth.models import User
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TestCase, Client
from django.test.utils import override_settings
@@ -38,6 +39,7 @@
response = self.client.get('/flatpage_root/no_such_flatpage/')
self.assertEqual(response.status_code, 404)
+ @skipIfCustomUser
def test_view_authenticated_flatpage(self):
"A flatpage served through a view can require authentication"
response = self.client.get('/flatpage_root/sekrit/')
diff --git a/lib/django-1.5/django/contrib/flatpages/tests/middleware.py b/lib/django-1.5/django/contrib/flatpages/tests/middleware.py
index 4afa4ff..1ad8fa9 100644
--- a/lib/django-1.5/django/contrib/flatpages/tests/middleware.py
+++ b/lib/django-1.5/django/contrib/flatpages/tests/middleware.py
@@ -1,6 +1,7 @@
import os
from django.conf import settings
from django.contrib.auth.models import User
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.flatpages.models import FlatPage
from django.test import TestCase
from django.test.utils import override_settings
@@ -36,6 +37,7 @@
response = self.client.get('/flatpage_root/no_such_flatpage/')
self.assertEqual(response.status_code, 404)
+ @skipIfCustomUser
def test_view_authenticated_flatpage(self):
"A flatpage served through a view can require authentication"
response = self.client.get('/flatpage_root/sekrit/')
@@ -57,6 +59,7 @@
response = self.client.get('/no_such_flatpage/')
self.assertEqual(response.status_code, 404)
+ @skipIfCustomUser
def test_fallback_authenticated_flatpage(self):
"A flatpage served by the middleware can require authentication"
response = self.client.get('/sekrit/')
diff --git a/lib/django-1.5/django/contrib/flatpages/tests/templatetags.py b/lib/django-1.5/django/contrib/flatpages/tests/templatetags.py
index aebc622..1a6f3c2 100644
--- a/lib/django-1.5/django/contrib/flatpages/tests/templatetags.py
+++ b/lib/django-1.5/django/contrib/flatpages/tests/templatetags.py
@@ -1,6 +1,7 @@
import os
from django.conf import settings
from django.contrib.auth.models import AnonymousUser, User
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase
from django.test.utils import override_settings
@@ -24,9 +25,6 @@
fixtures = ['sample_flatpages']
urls = 'django.contrib.flatpages.tests.urls'
- def setUp(self):
- self.me = User.objects.create_user('testuser', 'test@example.com', 's3krit')
-
def test_get_flatpages_tag(self):
"The flatpage template tag retrives unregistered prefixed flatpages by default"
out = Template(
@@ -51,8 +49,10 @@
}))
self.assertEqual(out, "A Flatpage,A Nested Flatpage,")
+ @skipIfCustomUser
def test_get_flatpages_tag_for_user(self):
"The flatpage template tag retrives all flatpages for an authenticated user"
+ me = User.objects.create_user('testuser', 'test@example.com', 's3krit')
out = Template(
"{% load flatpages %}"
"{% get_flatpages for me as flatpages %}"
@@ -60,7 +60,7 @@
"{{ page.title }},"
"{% endfor %}"
).render(Context({
- 'me': self.me
+ 'me': me
}))
self.assertEqual(out, "A Flatpage,A Nested Flatpage,Sekrit Nested Flatpage,Sekrit Flatpage,")
@@ -88,8 +88,10 @@
}))
self.assertEqual(out, "A Nested Flatpage,")
+ @skipIfCustomUser
def test_get_flatpages_with_prefix_for_user(self):
"The flatpage template tag retrive prefixed flatpages for an authenticated user"
+ me = User.objects.create_user('testuser', 'test@example.com', 's3krit')
out = Template(
"{% load flatpages %}"
"{% get_flatpages '/location/' for me as location_flatpages %}"
@@ -97,7 +99,7 @@
"{{ page.title }},"
"{% endfor %}"
).render(Context({
- 'me': self.me
+ 'me': me
}))
self.assertEqual(out, "A Nested Flatpage,Sekrit Nested Flatpage,")
diff --git a/lib/django-1.5/django/contrib/flatpages/tests/views.py b/lib/django-1.5/django/contrib/flatpages/tests/views.py
index b69bd6a..0448b21 100644
--- a/lib/django-1.5/django/contrib/flatpages/tests/views.py
+++ b/lib/django-1.5/django/contrib/flatpages/tests/views.py
@@ -1,6 +1,7 @@
import os
from django.conf import settings
from django.contrib.auth.models import User
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.flatpages.models import FlatPage
from django.test import TestCase
from django.test.utils import override_settings
@@ -36,6 +37,7 @@
response = self.client.get('/flatpage_root/no_such_flatpage/')
self.assertEqual(response.status_code, 404)
+ @skipIfCustomUser
def test_view_authenticated_flatpage(self):
"A flatpage served through a view can require authentication"
response = self.client.get('/flatpage_root/sekrit/')
diff --git a/lib/django-1.5/django/contrib/formtools/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/formtools/locale/ka/LC_MESSAGES/django.po
index 2b530da..9427cb3 100644
--- a/lib/django-1.5/django/contrib/formtools/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/formtools/locale/ka/LC_MESSAGES/django.po
@@ -1,14 +1,16 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
-"PO-Revision-Date: 2012-02-14 13:42+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 08:00+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -19,15 +21,15 @@
#: templates/formtools/wizard/wizard_form.html:15
msgid "first step"
-msgstr ""
+msgstr "პირველი ეტაპი"
#: templates/formtools/wizard/wizard_form.html:16
msgid "prev step"
-msgstr ""
+msgstr "წინა ეტაპი"
#: templates/formtools/wizard/wizard_form.html:18
msgid "submit"
-msgstr ""
+msgstr "გაგზავნა"
#: wizard/legacy.py:159
msgid ""
diff --git a/lib/django-1.5/django/contrib/formtools/tests/wizard/cookiestorage.py b/lib/django-1.5/django/contrib/formtools/tests/wizard/cookiestorage.py
index 3f26b85..060e826 100644
--- a/lib/django-1.5/django/contrib/formtools/tests/wizard/cookiestorage.py
+++ b/lib/django-1.5/django/contrib/formtools/tests/wizard/cookiestorage.py
@@ -5,10 +5,12 @@
from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponse
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.formtools.wizard.storage.cookie import CookieStorage
from django.contrib.formtools.tests.wizard.storage import get_request, TestStorage
+@skipIfCustomUser
class TestCookieStorage(TestStorage, TestCase):
def get_storage(self):
return CookieStorage
diff --git a/lib/django-1.5/django/contrib/formtools/tests/wizard/forms.py b/lib/django-1.5/django/contrib/formtools/tests/wizard/forms.py
index 51cfaa6..f6e177e 100644
--- a/lib/django-1.5/django/contrib/formtools/tests/wizard/forms.py
+++ b/lib/django-1.5/django/contrib/formtools/tests/wizard/forms.py
@@ -2,6 +2,7 @@
from django import forms, http
from django.conf import settings
+from django.db import models
from django.test import TestCase
from django.template.response import TemplateResponse
from django.utils.importlib import import_module
@@ -29,26 +30,40 @@
request.session = engine.SessionStore(None)
return request
+
class Step1(forms.Form):
name = forms.CharField()
+
class Step2(forms.Form):
name = forms.CharField()
+
class Step3(forms.Form):
data = forms.CharField()
+
class CustomKwargsStep1(Step1):
def __init__(self, test=None, *args, **kwargs):
self.test = test
return super(CustomKwargsStep1, self).__init__(*args, **kwargs)
-class UserForm(forms.ModelForm):
- class Meta:
- model = User
-UserFormSet = forms.models.modelformset_factory(User, form=UserForm, extra=2)
+class TestModel(models.Model):
+ name = models.CharField(max_length=100)
+
+ class Meta:
+ app_label = 'formtools'
+
+
+class TestModelForm(forms.ModelForm):
+ class Meta:
+ model = TestModel
+
+
+TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelForm, extra=2)
+
class TestWizard(WizardView):
storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
@@ -149,8 +164,8 @@
def test_form_instance(self):
request = get_request()
- the_instance = User()
- testform = TestWizard.as_view([('start', UserForm), ('step2', Step2)],
+ the_instance = TestModel()
+ testform = TestWizard.as_view([('start', TestModelForm), ('step2', Step2)],
instance_dict={'start': the_instance})
response, instance = testform(request)
@@ -163,12 +178,12 @@
def test_formset_instance(self):
request = get_request()
- the_instance1, created = User.objects.get_or_create(
- username='testuser1')
- the_instance2, created = User.objects.get_or_create(
- username='testuser2')
- testform = TestWizard.as_view([('start', UserFormSet), ('step2', Step2)],
- instance_dict={'start': User.objects.filter(username='testuser1')})
+ the_instance1, created = TestModel.objects.get_or_create(
+ name='test object 1')
+ the_instance2, created = TestModel.objects.get_or_create(
+ name='test object 2')
+ testform = TestWizard.as_view([('start', TestModelFormSet), ('step2', Step2)],
+ instance_dict={'start': TestModel.objects.filter(name='test object 1')})
response, instance = testform(request)
self.assertEqual(list(instance.get_form_instance('start')), [the_instance1])
diff --git a/lib/django-1.5/django/contrib/formtools/tests/wizard/namedwizardtests/tests.py b/lib/django-1.5/django/contrib/formtools/tests/wizard/namedwizardtests/tests.py
index 7529d89..7b13445 100644
--- a/lib/django-1.5/django/contrib/formtools/tests/wizard/namedwizardtests/tests.py
+++ b/lib/django-1.5/django/contrib/formtools/tests/wizard/namedwizardtests/tests.py
@@ -5,6 +5,7 @@
from django.test import TestCase
from django.contrib.auth.models import User
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.formtools.wizard.views import (NamedUrlSessionWizardView,
NamedUrlCookieWizardView)
@@ -276,6 +277,7 @@
self.assertEqual(response.context['wizard']['steps'].current, 'form1')
+@skipIfCustomUser
class NamedSessionWizardTests(NamedWizardTests, TestCase):
wizard_urlname = 'nwiz_session'
wizard_step_1_data = {
@@ -307,6 +309,7 @@
)
+@skipIfCustomUser
class NamedCookieWizardTests(NamedWizardTests, TestCase):
wizard_urlname = 'nwiz_cookie'
wizard_step_1_data = {
@@ -367,11 +370,13 @@
return response, self
+@skipIfCustomUser
class NamedSessionFormTests(NamedFormTests, TestCase):
formwizard_class = TestNamedUrlSessionWizardView
wizard_urlname = 'nwiz_session'
+@skipIfCustomUser
class NamedCookieFormTests(NamedFormTests, TestCase):
formwizard_class = TestNamedUrlCookieWizardView
wizard_urlname = 'nwiz_cookie'
diff --git a/lib/django-1.5/django/contrib/formtools/tests/wizard/sessionstorage.py b/lib/django-1.5/django/contrib/formtools/tests/wizard/sessionstorage.py
index 8541072..0bd9fd8 100644
--- a/lib/django-1.5/django/contrib/formtools/tests/wizard/sessionstorage.py
+++ b/lib/django-1.5/django/contrib/formtools/tests/wizard/sessionstorage.py
@@ -1,9 +1,11 @@
from django.test import TestCase
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.formtools.tests.wizard.storage import TestStorage
from django.contrib.formtools.wizard.storage.session import SessionStorage
+@skipIfCustomUser
class TestSessionStorage(TestStorage, TestCase):
def get_storage(self):
return SessionStorage
diff --git a/lib/django-1.5/django/contrib/formtools/tests/wizard/wizardtests/tests.py b/lib/django-1.5/django/contrib/formtools/tests/wizard/wizardtests/tests.py
index 4aaea7d..1ee5dbd 100644
--- a/lib/django-1.5/django/contrib/formtools/tests/wizard/wizardtests/tests.py
+++ b/lib/django-1.5/django/contrib/formtools/tests/wizard/wizardtests/tests.py
@@ -7,11 +7,19 @@
from django.test.client import RequestFactory
from django.conf import settings
from django.contrib.auth.models import User
+from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.formtools.wizard.views import CookieWizardView
-from django.contrib.formtools.tests.wizard.forms import UserForm, UserFormSet
from django.utils._os import upath
+class UserForm(forms.ModelForm):
+ class Meta:
+ model = User
+
+
+UserFormSet = forms.models.modelformset_factory(User, form=UserForm, extra=2)
+
+
class WizardTests(object):
urls = 'django.contrib.formtools.tests.wizard.wizardtests.urls'
@@ -75,7 +83,7 @@
# ticket #19025: `form` should be included in context
form = response.context_data['wizard']['form']
- self.assertEqual(response.context_data['form'], form)
+ self.assertEqual(response.context_data['form'], form)
def test_form_finish(self):
response = self.client.get(self.wizard_url)
@@ -196,6 +204,7 @@
self.assertEqual(response.status_code, 200)
+@skipIfCustomUser
class SessionWizardTests(WizardTests, TestCase):
wizard_url = '/wiz_session/'
wizard_step_1_data = {
@@ -226,6 +235,8 @@
}
)
+
+@skipIfCustomUser
class CookieWizardTests(WizardTests, TestCase):
wizard_url = '/wiz_cookie/'
wizard_step_1_data = {
@@ -256,6 +267,8 @@
}
)
+
+@skipIfCustomUser
class WizardTestKwargs(TestCase):
wizard_url = '/wiz_other_template/'
wizard_step_1_data = {
@@ -347,6 +360,7 @@
self.assertEqual(response.context_data['another_key'], 'another_value')
+@skipIfCustomUser
class WizardFormKwargsOverrideTests(TestCase):
def setUp(self):
super(WizardFormKwargsOverrideTests, self).setUp()
diff --git a/lib/django-1.5/django/contrib/gis/db/backends/spatialite/operations.py b/lib/django-1.5/django/contrib/gis/db/backends/spatialite/operations.py
index 5eaa778..151ecfe 100644
--- a/lib/django-1.5/django/contrib/gis/db/backends/spatialite/operations.py
+++ b/lib/django-1.5/django/contrib/gis/db/backends/spatialite/operations.py
@@ -10,6 +10,8 @@
from django.db.backends.sqlite3.base import DatabaseOperations
from django.db.utils import DatabaseError
from django.utils import six
+from django.utils.functional import cached_property
+
class SpatiaLiteOperator(SpatialOperation):
"For SpatiaLite operators (e.g. `&&`, `~`)."
@@ -146,8 +148,10 @@
except DatabaseError:
# we are using < 2.4.0-RC4
pass
- if version >= (3, 0, 0):
- self.geojson = 'AsGeoJSON'
+
+ @cached_property
+ def geojson(self):
+ return 'AsGeoJSON' if self.spatialite_version_tuple()[1:] >= (3, 0, 0) else None
def check_aggregate_support(self, aggregate):
"""
diff --git a/lib/django-1.5/django/contrib/gis/geos/libgeos.py b/lib/django-1.5/django/contrib/gis/geos/libgeos.py
index f011208..05e5d12 100644
--- a/lib/django-1.5/django/contrib/gis/geos/libgeos.py
+++ b/lib/django-1.5/django/contrib/gis/geos/libgeos.py
@@ -108,8 +108,11 @@
geos_version.restype = c_char_p
# Regular expression should be able to parse version strings such as
-# '3.0.0rc4-CAPI-1.3.3', '3.0.0-CAPI-1.4.1' or '3.4.0dev-CAPI-1.8.0'
-version_regex = re.compile(r'^(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<subminor>\d+))((rc(?P<release_candidate>\d+))|dev)?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)$')
+# '3.0.0rc4-CAPI-1.3.3', '3.0.0-CAPI-1.4.1', '3.4.0dev-CAPI-1.8.0' or '3.4.0dev-CAPI-1.8.0 r0'
+version_regex = re.compile(
+ r'^(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<subminor>\d+))'
+ r'((rc(?P<release_candidate>\d+))|dev)?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)( r\d+)?$'
+)
def geos_version_info():
"""
Returns a dictionary containing the various version metadata parsed from
@@ -119,8 +122,10 @@
"""
ver = geos_version().decode()
m = version_regex.match(ver)
- if not m: raise GEOSException('Could not parse version info string "%s"' % ver)
- return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
+ if not m:
+ raise GEOSException('Could not parse version info string "%s"' % ver)
+ return dict((key, m.group(key)) for key in (
+ 'version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
# Version numbers and whether or not prepared geometry support is available.
_verinfo = geos_version_info()
diff --git a/lib/django-1.5/django/contrib/gis/geos/tests/test_geos.py b/lib/django-1.5/django/contrib/gis/geos/tests/test_geos.py
index c15833f..e2adf06 100644
--- a/lib/django-1.5/django/contrib/gis/geos/tests/test_geos.py
+++ b/lib/django-1.5/django/contrib/gis/geos/tests/test_geos.py
@@ -1063,15 +1063,17 @@
self.assertEqual(mls.interpolate(17), Point(10, 7))
def test_geos_version(self):
- "Testing the GEOS version regular expression."
+ """Testing the GEOS version regular expression."""
from django.contrib.gis.geos.libgeos import version_regex
- versions = [ ('3.0.0rc4-CAPI-1.3.3', '3.0.0'),
- ('3.0.0-CAPI-1.4.1', '3.0.0'),
- ('3.4.0dev-CAPI-1.8.0', '3.4.0') ]
- for v, expected in versions:
- m = version_regex.match(v)
- self.assertTrue(m)
- self.assertEqual(m.group('version'), expected)
+ versions = [('3.0.0rc4-CAPI-1.3.3', '3.0.0', '1.3.3'),
+ ('3.0.0-CAPI-1.4.1', '3.0.0', '1.4.1'),
+ ('3.4.0dev-CAPI-1.8.0', '3.4.0', '1.8.0'),
+ ('3.4.0dev-CAPI-1.8.0 r0', '3.4.0', '1.8.0')]
+ for v_init, v_geos, v_capi in versions:
+ m = version_regex.match(v_init)
+ self.assertTrue(m, msg="Unable to parse the version string '%s'" % v_init)
+ self.assertEqual(m.group('version'), v_geos)
+ self.assertEqual(m.group('capi_version'), v_capi)
def suite():
diff --git a/lib/django-1.5/django/contrib/gis/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/gis/locale/ka/LC_MESSAGES/django.po
index 1ae3979..d61774c 100644
--- a/lib/django-1.5/django/contrib/gis/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/gis/locale/ka/LC_MESSAGES/django.po
@@ -1,6 +1,8 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# avsd05 <avsd05@gmail.com>, 2011.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
@@ -8,8 +10,8 @@
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
-"PO-Revision-Date: 2012-03-08 12:32+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 07:50+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -20,12 +22,12 @@
#: views.py:9
msgid "No feeds are registered."
-msgstr ""
+msgstr "არცერთი ფიდი არ არის რეგისტრირებული."
#: views.py:19
#, python-format
msgid "Slug %r isn't registered."
-msgstr ""
+msgstr "სლაგი %r არ არის რეგისტრირებული."
#: db/models/fields.py:51
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
@@ -80,14 +82,14 @@
#: sitemaps/views.py:46
#, python-format
msgid "No sitemap available for section: %r"
-msgstr ""
+msgstr "არ არსებობს საიტის რუკა სექციისთვის: %r"
#: sitemaps/views.py:60
#, python-format
msgid "Page %s empty"
-msgstr ""
+msgstr "გვერდი %s ცარიელია"
#: sitemaps/views.py:62
#, python-format
msgid "No page '%s'"
-msgstr ""
+msgstr "გვერდი '%s' არ არსებობს"
diff --git a/lib/django-1.5/django/contrib/gis/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/gis/locale/th/LC_MESSAGES/django.po
index 81cf609..19d42d4 100644
--- a/lib/django-1.5/django/contrib/gis/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/gis/locale/th/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:56+0200\n"
"PO-Revision-Date: 2012-03-09 04:19+0000\n"
-"Last-Translator: Kowit Charoenratchatabhan <kowito@gmail.com>\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
diff --git a/lib/django-1.5/django/contrib/gis/maps/google/gmap.py b/lib/django-1.5/django/contrib/gis/maps/google/gmap.py
index 75b285c..ff65626 100644
--- a/lib/django-1.5/django/contrib/gis/maps/google/gmap.py
+++ b/lib/django-1.5/django/contrib/gis/maps/google/gmap.py
@@ -134,7 +134,8 @@
@property
def scripts(self):
"Returns all <script></script> tags required with Google Maps JavaScript."
- return format_html('%s\n <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n </script>', self.api_script, mark_safe(self.js))
+ return format_html('{0}\n <script type="text/javascript">\n//<![CDATA[\n{1}//]]>\n </script>',
+ self.api_script, mark_safe(self.js))
@property
def style(self):
diff --git a/lib/django-1.5/django/contrib/gis/tests/geoadmin/tests.py b/lib/django-1.5/django/contrib/gis/tests/geoadmin/tests.py
index 6fadebd..ecd387d 100644
--- a/lib/django-1.5/django/contrib/gis/tests/geoadmin/tests.py
+++ b/lib/django-1.5/django/contrib/gis/tests/geoadmin/tests.py
@@ -1,11 +1,14 @@
from __future__ import absolute_import
-from django.test import TestCase
from django.contrib.gis import admin
from django.contrib.gis.geos import GEOSGeometry, Point
+from django.test import TestCase
+from django.test.utils import override_settings
from .models import City
+GOOGLE_MAPS_API_KEY = 'XXXX'
+
class GeoAdminTest(TestCase):
urls = 'django.contrib.gis.tests.geoadmin.urls'
@@ -35,7 +38,9 @@
result)
def test_olwidget_has_changed(self):
- """ Check that changes are accurately noticed by OpenLayersWidget. """
+ """
+ Check that changes are accurately noticed by OpenLayersWidget.
+ """
geoadmin = admin.site._registry[City]
form = geoadmin.get_changelist_form(None)()
has_changed = form.fields['point'].widget._has_changed
@@ -51,3 +56,15 @@
self.assertFalse(has_changed(initial, data_same))
self.assertFalse(has_changed(initial, data_almost_same))
self.assertTrue(has_changed(initial, data_changed))
+
+ @override_settings(GOOGLE_MAPS_API_KEY=GOOGLE_MAPS_API_KEY)
+ def test_google_map_scripts(self):
+ """
+ Testing GoogleMap.scripts() output. See #20773.
+ """
+ from django.contrib.gis.maps.google.gmap import GoogleMap
+
+ google_map = GoogleMap()
+ scripts = google_map.scripts
+ self.assertIn(GOOGLE_MAPS_API_KEY, scripts)
+ self.assertIn("new GMap2", scripts)
diff --git a/lib/django-1.5/django/contrib/humanize/locale/es_MX/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/humanize/locale/es_MX/LC_MESSAGES/django.po
index da4e1b8..b108c0b 100644
--- a/lib/django-1.5/django/contrib/humanize/locale/es_MX/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/humanize/locale/es_MX/LC_MESSAGES/django.po
@@ -257,14 +257,14 @@
msgid "a minute ago"
msgid_plural "%(count)s minutes ago"
msgstr[0] "Hace un minuto"
-msgstr[1] "Hace %s minutos"
+msgstr[1] "Hace %(count)s minutos"
#: templatetags/humanize.py:206
#, python-format
msgid "an hour ago"
msgid_plural "%(count)s hours ago"
msgstr[0] "Hace una hora"
-msgstr[1] "Hace %s horas"
+msgstr[1] "Hace %(count)s horas"
#: templatetags/humanize.py:212
#, python-format
diff --git a/lib/django-1.5/django/contrib/humanize/locale/ka/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/humanize/locale/ka/LC_MESSAGES/django.po
index 10b34e6..812302a 100644
--- a/lib/django-1.5/django/contrib/humanize/locale/ka/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/humanize/locale/ka/LC_MESSAGES/django.po
@@ -1,14 +1,16 @@
# This file is distributed under the same license as the Django package.
#
# Translators:
+# Translators:
+# André Bouatchidzé <a@anbz.net>, 2013.
# Jannis Leidel <jannis@leidel.info>, 2011.
msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:57+0200\n"
-"PO-Revision-Date: 2012-02-14 13:08+0000\n"
-"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
+"PO-Revision-Date: 2013-03-01 10:40+0000\n"
+"Last-Translator: André Bouatchidzé <a@anbz.net>\n"
"Language-Team: Georgian (http://www.transifex.com/projects/p/django/language/"
"ka/)\n"
"MIME-Version: 1.0\n"
@@ -55,7 +57,7 @@
#, python-format
msgid "%(value)s million"
msgid_plural "%(value)s million"
-msgstr[0] ""
+msgstr[0] "%(value)s მილიონი"
#: templatetags/humanize.py:58
#, python-format
@@ -67,7 +69,7 @@
#, python-format
msgid "%(value)s billion"
msgid_plural "%(value)s billion"
-msgstr[0] ""
+msgstr[0] "%(value)s მილიარდი"
#: templatetags/humanize.py:62
#, python-format
@@ -79,103 +81,103 @@
#, python-format
msgid "%(value)s trillion"
msgid_plural "%(value)s trillion"
-msgstr[0] ""
+msgstr[0] "%(value)s ტრილიონი"
#: templatetags/humanize.py:66
#, python-format
msgid "%(value).1f quadrillion"
msgid_plural "%(value).1f quadrillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f კვადრილიონი"
#: templatetags/humanize.py:67
#, python-format
msgid "%(value)s quadrillion"
msgid_plural "%(value)s quadrillion"
-msgstr[0] ""
+msgstr[0] "%(value)s კვადრილიონი"
#: templatetags/humanize.py:70
#, python-format
msgid "%(value).1f quintillion"
msgid_plural "%(value).1f quintillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f კვინტილიონი"
#: templatetags/humanize.py:71
#, python-format
msgid "%(value)s quintillion"
msgid_plural "%(value)s quintillion"
-msgstr[0] ""
+msgstr[0] "%(value)s კვინტილიონი"
#: templatetags/humanize.py:74
#, python-format
msgid "%(value).1f sextillion"
msgid_plural "%(value).1f sextillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f სექსტილიონი"
#: templatetags/humanize.py:75
#, python-format
msgid "%(value)s sextillion"
msgid_plural "%(value)s sextillion"
-msgstr[0] ""
+msgstr[0] "%(value)s სექსტილიონი"
#: templatetags/humanize.py:78
#, python-format
msgid "%(value).1f septillion"
msgid_plural "%(value).1f septillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f სეპტილიონი"
#: templatetags/humanize.py:79
#, python-format
msgid "%(value)s septillion"
msgid_plural "%(value)s septillion"
-msgstr[0] ""
+msgstr[0] "%(value)s სეპტილიონი"
#: templatetags/humanize.py:82
#, python-format
msgid "%(value).1f octillion"
msgid_plural "%(value).1f octillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f ოქტილიონი"
#: templatetags/humanize.py:83
#, python-format
msgid "%(value)s octillion"
msgid_plural "%(value)s octillion"
-msgstr[0] ""
+msgstr[0] "%(value)s ოქტილიონი"
#: templatetags/humanize.py:86
#, python-format
msgid "%(value).1f nonillion"
msgid_plural "%(value).1f nonillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f ნონილიონი"
#: templatetags/humanize.py:87
#, python-format
msgid "%(value)s nonillion"
msgid_plural "%(value)s nonillion"
-msgstr[0] ""
+msgstr[0] "%(value)s ნონილიონი"
#: templatetags/humanize.py:90
#, python-format
msgid "%(value).1f decillion"
msgid_plural "%(value).1f decillion"
-msgstr[0] ""
+msgstr[0] "%(value).1f დეცილიონი"
#: templatetags/humanize.py:91
#, python-format
msgid "%(value)s decillion"
msgid_plural "%(value)s decillion"
-msgstr[0] ""
+msgstr[0] "%(value)s დეცილიონი"
#: templatetags/humanize.py:94
#, python-format
msgid "%(value).1f googol"
msgid_plural "%(value).1f googol"
-msgstr[0] ""
+msgstr[0] "%(value).1f გუგოლი"
#: templatetags/humanize.py:95
#, python-format
msgid "%(value)s googol"
msgid_plural "%(value)s googol"
-msgstr[0] ""
+msgstr[0] "%(value)s გუგოლი"
#: templatetags/humanize.py:144
msgid "one"
@@ -217,50 +219,50 @@
#, python-format
msgctxt "naturaltime"
msgid "%(delta)s ago"
-msgstr ""
+msgstr "%(delta)s-ის წინ"
#: templatetags/humanize.py:193 templatetags/humanize.py:215
msgid "now"
-msgstr ""
+msgstr "ახლა"
#: templatetags/humanize.py:196
#, python-format
msgid "a second ago"
msgid_plural "%(count)s seconds ago"
-msgstr[0] ""
+msgstr[0] "%(count)s წამის წინ"
#: templatetags/humanize.py:201
#, python-format
msgid "a minute ago"
msgid_plural "%(count)s minutes ago"
-msgstr[0] ""
+msgstr[0] "%(count)s წუთის წინ"
#: templatetags/humanize.py:206
#, python-format
msgid "an hour ago"
msgid_plural "%(count)s hours ago"
-msgstr[0] ""
+msgstr[0] "%(count)s საათის წინ"
#: templatetags/humanize.py:212
#, python-format
msgctxt "naturaltime"
msgid "%(delta)s from now"
-msgstr ""
+msgstr "%(delta)s-ის შემდეგ"
#: templatetags/humanize.py:218
#, python-format
msgid "a second from now"
msgid_plural "%(count)s seconds from now"
-msgstr[0] ""
+msgstr[0] "%(count)s წამის შემდეგ"
#: templatetags/humanize.py:223
#, python-format
msgid "a minute from now"
msgid_plural "%(count)s minutes from now"
-msgstr[0] ""
+msgstr[0] "%(count)s წუთის შემდეგ"
#: templatetags/humanize.py:228
#, python-format
msgid "an hour from now"
msgid_plural "%(count)s hours from now"
-msgstr[0] ""
+msgstr[0] "%(count)s საათის შემდეგ"
diff --git a/lib/django-1.5/django/contrib/humanize/locale/mn/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/humanize/locale/mn/LC_MESSAGES/django.po
index 5efb776..94419d6 100644
--- a/lib/django-1.5/django/contrib/humanize/locale/mn/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/humanize/locale/mn/LC_MESSAGES/django.po
@@ -258,14 +258,14 @@
msgid "a minute ago"
msgid_plural "%(count)s minutes ago"
msgstr[0] "минутын өмнө"
-msgstr[1] "%s минутын өмнө"
+msgstr[1] "%(count)s минутын өмнө"
#: templatetags/humanize.py:206
#, python-format
msgid "an hour ago"
msgid_plural "%(count)s hours ago"
msgstr[0] "цагын өмнө"
-msgstr[1] "%s цагын өмнө"
+msgstr[1] "%(count)s цагын өмнө"
#: templatetags/humanize.py:212
#, python-format
diff --git a/lib/django-1.5/django/contrib/humanize/locale/ro/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/humanize/locale/ro/LC_MESSAGES/django.po
index 2795c20..7d6b1e1 100644
--- a/lib/django-1.5/django/contrib/humanize/locale/ro/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/humanize/locale/ro/LC_MESSAGES/django.po
@@ -276,7 +276,7 @@
msgid_plural "%(count)s seconds ago"
msgstr[0] "acum o secundă"
msgstr[1] "acum %(count)s secunde"
-msgstr[2] ""
+msgstr[2] "acum %(count)s de secunde"
#: templatetags/humanize.py:201
#, python-format
diff --git a/lib/django-1.5/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po
index e7c42cb..7da9cc7 100644
--- a/lib/django-1.5/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/humanize/locale/tr/LC_MESSAGES/django.po
@@ -235,13 +235,13 @@
#, python-format
msgid "a minute ago"
msgid_plural "%(count)s minutes ago"
-msgstr[0] "%s dakika önce"
+msgstr[0] "%(count)s dakika önce"
#: templatetags/humanize.py:206
#, python-format
msgid "an hour ago"
msgid_plural "%(count)s hours ago"
-msgstr[0] "%s saat önce"
+msgstr[0] "%(count)s saat önce"
#: templatetags/humanize.py:212
#, python-format
diff --git a/lib/django-1.5/django/contrib/localflavor/__init__.py b/lib/django-1.5/django/contrib/localflavor/__init__.py
index 785186f..575b015 100644
--- a/lib/django-1.5/django/contrib/localflavor/__init__.py
+++ b/lib/django-1.5/django/contrib/localflavor/__init__.py
@@ -1,2 +1,5 @@
import warnings
-warnings.warn("django.contrib.localflavor is deprecated. Use the separate django-localflavor-* packages instead.", DeprecationWarning)
+
+warnings.warn("django.contrib.localflavor is deprecated. "
+ "Use the separate django-localflavor package instead.",
+ DeprecationWarning)
diff --git a/lib/django-1.5/django/contrib/messages/locale/th/LC_MESSAGES/django.po b/lib/django-1.5/django/contrib/messages/locale/th/LC_MESSAGES/django.po
index 720f0b7..5b1a95a 100644
--- a/lib/django-1.5/django/contrib/messages/locale/th/LC_MESSAGES/django.po
+++ b/lib/django-1.5/django/contrib/messages/locale/th/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-10-15 10:57+0200\n"
"PO-Revision-Date: 2012-02-14 13:16+0000\n"
-"Last-Translator: Kowit Charoenratchatabhan <kowito@gmail.com>\n"
+"Last-Translator: Kowit Charoenratchatabhan <kowit.s.c@gmail.com>\n"
"Language-Team: Thai (http://www.transifex.com/projects/p/django/language/"
"th/)\n"
"MIME-Version: 1.0\n"
diff --git a/lib/django-1.5/django/contrib/messages/storage/session.py b/lib/django-1.5/django/contrib/messages/storage/session.py
index 225dfda..c3e293c 100644
--- a/lib/django-1.5/django/contrib/messages/storage/session.py
+++ b/lib/django-1.5/django/contrib/messages/storage/session.py
@@ -1,4 +1,8 @@
+import json
+
from django.contrib.messages.storage.base import BaseStorage
+from django.contrib.messages.storage.cookie import MessageEncoder, MessageDecoder
+from django.utils import six
class SessionStorage(BaseStorage):
@@ -20,14 +24,23 @@
always stores everything it is given, so return True for the
all_retrieved flag.
"""
- return self.request.session.get(self.session_key), True
+ return self.deserialize_messages(self.request.session.get(self.session_key)), True
def _store(self, messages, response, *args, **kwargs):
"""
Stores a list of messages to the request's session.
"""
if messages:
- self.request.session[self.session_key] = messages
+ self.request.session[self.session_key] = self.serialize_messages(messages)
else:
self.request.session.pop(self.session_key, None)
return []
+
+ def serialize_messages(self, messages):
+ encoder = MessageEncoder(separators=(',', ':'))
+ return encoder.encode(messages)
+
+ def deserialize_messages(self, data):
+ if data and isinstance(data, six.string_types):
+ return json.loads(data, cls=MessageDecoder)
+ return data
diff --git a/lib/django-1.5/django/contrib/messages/tests/base.py b/lib/django-1.5/django/contrib/messages/tests/base.py
index b3ced12..7719d66 100644
--- a/lib/django-1.5/django/contrib/messages/tests/base.py
+++ b/lib/django-1.5/django/contrib/messages/tests/base.py
@@ -61,6 +61,7 @@
MESSAGE_TAGS = '',
MESSAGE_STORAGE = '%s.%s' % (self.storage_class.__module__,
self.storage_class.__name__),
+ SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer',
)
self.settings_override.enable()
diff --git a/lib/django-1.5/django/contrib/messages/tests/session.py b/lib/django-1.5/django/contrib/messages/tests/session.py
index e162f49..0d2daaa 100644
--- a/lib/django-1.5/django/contrib/messages/tests/session.py
+++ b/lib/django-1.5/django/contrib/messages/tests/session.py
@@ -10,13 +10,13 @@
Sets the messages into the backend request's session and remove the
backend's loaded data cache.
"""
- storage.request.session[storage.session_key] = messages
+ storage.request.session[storage.session_key] = storage.serialize_messages(messages)
if hasattr(storage, '_loaded_data'):
del storage._loaded_data
def stored_session_messages_count(storage):
- data = storage.request.session.get(storage.session_key, [])
+ data = storage.deserialize_messages(storage.request.session.get(storage.session_key, []))
return len(data)
diff --git a/lib/django-1.5/django/contrib/sessions/backends/base.py b/lib/django-1.5/django/contrib/sessions/backends/base.py
index f79a264..44b06d6 100644
--- a/lib/django-1.5/django/contrib/sessions/backends/base.py
+++ b/lib/django-1.5/django/contrib/sessions/backends/base.py
@@ -2,10 +2,6 @@
import base64
from datetime import datetime, timedelta
-try:
- from django.utils.six.moves import cPickle as pickle
-except ImportError:
- import pickle
import string
from django.conf import settings
@@ -15,6 +11,7 @@
from django.utils.crypto import salted_hmac
from django.utils import timezone
from django.utils.encoding import force_bytes
+from django.utils.module_loading import import_by_path
# session_key should not be case sensitive because some backends can store it
# on case insensitive file systems.
@@ -38,6 +35,7 @@
self._session_key = session_key
self.accessed = False
self.modified = False
+ self.serializer = import_by_path(settings.SESSION_SERIALIZER)
def __contains__(self, key):
return key in self._session
@@ -82,24 +80,25 @@
return salted_hmac(key_salt, value).hexdigest()
def encode(self, session_dict):
- "Returns the given session dictionary pickled and encoded as a string."
- pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
- hash = self._hash(pickled)
- return base64.b64encode(hash.encode() + b":" + pickled).decode('ascii')
+ "Returns the given session dictionary serialized and encoded as a string."
+ serialized = self.serializer().dumps(session_dict)
+ hash = self._hash(serialized)
+ return base64.b64encode(hash.encode() + b":" + serialized).decode('ascii')
def decode(self, session_data):
encoded_data = base64.b64decode(force_bytes(session_data))
try:
# could produce ValueError if there is no ':'
- hash, pickled = encoded_data.split(b':', 1)
- expected_hash = self._hash(pickled)
+ hash, serialized = encoded_data.split(b':', 1)
+ expected_hash = self._hash(serialized)
if not constant_time_compare(hash.decode(), expected_hash):
raise SuspiciousOperation("Session data corrupted")
else:
- return pickle.loads(pickled)
+ return self.serializer().loads(serialized)
except Exception:
- # ValueError, SuspiciousOperation, unpickling exceptions. If any of
- # these happen, just return an empty dictionary (an empty session).
+ # ValueError, SuspiciousOperation, deserialization exceptions. If
+ # any of these happen, just return an empty dictionary (an empty
+ # session).
return {}
def update(self, dict_):
diff --git a/lib/django-1.5/django/contrib/sessions/backends/signed_cookies.py b/lib/django-1.5/django/contrib/sessions/backends/signed_cookies.py
index c2b7a31..77a6750 100644
--- a/lib/django-1.5/django/contrib/sessions/backends/signed_cookies.py
+++ b/lib/django-1.5/django/contrib/sessions/backends/signed_cookies.py
@@ -1,26 +1,9 @@
-try:
- from django.utils.six.moves import cPickle as pickle
-except ImportError:
- import pickle
-
from django.conf import settings
from django.core import signing
from django.contrib.sessions.backends.base import SessionBase
-class PickleSerializer(object):
- """
- Simple wrapper around pickle to be used in signing.dumps and
- signing.loads.
- """
- def dumps(self, obj):
- return pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
-
- def loads(self, data):
- return pickle.loads(data)
-
-
class SessionStore(SessionBase):
def load(self):
@@ -31,7 +14,7 @@
"""
try:
return signing.loads(self.session_key,
- serializer=PickleSerializer,
+ serializer=self.serializer,
# This doesn't handle non-default expiry dates, see #19201
max_age=settings.SESSION_COOKIE_AGE,
salt='django.contrib.sessions.backends.signed_cookies')
@@ -91,7 +74,7 @@
session_cache = getattr(self, '_session_cache', {})
return signing.dumps(session_cache, compress=True,
salt='django.contrib.sessions.backends.signed_cookies',
- serializer=PickleSerializer)
+ serializer=self.serializer)
@classmethod
def clear_expired(cls):
diff --git a/lib/django-1.5/django/contrib/sessions/models.py b/lib/django-1.5/django/contrib/sessions/models.py
index 0179c35..3a6e311 100644
--- a/lib/django-1.5/django/contrib/sessions/models.py
+++ b/lib/django-1.5/django/contrib/sessions/models.py
@@ -5,7 +5,7 @@
class SessionManager(models.Manager):
def encode(self, session_dict):
"""
- Returns the given session dictionary pickled and encoded as a string.
+ Returns the given session dictionary serialized and encoded as a string.
"""
return SessionStore().encode(session_dict)
diff --git a/lib/django-1.5/django/contrib/sessions/serializers.py b/lib/django-1.5/django/contrib/sessions/serializers.py
new file mode 100644
index 0000000..92a31c0
--- /dev/null
+++ b/lib/django-1.5/django/contrib/sessions/serializers.py
@@ -0,0 +1,20 @@
+from django.core.signing import JSONSerializer as BaseJSONSerializer
+try:
+ from django.utils.six.moves import cPickle as pickle
+except ImportError:
+ import pickle
+
+
+class PickleSerializer(object):
+ """
+ Simple wrapper around pickle to be used in signing.dumps and
+ signing.loads.
+ """
+ def dumps(self, obj):
+ return pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
+
+ def loads(self, data):
+ return pickle.loads(data)
+
+
+JSONSerializer = BaseJSONSerializer
diff --git a/lib/django-1.5/django/contrib/sessions/tests.py b/lib/django-1.5/django/contrib/sessions/tests.py
index da79ac9..829eb82 100644
--- a/lib/django-1.5/django/contrib/sessions/tests.py
+++ b/lib/django-1.5/django/contrib/sessions/tests.py
@@ -273,21 +273,25 @@
self.assertEqual(self.session.decode(encoded), data)
def test_actual_expiry(self):
- # Regression test for #19200
- old_session_key = None
- new_session_key = None
- try:
- self.session['foo'] = 'bar'
- self.session.set_expiry(-timedelta(seconds=10))
- self.session.save()
- old_session_key = self.session.session_key
- # With an expiry date in the past, the session expires instantly.
- new_session = self.backend(self.session.session_key)
- new_session_key = new_session.session_key
- self.assertNotIn('foo', new_session)
- finally:
- self.session.delete(old_session_key)
- self.session.delete(new_session_key)
+ # this doesn't work with JSONSerializer (serializing timedelta)
+ with override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer'):
+ self.session = self.backend() # reinitialize after overriding settings
+
+ # Regression test for #19200
+ old_session_key = None
+ new_session_key = None
+ try:
+ self.session['foo'] = 'bar'
+ self.session.set_expiry(-timedelta(seconds=10))
+ self.session.save()
+ old_session_key = self.session.session_key
+ # With an expiry date in the past, the session expires instantly.
+ new_session = self.backend(self.session.session_key)
+ new_session_key = new_session.session_key
+ self.assertNotIn('foo', new_session)
+ finally:
+ self.session.delete(old_session_key)
+ self.session.delete(new_session_key)
class DatabaseSessionTests(SessionTestsMixin, TestCase):
@@ -468,6 +472,9 @@
},
}, SESSION_CACHE_ALIAS='sessions')
def test_non_default_cache(self):
+ # Re-initalize the session backend to make use of overridden settings.
+ self.session = self.backend()
+
self.session.save()
self.assertEqual(get_cache('default').get(self.session.cache_key), None)
self.assertNotEqual(get_cache('sessions').get(self.session.cache_key), None)
diff --git a/lib/django-1.5/django/contrib/sitemaps/tests/base.py b/lib/django-1.5/django/contrib/sitemaps/tests/base.py
index bbe8229..099dba7 100644
--- a/lib/django-1.5/django/contrib/sitemaps/tests/base.py
+++ b/lib/django-1.5/django/contrib/sitemaps/tests/base.py
@@ -1,9 +1,24 @@
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.cache import cache
+from django.db import models
from django.test import TestCase
+class TestModel(models.Model):
+ "A test model for "
+ name = models.CharField(max_length=100)
+
+ class Meta:
+ app_label = 'sitemaps'
+
+ def __unicode__(self):
+ return self.name
+
+ def get_absolute_url(self):
+ return '/testmodel/%s/' % self.id
+
+
class SitemapTestsBase(TestCase):
protocol = 'http'
domain = 'example.com' if Site._meta.installed else 'testserver'
@@ -13,8 +28,8 @@
self.base_url = '%s://%s' % (self.protocol, self.domain)
self.old_Site_meta_installed = Site._meta.installed
cache.clear()
- # Create a user that will double as sitemap content
- User.objects.create_user('testuser', 'test@example.com', 's3krit')
+ # Create an object for sitemap content.
+ TestModel.objects.create(name='Test Object')
def tearDown(self):
Site._meta.installed = self.old_Site_meta_installed
diff --git a/lib/django-1.5/django/contrib/sitemaps/tests/generic.py b/lib/django-1.5/django/contrib/sitemaps/tests/generic.py
index ae054c9..5b26573 100644
--- a/lib/django-1.5/django/contrib/sitemaps/tests/generic.py
+++ b/lib/django-1.5/django/contrib/sitemaps/tests/generic.py
@@ -1,9 +1,9 @@
from __future__ import unicode_literals
-from django.contrib.auth.models import User
from django.test.utils import override_settings
-from .base import SitemapTestsBase
+from .base import TestModel, SitemapTestsBase
+
@override_settings(ABSOLUTE_URL_OVERRIDES={})
class GenericViewsSitemapTests(SitemapTestsBase):
@@ -12,8 +12,8 @@
"A minimal generic sitemap can be rendered"
response = self.client.get('/generic/sitemap.xml')
expected = ''
- for username in User.objects.values_list("username", flat=True):
- expected += "<url><loc>%s/users/%s/</loc></url>" % (self.base_url, username)
+ for pk in TestModel.objects.values_list("id", flat=True):
+ expected += "<url><loc>%s/testmodel/%s/</loc></url>" % (self.base_url, pk)
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
%s
diff --git a/lib/django-1.5/django/contrib/sitemaps/tests/http.py b/lib/django-1.5/django/contrib/sitemaps/tests/http.py
index 4a1cf66..1a91d97 100644
--- a/lib/django-1.5/django/contrib/sitemaps/tests/http.py
+++ b/lib/django-1.5/django/contrib/sitemaps/tests/http.py
@@ -4,7 +4,6 @@
from datetime import date
from django.conf import settings
-from django.contrib.auth.models import User
from django.contrib.sitemaps import Sitemap, GenericSitemap
from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured
@@ -14,7 +13,7 @@
from django.utils._os import upath
from django.utils.translation import activate, deactivate
-from .base import SitemapTestsBase
+from .base import TestModel, SitemapTestsBase
class HTTPSitemapTests(SitemapTestsBase):
@@ -128,10 +127,10 @@
Check to make sure that the raw item is included with each
Sitemap.get_url() url result.
"""
- user_sitemap = GenericSitemap({'queryset': User.objects.all()})
- def is_user(url):
- return isinstance(url['item'], User)
- item_in_url_info = all(map(is_user, user_sitemap.get_urls()))
+ test_sitemap = GenericSitemap({'queryset': TestModel.objects.all()})
+ def is_testmodel(url):
+ return isinstance(url['item'], TestModel)
+ item_in_url_info = all(map(is_testmodel, test_sitemap.get_urls()))
self.assertTrue(item_in_url_info)
def test_cached_sitemap_index(self):
diff --git a/lib/django-1.5/django/contrib/sitemaps/tests/urls/http.py b/lib/django-1.5/django/contrib/sitemaps/tests/urls/http.py
index 018e46a..56103f4 100644
--- a/lib/django-1.5/django/contrib/sitemaps/tests/urls/http.py
+++ b/lib/django-1.5/django/contrib/sitemaps/tests/urls/http.py
@@ -4,6 +4,9 @@
from django.contrib.auth.models import User
from django.views.decorators.cache import cache_page
+from django.contrib.sitemaps.tests.base import TestModel
+
+
class SimpleSitemap(Sitemap):
changefreq = "never"
priority = 0.5
@@ -18,7 +21,7 @@
}
generic_sitemaps = {
- 'generic': GenericSitemap({'queryset': User.objects.all()}),
+ 'generic': GenericSitemap({'queryset': TestModel.objects.all()}),
}
flatpage_sitemaps = {
diff --git a/lib/django-1.5/django/contrib/staticfiles/storage.py b/lib/django-1.5/django/contrib/staticfiles/storage.py
index 7e87a89..2cdf460 100644
--- a/lib/django-1.5/django/contrib/staticfiles/storage.py
+++ b/lib/django-1.5/django/contrib/staticfiles/storage.py
@@ -202,7 +202,7 @@
def post_process(self, paths, dry_run=False, **options):
"""
- Post process the given list of files (called from collectstatic).
+ Post process the given SortedDict of files (called from collectstatic).
Processing is actually two separate operations:
diff --git a/lib/django-1.5/django/contrib/syndication/views.py b/lib/django-1.5/django/contrib/syndication/views.py
index 996b7df..be8fbbb 100644
--- a/lib/django-1.5/django/contrib/syndication/views.py
+++ b/lib/django-1.5/django/contrib/syndication/views.py
@@ -11,6 +11,7 @@
from django.utils.encoding import force_text, iri_to_uri, smart_text
from django.utils.html import escape
from django.utils.http import http_date
+from django.utils import six
from django.utils.timezone import is_naive
@@ -69,15 +70,14 @@
except AttributeError:
return default
if callable(attr):
- # Check __code__.co_argcount rather than try/excepting the
- # function and catching the TypeError, because something inside
- # the function may raise the TypeError. This technique is more
- # accurate.
- if hasattr(attr, '__code__'):
- argcount = attr.__code__.co_argcount
- else:
- argcount = attr.__call__.__code__.co_argcount
- if argcount == 2: # one argument is 'self'
+ # Check co_argcount rather than try/excepting the function and
+ # catching the TypeError, because something inside the function
+ # may raise the TypeError. This technique is more accurate.
+ try:
+ code = six.get_function_code(attr)
+ except AttributeError:
+ code = six.get_function_code(attr.__call__)
+ if code.co_argcount == 2: # one argument is 'self'
return attr(obj)
else:
return attr()
diff --git a/lib/django-1.5/django/core/cache/backends/memcached.py b/lib/django-1.5/django/core/cache/backends/memcached.py
index 2c3f198..b5c56d8 100644
--- a/lib/django-1.5/django/core/cache/backends/memcached.py
+++ b/lib/django-1.5/django/core/cache/backends/memcached.py
@@ -75,7 +75,7 @@
self._cache.delete(key)
def get_many(self, keys, version=None):
- new_keys = map(lambda x: self.make_key(x, version=version), keys)
+ new_keys = [self.make_key(x, version=version) for x in keys]
ret = self._cache.get_multi(new_keys)
if ret:
_ = {}
diff --git a/lib/django-1.5/django/core/handlers/wsgi.py b/lib/django-1.5/django/core/handlers/wsgi.py
index a9fa094..05c7150 100644
--- a/lib/django-1.5/django/core/handlers/wsgi.py
+++ b/lib/django-1.5/django/core/handlers/wsgi.py
@@ -140,7 +140,7 @@
path_info = '/'
self.environ = environ
self.path_info = path_info
- self.path = '%s%s' % (script_name, path_info)
+ self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
self.META = environ
self.META['PATH_INFO'] = path_info
self.META['SCRIPT_NAME'] = script_name
diff --git a/lib/django-1.5/django/core/management/commands/makemessages.py b/lib/django-1.5/django/core/management/commands/makemessages.py
index 606cbe0..f5762df 100644
--- a/lib/django-1.5/django/core/management/commands/makemessages.py
+++ b/lib/django-1.5/django/core/management/commands/makemessages.py
@@ -324,8 +324,11 @@
for dirpath, file in find_files(".", ignore_patterns, verbosity,
stdout, symlinks=symlinks):
- process_file(file, dirpath, potfile, domain, verbosity, extensions,
- wrap, location, stdout)
+ try:
+ process_file(file, dirpath, potfile, domain, verbosity, extensions,
+ wrap, location, stdout)
+ except UnicodeDecodeError:
+ stdout.write("UnicodeDecodeError: skipped file %s in %s" % (file, dirpath))
if os.path.exists(potfile):
write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
diff --git a/lib/django-1.5/django/core/management/commands/test.py b/lib/django-1.5/django/core/management/commands/test.py
index 48b330a..2b8e801 100644
--- a/lib/django-1.5/django/core/management/commands/test.py
+++ b/lib/django-1.5/django/core/management/commands/test.py
@@ -1,3 +1,4 @@
+import logging
import sys
import os
from optparse import make_option, OptionParser
@@ -6,6 +7,7 @@
from django.core.management.base import BaseCommand
from django.test.utils import get_runner
+
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--noinput',
@@ -57,6 +59,21 @@
version=self.get_version(),
option_list=options)
+ def execute(self, *args, **options):
+ if int(options['verbosity']) > 0:
+ # ensure that deprecation warnings are displayed during testing
+ # the following state is assumed:
+ # logging.capturewarnings is true
+ # a "default" level warnings filter has been added for
+ # DeprecationWarning. See django.conf.LazySettings._configure_logging
+ logger = logging.getLogger('py.warnings')
+ handler = logging.StreamHandler()
+ logger.addHandler(handler)
+ super(Command, self).execute(*args, **options)
+ if int(options['verbosity']) > 0:
+ # remove the testing-specific handler
+ logger.removeHandler(handler)
+
def handle(self, *test_labels, **options):
from django.conf import settings
from django.test.utils import get_runner
diff --git a/lib/django-1.5/django/core/serializers/base.py b/lib/django-1.5/django/core/serializers/base.py
index 294934a..1e78026 100644
--- a/lib/django-1.5/django/core/serializers/base.py
+++ b/lib/django-1.5/django/core/serializers/base.py
@@ -161,9 +161,7 @@
def save(self, save_m2m=True, using=None):
# Call save on the Model baseclass directly. This bypasses any
# model-defined save. The save is also forced to be raw.
- # This ensures that the data that is deserialized is literally
- # what came from the file, not post-processed by pre_save/save
- # methods.
+ # raw=True is passed to any pre/post_save signals.
models.Model.save_base(self.object, using=using, raw=True)
if self.m2m_data and save_m2m:
for accessor_name, object_list in self.m2m_data.items():
diff --git a/lib/django-1.5/django/core/signing.py b/lib/django-1.5/django/core/signing.py
index 92ab968..4d2b0a2 100644
--- a/lib/django-1.5/django/core/signing.py
+++ b/lib/django-1.5/django/core/signing.py
@@ -195,6 +195,10 @@
return super(TimestampSigner, self).sign(value)
def unsign(self, value, max_age=None):
+ """
+ Retrieve original value and check it wasn't signed more
+ than max_age seconds ago.
+ """
result = super(TimestampSigner, self).unsign(value)
value, timestamp = result.rsplit(self.sep, 1)
timestamp = baseconv.base62.decode(timestamp)
diff --git a/lib/django-1.5/django/db/backends/mysql/base.py b/lib/django-1.5/django/db/backends/mysql/base.py
index 158d936..38cf7e4 100644
--- a/lib/django-1.5/django/db/backends/mysql/base.py
+++ b/lib/django-1.5/django/db/backends/mysql/base.py
@@ -123,7 +123,7 @@
except Database.OperationalError as e:
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
- if e[0] in self.codes_for_integrityerror:
+ if e.args[0] in self.codes_for_integrityerror:
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
except Database.DatabaseError as e:
@@ -137,7 +137,7 @@
except Database.OperationalError as e:
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
- if e[0] in self.codes_for_integrityerror:
+ if e.args[0] in self.codes_for_integrityerror:
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
except Database.DatabaseError as e:
@@ -383,8 +383,9 @@
kwargs = {
'conv': django_conversions,
'charset': 'utf8',
- 'use_unicode': True,
}
+ if not six.PY3:
+ kwargs['use_unicode'] = True
settings_dict = self.settings_dict
if settings_dict['USER']:
kwargs['user'] = settings_dict['USER']
diff --git a/lib/django-1.5/django/db/backends/mysql/validation.py b/lib/django-1.5/django/db/backends/mysql/validation.py
index de7474d..2ce957c 100644
--- a/lib/django-1.5/django/db/backends/mysql/validation.py
+++ b/lib/django-1.5/django/db/backends/mysql/validation.py
@@ -10,6 +10,7 @@
from django.db import models
varchar_fields = (models.CharField, models.CommaSeparatedIntegerField,
models.SlugField)
- if isinstance(f, varchar_fields) and f.max_length > 255 and f.unique:
+ if (isinstance(f, varchar_fields) and f.unique
+ and (f.max_length is None or int(f.max_length) > 255)):
msg = '"%(name)s": %(cls)s cannot have a "max_length" greater than 255 when using "unique=True".'
errors.add(opts, msg % {'name': f.name, 'cls': f.__class__.__name__})
diff --git a/lib/django-1.5/django/db/backends/oracle/creation.py b/lib/django-1.5/django/db/backends/oracle/creation.py
index d9bf3df..76d9da2 100644
--- a/lib/django-1.5/django/db/backends/oracle/creation.py
+++ b/lib/django-1.5/django/db/backends/oracle/creation.py
@@ -168,6 +168,7 @@
IDENTIFIED BY %(password)s
DEFAULT TABLESPACE %(tblspace)s
TEMPORARY TABLESPACE %(tblspace_temp)s
+ QUOTA UNLIMITED ON %(tblspace)s
""",
"""GRANT CONNECT, RESOURCE TO %(user)s""",
]
diff --git a/lib/django-1.5/django/db/models/base.py b/lib/django-1.5/django/db/models/base.py
index a58761b..4b0da16 100644
--- a/lib/django-1.5/django/db/models/base.py
+++ b/lib/django-1.5/django/db/models/base.py
@@ -895,7 +895,7 @@
def full_clean(self, exclude=None):
"""
Calls clean_fields, clean, and validate_unique, on the model,
- and raises a ``ValidationError`` for any errors that occured.
+ and raises a ``ValidationError`` for any errors that occurred.
"""
errors = {}
if exclude is None:
diff --git a/lib/django-1.5/django/db/models/fields/__init__.py b/lib/django-1.5/django/db/models/fields/__init__.py
index 67d2531..9949dfa 100644
--- a/lib/django-1.5/django/db/models/fields/__init__.py
+++ b/lib/django-1.5/django/db/models/fields/__init__.py
@@ -1,6 +1,5 @@
from __future__ import unicode_literals
-import collections
import copy
import datetime
import decimal
@@ -16,6 +15,7 @@
from django.utils.datastructures import DictWrapper
from django.utils.dateparse import parse_date, parse_datetime, parse_time
from django.utils.functional import curry, total_ordering
+from django.utils.itercompat import is_iterator
from django.utils.text import capfirst
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@@ -444,7 +444,7 @@
return bound_field_class(self, fieldmapping, original)
def _get_choices(self):
- if isinstance(self._choices, collections.Iterator):
+ if is_iterator(self._choices):
choices, self._choices = tee(self._choices)
return choices
else:
diff --git a/lib/django-1.5/django/db/models/fields/related.py b/lib/django-1.5/django/db/models/fields/related.py
index 95c865d..2ac4b47 100644
--- a/lib/django-1.5/django/db/models/fields/related.py
+++ b/lib/django-1.5/django/db/models/fields/related.py
@@ -1172,7 +1172,7 @@
'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to},
})
# Construct and return the new class.
- return type(name, (models.Model,), {
+ return type(str(name), (models.Model,), {
'Meta': meta,
'__module__': klass.__module__,
from_: models.ForeignKey(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace),
diff --git a/lib/django-1.5/django/db/models/query.py b/lib/django-1.5/django/db/models/query.py
index b465285..c523a9d 100644
--- a/lib/django-1.5/django/db/models/query.py
+++ b/lib/django-1.5/django/db/models/query.py
@@ -68,11 +68,27 @@
"""
# Force the cache to be fully populated.
len(self)
-
obj_dict = self.__dict__.copy()
obj_dict['_iter'] = None
+ obj_dict['_known_related_objects'] = dict(
+ (field.name, val) for field, val in self._known_related_objects.items()
+ )
return obj_dict
+ def __setstate__(self, obj_dict):
+ model = obj_dict['model']
+ if model is None:
+ # if model is None, then self should be emptyqs and the related
+ # objects do not matter.
+ obj_dict['_known_related_objects'] = {}
+ else:
+ opts = model._meta
+ obj_dict['_known_related_objects'] = dict(
+ (opts.get_field(field.name if hasattr(field, 'name') else field), val)
+ for field, val in obj_dict['_known_related_objects'].items()
+ )
+ self.__dict__.update(obj_dict)
+
def __repr__(self):
data = list(self[:REPR_OUTPUT_SIZE + 1])
if len(data) > REPR_OUTPUT_SIZE:
@@ -102,7 +118,7 @@
len(self)
if self._result_cache is None:
- self._iter = self._safe_iterator(self.iterator())
+ self._iter = self.iterator()
self._result_cache = []
if self._iter:
return self._result_iter()
@@ -337,18 +353,6 @@
yield obj
- def _safe_iterator(self, iterator):
- # ensure result cache is cleared when iterating over a queryset
- # raises an exception
- try:
- for item in iterator:
- yield item
- except StopIteration:
- raise
- except Exception:
- self._result_cache = None
- raise
-
def aggregate(self, *args, **kwargs):
"""
Returns a dictionary containing the calculations (aggregation)
@@ -396,13 +400,11 @@
return clone._result_cache[0]
if not num:
raise self.model.DoesNotExist(
- "%s matching query does not exist. "
- "Lookup parameters were %s" %
- (self.model._meta.object_name, kwargs))
+ "%s matching query does not exist." %
+ self.model._meta.object_name)
raise self.model.MultipleObjectsReturned(
- "get() returned more than one %s -- it returned %s! "
- "Lookup parameters were %s" %
- (self.model._meta.object_name, num, kwargs))
+ "get() returned more than one %s -- it returned %s!" %
+ (self.model._meta.object_name, num))
def create(self, **kwargs):
"""
@@ -1711,8 +1713,18 @@
if len(obj_list) == 0:
break
+ current_lookup = LOOKUP_SEP.join(attrs[0:level+1])
+ if current_lookup in done_queries:
+ # Skip any prefetching, and any object preparation
+ obj_list = done_queries[current_lookup]
+ continue
+
+ # Prepare objects:
good_objects = True
for obj in obj_list:
+ # Since prefetching can re-use instances, it is possible to have
+ # the same instance multiple times in obj_list, so obj might
+ # already be prepared.
if not hasattr(obj, '_prefetched_objects_cache'):
try:
obj._prefetched_objects_cache = {}
@@ -1723,9 +1735,6 @@
# now.
good_objects = False
break
- else:
- # We already did this list
- break
if not good_objects:
break
@@ -1750,23 +1759,18 @@
"prefetch_related()." % lookup)
if prefetcher is not None and not is_fetched:
- # Check we didn't do this already
- current_lookup = LOOKUP_SEP.join(attrs[0:level+1])
- if current_lookup in done_queries:
- obj_list = done_queries[current_lookup]
- else:
- obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr)
- # We need to ensure we don't keep adding lookups from the
- # same relationships to stop infinite recursion. So, if we
- # are already on an automatically added lookup, don't add
- # the new lookups from relationships we've seen already.
- if not (lookup in auto_lookups and
- descriptor in followed_descriptors):
- for f in additional_prl:
- new_prl = LOOKUP_SEP.join([current_lookup, f])
- auto_lookups.append(new_prl)
- done_queries[current_lookup] = obj_list
- followed_descriptors.add(descriptor)
+ obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr)
+ # We need to ensure we don't keep adding lookups from the
+ # same relationships to stop infinite recursion. So, if we
+ # are already on an automatically added lookup, don't add
+ # the new lookups from relationships we've seen already.
+ if not (lookup in auto_lookups and
+ descriptor in followed_descriptors):
+ for f in additional_prl:
+ new_prl = LOOKUP_SEP.join([current_lookup, f])
+ auto_lookups.append(new_prl)
+ done_queries[current_lookup] = obj_list
+ followed_descriptors.add(descriptor)
else:
# Either a singly related object that has already been fetched
# (e.g. via select_related), or hopefully some other property
diff --git a/lib/django-1.5/django/db/models/sql/query.py b/lib/django-1.5/django/db/models/sql/query.py
index e7c8d6c..390444d 100644
--- a/lib/django-1.5/django/db/models/sql/query.py
+++ b/lib/django-1.5/django/db/models/sql/query.py
@@ -208,12 +208,17 @@
Unpickling support.
"""
# Rebuild list of field instances
- opts = obj_dict['model']._meta
- obj_dict['select_fields'] = [
- name is not None and opts.get_field(name) or None
- for name in obj_dict['select_fields']
- ]
-
+ model = obj_dict['model']
+ if model is None:
+ # if model is None the queryset should be emptyqs. So the
+ # select_fields do not matter.
+ obj_dict['select_fields'] = []
+ else:
+ opts = model._meta
+ obj_dict['select_fields'] = [
+ name is not None and opts.get_field(name) or None
+ for name in obj_dict['select_fields']
+ ]
self.__dict__.update(obj_dict)
def prepare(self):
@@ -1106,7 +1111,14 @@
# If value is a query expression, evaluate it
value = SQLEvaluator(value, self, reuse=can_reuse)
having_clause = value.contains_aggregate
-
+ # For Oracle '' is equivalent to null. The check needs to be done
+ # at this stage because join promotion can't be done at compiler
+ # stage. Using DEFAULT_DB_ALIAS isn't nice, but it is the best we
+ # can do here. Similar thing is done in is_nullable(), too.
+ if (connections[DEFAULT_DB_ALIAS].features.interprets_empty_strings_as_nulls and
+ lookup_type == 'exact' and value == ''):
+ value = True
+ lookup_type = 'isnull'
for alias, aggregate in self.aggregates.items():
if alias in (parts[0], LOOKUP_SEP.join(parts)):
entry = self.where_class()
diff --git a/lib/django-1.5/django/db/models/sql/where.py b/lib/django-1.5/django/db/models/sql/where.py
index 47f4ffa..2841d13 100644
--- a/lib/django-1.5/django/db/models/sql/where.py
+++ b/lib/django-1.5/django/db/models/sql/where.py
@@ -4,7 +4,6 @@
from __future__ import absolute_import
-import collections
import datetime
from itertools import repeat
@@ -12,6 +11,7 @@
from django.db.models.fields import Field
from django.db.models.sql.datastructures import EmptyResultSet
from django.db.models.sql.aggregates import Aggregate
+from django.utils.itercompat import is_iterator
from django.utils.six.moves import xrange
# Connection types
@@ -51,7 +51,7 @@
return
obj, lookup_type, value = data
- if isinstance(value, collections.Iterator):
+ if is_iterator(value):
# Consume any generators immediately, so that we can determine
# emptiness and transform any non-empty values correctly.
value = list(value)
diff --git a/lib/django-1.5/django/forms/forms.py b/lib/django-1.5/django/forms/forms.py
index 3299c2b..2a2290f 100644
--- a/lib/django-1.5/django/forms/forms.py
+++ b/lib/django-1.5/django/forms/forms.py
@@ -503,7 +503,7 @@
If attrs are given, they're used as HTML attributes on the <label> tag.
"""
- contents = contents or conditional_escape(self.label)
+ contents = contents or self.label
widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id
if id_:
@@ -511,6 +511,8 @@
contents = format_html('<label for="{0}"{1}>{2}</label>',
widget.id_for_label(id_), attrs, contents
)
+ else:
+ contents = conditional_escape(contents)
return mark_safe(contents)
def css_classes(self, extra_classes=None):
diff --git a/lib/django-1.5/django/forms/formsets.py b/lib/django-1.5/django/forms/formsets.py
index 84a2855..a5629eb 100644
--- a/lib/django-1.5/django/forms/formsets.py
+++ b/lib/django-1.5/django/forms/formsets.py
@@ -111,7 +111,7 @@
if self.is_bound:
return self.management_form.cleaned_data[INITIAL_FORM_COUNT]
else:
- # Use the length of the inital data if it's there, 0 otherwise.
+ # Use the length of the initial data if it's there, 0 otherwise.
initial_forms = self.initial and len(self.initial) or 0
if initial_forms > self.max_num >= 0:
initial_forms = self.max_num
diff --git a/lib/django-1.5/django/middleware/csrf.py b/lib/django-1.5/django/middleware/csrf.py
index 339f42a..c7c25bf 100644
--- a/lib/django-1.5/django/middleware/csrf.py
+++ b/lib/django-1.5/django/middleware/csrf.py
@@ -53,6 +53,14 @@
return request.META.get("CSRF_COOKIE", None)
+def rotate_token(request):
+ """
+ Changes the CSRF token in use for a request - should be done on login
+ for security purposes.
+ """
+ request.META["CSRF_COOKIE"] = _get_new_csrf_key()
+
+
def _sanitize_token(token):
# Allow only alphanum
if len(token) > CSRF_KEY_LENGTH:
diff --git a/lib/django-1.5/django/template/defaulttags.py b/lib/django-1.5/django/template/defaulttags.py
index b5c8cf2..cd8132c 100644
--- a/lib/django-1.5/django/template/defaulttags.py
+++ b/lib/django-1.5/django/template/defaulttags.py
@@ -1,6 +1,7 @@
"""Default tags used by the template system, available to all templates."""
from __future__ import unicode_literals
+import os
import sys
import re
from datetime import datetime
@@ -312,6 +313,7 @@
return ''
def include_is_allowed(filepath):
+ filepath = os.path.abspath(filepath)
for root in settings.ALLOWED_INCLUDE_ROOTS:
if filepath.startswith(root):
return True
diff --git a/lib/django-1.5/django/template/loaders/cached.py b/lib/django-1.5/django/template/loaders/cached.py
index 4c25acd..fb987a5 100644
--- a/lib/django-1.5/django/template/loaders/cached.py
+++ b/lib/django-1.5/django/template/loaders/cached.py
@@ -6,6 +6,7 @@
import hashlib
from django.template.base import TemplateDoesNotExist
from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin
+from django.utils.encoding import force_bytes
class Loader(BaseLoader):
is_usable = True
@@ -40,7 +41,7 @@
key = template_name
if template_dirs:
# If template directories were specified, use a hash to differentiate
- key = '-'.join([template_name, hashlib.sha1('|'.join(template_dirs)).hexdigest()])
+ key = '-'.join([template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()])
if key not in self.template_cache:
template, origin = self.find_template(template_name, template_dirs)
diff --git a/lib/django-1.5/django/test/_doctest.py b/lib/django-1.5/django/test/_doctest.py
index 82d4a6d..9b8ddc9 100644
--- a/lib/django-1.5/django/test/_doctest.py
+++ b/lib/django-1.5/django/test/_doctest.py
@@ -883,7 +883,7 @@
if module is None:
return True
elif inspect.isfunction(object):
- return module.__dict__ is object.__globals__
+ return module.__dict__ is six.get_function_globals(object)
elif inspect.isclass(object):
return module.__name__ == object.__module__
elif inspect.getmodule(object) is not None:
@@ -1021,7 +1021,7 @@
# Find the line number for functions & methods.
if inspect.ismethod(obj): obj = obj.__func__
- if inspect.isfunction(obj): obj = obj.__code__
+ if inspect.isfunction(obj): obj = six.get_function_code(obj)
if inspect.istraceback(obj): obj = obj.tb_frame
if inspect.isframe(obj): obj = obj.f_code
if inspect.iscode(obj):
diff --git a/lib/django-1.5/django/test/html.py b/lib/django-1.5/django/test/html.py
index cda9dfa..0d30bd2 100644
--- a/lib/django-1.5/django/test/html.py
+++ b/lib/django-1.5/django/test/html.py
@@ -222,7 +222,7 @@
"""
Takes a string that contains *valid* HTML and turns it into a Python object
structure that can be easily compared against other HTML on semantic
- equivilance. Syntactical differences like which quotation is used on
+ equivalence. Syntactical differences like which quotation is used on
arguments will be ignored.
"""
diff --git a/lib/django-1.5/django/test/simple.py b/lib/django-1.5/django/test/simple.py
index 8faf1e4..24471ca 100644
--- a/lib/django-1.5/django/test/simple.py
+++ b/lib/django-1.5/django/test/simple.py
@@ -1,4 +1,3 @@
-import logging
import unittest as real_unittest
from django.conf import settings
@@ -274,6 +273,7 @@
mirrored_aliases = {}
test_databases = {}
dependencies = {}
+ default_sig = connections[DEFAULT_DB_ALIAS].creation.test_db_signature()
for alias in connections:
connection = connections[alias]
if connection.settings_dict['TEST_MIRROR']:
@@ -295,7 +295,7 @@
dependencies[alias] = (
connection.settings_dict['TEST_DEPENDENCIES'])
else:
- if alias != DEFAULT_DB_ALIAS:
+ if alias != DEFAULT_DB_ALIAS and connection.creation.test_db_signature() != default_sig:
dependencies[alias] = connection.settings_dict.get(
'TEST_DEPENDENCIES', [DEFAULT_DB_ALIAS])
@@ -310,12 +310,14 @@
for alias in aliases:
connection = connections[alias]
- old_names.append((connection, db_name, True))
if test_db_name is None:
test_db_name = connection.creation.create_test_db(
self.verbosity, autoclobber=not self.interactive)
+ destroy = True
else:
connection.settings_dict['NAME'] = test_db_name
+ destroy = False
+ old_names.append((connection, db_name, destroy))
for alias, mirror_alias in mirrored_aliases.items():
mirrors.append((alias, connections[alias].settings_dict['NAME']))
@@ -366,19 +368,7 @@
self.setup_test_environment()
suite = self.build_suite(test_labels, extra_tests)
old_config = self.setup_databases()
- if self.verbosity > 0:
- # ensure that deprecation warnings are displayed during testing
- # the following state is assumed:
- # logging.capturewarnings is true
- # a "default" level warnings filter has been added for
- # DeprecationWarning. See django.conf.LazySettings._configure_logging
- logger = logging.getLogger('py.warnings')
- handler = logging.StreamHandler()
- logger.addHandler(handler)
result = self.run_suite(suite)
- if self.verbosity > 0:
- # remove the testing-specific handler
- logger.removeHandler(handler)
self.teardown_databases(old_config)
self.teardown_test_environment()
return self.suite_result(suite, result)
diff --git a/lib/django-1.5/django/test/testcases.py b/lib/django-1.5/django/test/testcases.py
index 3a93b3b..e556d18 100644
--- a/lib/django-1.5/django/test/testcases.py
+++ b/lib/django-1.5/django/test/testcases.py
@@ -638,12 +638,17 @@
self.assertEqual(response.status_code, status_code,
msg_prefix + "Couldn't retrieve content: Response code was %d"
" (expected %d)" % (response.status_code, status_code))
- text = force_text(text, encoding=response._charset)
+
if response.streaming:
content = b''.join(response.streaming_content)
else:
content = response.content
- content = content.decode(response._charset)
+ if not isinstance(text, bytes) or html:
+ text = force_text(text, encoding=response._charset)
+ content = content.decode(response._charset)
+ text_repr = "'%s'" % text
+ else:
+ text_repr = repr(text)
if html:
content = assert_and_parse_html(self, content, None,
"Response's content is not valid HTML:")
@@ -652,11 +657,11 @@
real_count = content.count(text)
if count is not None:
self.assertEqual(real_count, count,
- msg_prefix + "Found %d instances of '%s' in response"
- " (expected %d)" % (real_count, text, count))
+ msg_prefix + "Found %d instances of %s in response"
+ " (expected %d)" % (real_count, text_repr, count))
else:
self.assertTrue(real_count != 0,
- msg_prefix + "Couldn't find '%s' in response" % text)
+ msg_prefix + "Couldn't find %s in response" % text_repr)
def assertNotContains(self, response, text, status_code=200,
msg_prefix='', html=False):
@@ -678,15 +683,21 @@
self.assertEqual(response.status_code, status_code,
msg_prefix + "Couldn't retrieve content: Response code was %d"
" (expected %d)" % (response.status_code, status_code))
- text = force_text(text, encoding=response._charset)
- content = response.content.decode(response._charset)
+
+ content = response.content
+ if not isinstance(text, bytes) or html:
+ text = force_text(text, encoding=response._charset)
+ content = content.decode(response._charset)
+ text_repr = "'%s'" % text
+ else:
+ text_repr = repr(text)
if html:
content = assert_and_parse_html(self, content, None,
'Response\'s content is not valid HTML:')
text = assert_and_parse_html(self, text, None,
'Second argument is not valid HTML:')
self.assertEqual(content.count(text), 0,
- msg_prefix + "Response should not contain '%s'" % text)
+ msg_prefix + "Response should not contain %s" % text_repr)
def assertFormError(self, response, form, field, errors, msg_prefix=''):
"""
diff --git a/lib/django-1.5/django/test/utils.py b/lib/django-1.5/django/test/utils.py
index 43c031c..73ce395 100644
--- a/lib/django-1.5/django/test/utils.py
+++ b/lib/django-1.5/django/test/utils.py
@@ -4,6 +4,7 @@
from django.conf import settings, UserSettingsHolder
from django.core import mail
+from django.http import request
from django.template import Template, loader, TemplateDoesNotExist
from django.template.loaders import cached
from django.test.signals import template_rendered, setting_changed
@@ -72,13 +73,16 @@
- Set the email backend to the locmem email backend.
- Setting the active locale to match the LANGUAGE_CODE setting.
"""
- Template.original_render = Template._render
+ Template._original_render = Template._render
Template._render = instrumented_test_render
- mail.original_email_backend = settings.EMAIL_BACKEND
+ # Storing previous values in the settings module itself is problematic.
+ # Store them in arbitrary (but related) modules instead. See #20636.
+
+ mail._original_email_backend = settings.EMAIL_BACKEND
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
- settings._original_allowed_hosts = settings.ALLOWED_HOSTS
+ request._original_allowed_hosts = settings.ALLOWED_HOSTS
settings.ALLOWED_HOSTS = ['*']
mail.outbox = []
@@ -93,14 +97,14 @@
- Restoring the email sending functions
"""
- Template._render = Template.original_render
- del Template.original_render
+ Template._render = Template._original_render
+ del Template._original_render
- settings.EMAIL_BACKEND = mail.original_email_backend
- del mail.original_email_backend
+ settings.EMAIL_BACKEND = mail._original_email_backend
+ del mail._original_email_backend
- settings.ALLOWED_HOSTS = settings._original_allowed_hosts
- del settings._original_allowed_hosts
+ settings.ALLOWED_HOSTS = request._original_allowed_hosts
+ del request._original_allowed_hosts
del mail.outbox
diff --git a/lib/django-1.5/django/utils/cache.py b/lib/django-1.5/django/utils/cache.py
index 0fceaa9..ecf0f83 100644
--- a/lib/django-1.5/django/utils/cache.py
+++ b/lib/django-1.5/django/utils/cache.py
@@ -181,7 +181,7 @@
for header in headerlist:
value = request.META.get(header, None)
if value is not None:
- ctx.update(value)
+ ctx.update(force_bytes(value))
path = hashlib.md5(force_bytes(iri_to_uri(request.get_full_path())))
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % (
key_prefix, method, path.hexdigest(), ctx.hexdigest())
diff --git a/lib/django-1.5/django/utils/crypto.py b/lib/django-1.5/django/utils/crypto.py
index 57bc60d..94f717b 100644
--- a/lib/django-1.5/django/utils/crypto.py
+++ b/lib/django-1.5/django/utils/crypto.py
@@ -72,10 +72,10 @@
# is better than absolute predictability.
random.seed(
hashlib.sha256(
- "%s%s%s" % (
+ ("%s%s%s" % (
random.getstate(),
time.time(),
- settings.SECRET_KEY)
+ settings.SECRET_KEY)).encode('utf-8')
).digest())
return ''.join([random.choice(allowed_chars) for i in range(length)])
diff --git a/lib/django-1.5/django/utils/encoding.py b/lib/django-1.5/django/utils/encoding.py
index 1521584..f08d417 100644
--- a/lib/django-1.5/django/utils/encoding.py
+++ b/lib/django-1.5/django/utils/encoding.py
@@ -232,7 +232,7 @@
return path
# I know about `os.sep` and `os.altsep` but I want to leave
# some flexibility for hardcoding separators.
- return quote(force_bytes(path.replace("\\", "/")), safe=b"/~!*()'")
+ return quote(force_bytes(path).replace(b"\\", b"/"), safe=b"/~!*()'")
# The encoding of the default system locale but falls back to the
# given fallback encoding if the encoding is unsupported by python or could
diff --git a/lib/django-1.5/django/utils/functional.py b/lib/django-1.5/django/utils/functional.py
index 1dcbd55..5fbafad 100644
--- a/lib/django-1.5/django/utils/functional.py
+++ b/lib/django-1.5/django/utils/functional.py
@@ -4,6 +4,8 @@
import sys
from django.utils import six
+from django.utils.six.moves import copyreg
+
# You can't trivially replace this `functools.partial` because this binds to
# classes and returns bound instances, whereas functools.partial (on CPython)
@@ -294,15 +296,23 @@
self._setup()
return self._wrapped.__dict__
- # Python 3.3 will call __reduce__ when pickling; these methods are needed
- # to serialize and deserialize correctly. They are not called in earlier
- # versions of Python.
+ # Python 3.3 will call __reduce__ when pickling; this method is needed
+ # to serialize and deserialize correctly.
@classmethod
def __newobj__(cls, *args):
return cls.__new__(cls, *args)
- def __reduce__(self):
- return (self.__newobj__, (self.__class__,), self.__getstate__())
+ def __reduce_ex__(self, proto):
+ if proto >= 2:
+ # On Py3, since the default protocol is 3, pickle uses the
+ # ``__newobj__`` method (& more efficient opcodes) for writing.
+ return (self.__newobj__, (self.__class__,), self.__getstate__())
+ else:
+ # On Py2, the default protocol is 0 (for back-compat) & the above
+ # code fails miserably (see regression test). Instead, we return
+ # exactly what's returned if there's no ``__reduce__`` method at
+ # all.
+ return (copyreg._reconstructor, (self.__class__, object, None), self.__getstate__())
# Need to pretend to be the wrapped class, for the sake of objects that care
# about this (especially in equality tests)
diff --git a/lib/django-1.5/django/utils/html.py b/lib/django-1.5/django/utils/html.py
index 32a3b07..a0d158d 100644
--- a/lib/django-1.5/django/utils/html.py
+++ b/lib/django-1.5/django/utils/html.py
@@ -5,13 +5,13 @@
import re
import string
try:
- from urllib.parse import quote, urlsplit, urlunsplit
+ from urllib.parse import quote, unquote, urlsplit, urlunsplit
except ImportError: # Python 2
- from urllib import quote
+ from urllib import quote, unquote
from urlparse import urlsplit, urlunsplit
from django.utils.safestring import SafeData, mark_safe
-from django.utils.encoding import force_bytes, force_text
+from django.utils.encoding import force_text, force_str
from django.utils.functional import allow_lazy
from django.utils import six
from django.utils.text import normalize_newlines
@@ -24,7 +24,6 @@
DOTS = ['·', '*', '\u2022', '•', '•', '•']
unencoded_ampersands_re = re.compile(r'&(?!(\w+|#\d+);)')
-unquoted_percents_re = re.compile(r'%(?![0-9A-Fa-f]{2})')
word_split_re = re.compile(r'(\s+)')
simple_url_re = re.compile(r'^https?://\w', re.IGNORECASE)
simple_url_2_re = re.compile(r'^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)$', re.IGNORECASE)
@@ -158,11 +157,9 @@
else:
url = urlunsplit((scheme, netloc, path, query, fragment))
- # An URL is considered unquoted if it contains no % characters or
- # contains a % not followed by two hexadecimal digits. See #9655.
- if '%' not in url or unquoted_percents_re.search(url):
- # See http://bugs.python.org/issue2637
- url = quote(force_bytes(url), safe=b'!*\'();:@&=+$,/?#[]~')
+ url = unquote(force_str(url))
+ # See http://bugs.python.org/issue2637
+ url = quote(url, safe=b'!*\'();:@&=+$,/?#[]~')
return force_text(url)
diff --git a/lib/django-1.5/django/utils/http.py b/lib/django-1.5/django/utils/http.py
index 0ab5198..f376b1c 100644
--- a/lib/django-1.5/django/utils/http.py
+++ b/lib/django-1.5/django/utils/http.py
@@ -217,7 +217,7 @@
def quote_etag(etag):
"""
- Wraps a string in double quotes escaping contents as necesary.
+ Wraps a string in double quotes escaping contents as necessary.
"""
return '"%s"' % etag.replace('\\', '\\\\').replace('"', '\\"')
@@ -231,11 +231,12 @@
def is_safe_url(url, host=None):
"""
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
- a different host).
+ a different host and uses a safe scheme).
Always returns ``False`` on an empty url.
"""
if not url:
return False
- netloc = urllib_parse.urlparse(url)[1]
- return not netloc or netloc == host
+ url_info = urllib_parse.urlparse(url)
+ return (not url_info.netloc or url_info.netloc == host) and \
+ (not url_info.scheme or url_info.scheme in ['http', 'https'])
diff --git a/lib/django-1.5/django/utils/itercompat.py b/lib/django-1.5/django/utils/itercompat.py
index aa329c1..2c555bd 100644
--- a/lib/django-1.5/django/utils/itercompat.py
+++ b/lib/django-1.5/django/utils/itercompat.py
@@ -4,10 +4,12 @@
these implementations if necessary.
"""
-from django.utils.six.moves import builtins
+import collections
import itertools
+import sys
import warnings
+
def is_iterable(x):
"A implementation independent way of checking for iterables"
try:
@@ -17,6 +19,17 @@
else:
return True
+def is_iterator(x):
+ """An implementation independent way of checking for iterators
+
+ Python 2.6 has a different implementation of collections.Iterator which
+ accepts anything with a `next` method. 2.7+ requires and `__iter__` method
+ as well.
+ """
+ if sys.version_info >= (2, 7):
+ return isinstance(x, collections.Iterator)
+ return isinstance(x, collections.Iterator) and hasattr(x, '__iter__')
+
def product(*args, **kwds):
warnings.warn("django.utils.itercompat.product is deprecated; use the native version instead",
PendingDeprecationWarning)
diff --git a/lib/django-1.5/django/utils/module_loading.py b/lib/django-1.5/django/utils/module_loading.py
index f8aadb3..ede585e 100644
--- a/lib/django-1.5/django/utils/module_loading.py
+++ b/lib/django-1.5/django/utils/module_loading.py
@@ -2,6 +2,35 @@
import os
import sys
+from django.core.exceptions import ImproperlyConfigured
+from django.utils import six
+from django.utils.importlib import import_module
+
+
+def import_by_path(dotted_path, error_prefix=''):
+ """
+ Import a dotted module path and return the attribute/class designated by the
+ last name in the path. Raise ImproperlyConfigured if something goes wrong.
+ """
+ try:
+ module_path, class_name = dotted_path.rsplit('.', 1)
+ except ValueError:
+ raise ImproperlyConfigured("%s%s doesn't look like a module path" % (
+ error_prefix, dotted_path))
+ try:
+ module = import_module(module_path)
+ except ImportError as e:
+ msg = '%sError importing module %s: "%s"' % (
+ error_prefix, module_path, e)
+ six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
+ sys.exc_info()[2])
+ try:
+ attr = getattr(module, class_name)
+ except AttributeError:
+ raise ImproperlyConfigured('%sModule "%s" does not define a "%s" attribute/class' % (
+ error_prefix, module_path, class_name))
+ return attr
+
def module_has_submodule(package, module_name):
"""See if 'module' is in 'package'."""
diff --git a/lib/django-1.5/django/utils/six.py b/lib/django-1.5/django/utils/six.py
index 9e38231..06d9b4a 100644
--- a/lib/django-1.5/django/utils/six.py
+++ b/lib/django-1.5/django/utils/six.py
@@ -1,5 +1,24 @@
"""Utilities for writing code that runs on Python 2 and 3"""
+# Copyright (c) 2010-2013 Benjamin Peterson
+#
+# 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.
+
import operator
import sys
import types
@@ -26,7 +45,7 @@
text_type = unicode
binary_type = str
- if sys.platform == "java":
+ if sys.platform.startswith("java"):
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
@@ -133,6 +152,9 @@
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
MovedModule("html_parser", "HTMLParser", "html.parser"),
MovedModule("http_client", "httplib", "http.client"),
+ MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
+ MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
+ MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
@@ -164,7 +186,7 @@
setattr(_MovedItems, attr.name, attr)
del attr
-moves = sys.modules["django.utils.six.moves"] = _MovedItems("moves")
+moves = sys.modules[__name__ + ".moves"] = _MovedItems("moves")
def add_move(move):
@@ -187,22 +209,28 @@
_meth_func = "__func__"
_meth_self = "__self__"
+ _func_closure = "__closure__"
_func_code = "__code__"
_func_defaults = "__defaults__"
+ _func_globals = "__globals__"
_iterkeys = "keys"
_itervalues = "values"
_iteritems = "items"
+ _iterlists = "lists"
else:
_meth_func = "im_func"
_meth_self = "im_self"
+ _func_closure = "func_closure"
_func_code = "func_code"
_func_defaults = "func_defaults"
+ _func_globals = "func_globals"
_iterkeys = "iterkeys"
_itervalues = "itervalues"
_iteritems = "iteritems"
+ _iterlists = "iterlists"
try:
@@ -213,14 +241,18 @@
next = advance_iterator
+try:
+ callable = callable
+except NameError:
+ def callable(obj):
+ return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+
+
if PY3:
def get_unbound_function(unbound):
return unbound
Iterator = object
-
- def callable(obj):
- return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
else:
def get_unbound_function(unbound):
return unbound.im_func
@@ -237,21 +269,27 @@
get_method_function = operator.attrgetter(_meth_func)
get_method_self = operator.attrgetter(_meth_self)
+get_function_closure = operator.attrgetter(_func_closure)
get_function_code = operator.attrgetter(_func_code)
get_function_defaults = operator.attrgetter(_func_defaults)
+get_function_globals = operator.attrgetter(_func_globals)
-def iterkeys(d):
+def iterkeys(d, **kw):
"""Return an iterator over the keys of a dictionary."""
- return iter(getattr(d, _iterkeys)())
+ return iter(getattr(d, _iterkeys)(**kw))
-def itervalues(d):
+def itervalues(d, **kw):
"""Return an iterator over the values of a dictionary."""
- return iter(getattr(d, _itervalues)())
+ return iter(getattr(d, _itervalues)(**kw))
-def iteritems(d):
+def iteritems(d, **kw):
"""Return an iterator over the (key, value) pairs of a dictionary."""
- return iter(getattr(d, _iteritems)())
+ return iter(getattr(d, _iteritems)(**kw))
+
+def iterlists(d, **kw):
+ """Return an iterator over the (key, [values]) pairs of a dictionary."""
+ return iter(getattr(d, _iterlists)(**kw))
if PY3:
@@ -295,17 +333,17 @@
del builtins
else:
- def exec_(code, globs=None, locs=None):
+ def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
- if globs is None:
+ if _globs_ is None:
frame = sys._getframe(1)
- globs = frame.f_globals
- if locs is None:
- locs = frame.f_locals
+ _globs_ = frame.f_globals
+ if _locs_ is None:
+ _locs_ = frame.f_locals
del frame
- elif locs is None:
- locs = globs
- exec("""exec code in globs, locs""")
+ elif _locs_ is None:
+ _locs_ = _globs_
+ exec("""exec _code_ in _globs_, _locs_""")
exec_("""def reraise(tp, value, tb=None):
@@ -369,18 +407,11 @@
### Additional customizations for Django ###
if PY3:
- _iterlists = "lists"
_assertRaisesRegex = "assertRaisesRegex"
else:
- _iterlists = "iterlists"
_assertRaisesRegex = "assertRaisesRegexp"
-def iterlists(d):
- """Return an iterator over the values of a MultiValueDict."""
- return getattr(d, _iterlists)()
-
-
def assertRaisesRegex(self, *args, **kwargs):
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
diff --git a/lib/django-1.5/django/utils/timesince.py b/lib/django-1.5/django/utils/timesince.py
index 1721f09..7b96d39 100644
--- a/lib/django-1.5/django/utils/timesince.py
+++ b/lib/django-1.5/django/utils/timesince.py
@@ -16,7 +16,8 @@
displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are
possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not.
- Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
+ Adapted from
+ http://web.archive.org/web/20060617175230/http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
"""
chunks = (
(60 * 60 * 24 * 365, lambda n: ungettext('year', 'years', n)),
diff --git a/lib/django-1.5/django/views/decorators/debug.py b/lib/django-1.5/django/views/decorators/debug.py
index 78ae6b1..a611981 100644
--- a/lib/django-1.5/django/views/decorators/debug.py
+++ b/lib/django-1.5/django/views/decorators/debug.py
@@ -1,5 +1,7 @@
import functools
+from django.http import HttpRequest
+
def sensitive_variables(*variables):
"""
@@ -62,6 +64,10 @@
def decorator(view):
@functools.wraps(view)
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
+ assert isinstance(request, HttpRequest), (
+ "sensitive_post_parameters didn't receive an HttpRequest. If you "
+ "are decorating a classmethod, be sure to use @method_decorator."
+ )
if parameters:
request.sensitive_post_parameters = parameters
else:
diff --git a/lib/django-1.5/docs/Makefile b/lib/django-1.5/docs/Makefile
index 2a8bcd7..21b8a9c 100644
--- a/lib/django-1.5/docs/Makefile
+++ b/lib/django-1.5/docs/Makefile
@@ -2,15 +2,16 @@
#
# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+PAPER ?=
+BUILDDIR ?= _build
+LANGUAGE ?=
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -n -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS = -n -d $(BUILDDIR)/doctrees -D language=$(LANGUAGE) $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
diff --git a/lib/django-1.5/docs/_ext/djangodocs.py b/lib/django-1.5/docs/_ext/djangodocs.py
index 6c0e189..29ff505 100644
--- a/lib/django-1.5/docs/_ext/djangodocs.py
+++ b/lib/django-1.5/docs/_ext/djangodocs.py
@@ -90,9 +90,15 @@
# Don't use border=1, which docutils does by default.
def visit_table(self, node):
+ self.context.append(self.compact_p)
+ self.compact_p = True
self._table_row_index = 0 # Needed by Sphinx
self.body.append(self.starttag(node, 'table', CLASS='docutils'))
+ def depart_table(self, node):
+ self.compact_p = self.context.pop()
+ self.body.append('</table>\n')
+
# <big>? Really?
def visit_desc_parameterlist(self, node):
self.body.append('(')
@@ -120,7 +126,7 @@
# which is a bit less obvious that I'd like.
#
# FIXME: these messages are all hardcoded in English. We need to change
- # that to accomodate other language docs, but I can't work out how to make
+ # that to accommodate other language docs, but I can't work out how to make
# that work.
#
version_text = {
diff --git a/lib/django-1.5/docs/_ext/literals_to_xrefs.py b/lib/django-1.5/docs/_ext/literals_to_xrefs.py
index d7b3cfc..6feeca9 100644
--- a/lib/django-1.5/docs/_ext/literals_to_xrefs.py
+++ b/lib/django-1.5/docs/_ext/literals_to_xrefs.py
@@ -110,7 +110,7 @@
#
# The following is taken from django.utils.termcolors and is copied here to
-# avoid the dependancy.
+# avoid the dependency.
#
diff --git a/lib/django-1.5/docs/conf.py b/lib/django-1.5/docs/conf.py
index 0f0f28f..9c5a29e 100644
--- a/lib/django-1.5/docs/conf.py
+++ b/lib/django-1.5/docs/conf.py
@@ -52,9 +52,9 @@
# built documents.
#
# The short X.Y version.
-version = '1.5'
+version = '1.5.4'
# The full version, including alpha/beta/rc tags.
-release = '1.5'
+release = '1.5.4'
# The next version to be released
django_next_version = '1.6'
@@ -62,6 +62,9 @@
# for a list of supported languages.
#language = None
+# Location for .po/.mo translation files used when language is set
+locale_dirs = ['locale/']
+
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
@@ -94,7 +97,7 @@
intersphinx_mapping = {
'python': ('http://docs.python.org/2.7', None),
'sphinx': ('http://sphinx.pocoo.org/', None),
- 'six': ('http://packages.python.org/six/', None),
+ 'six': ('http://pythonhosted.org/six/', None),
'simplejson': ('http://simplejson.readthedocs.org/en/latest/', None),
}
diff --git a/lib/django-1.5/docs/faq/general.txt b/lib/django-1.5/docs/faq/general.txt
index 659a8c5..9997022 100644
--- a/lib/django-1.5/docs/faq/general.txt
+++ b/lib/django-1.5/docs/faq/general.txt
@@ -188,3 +188,31 @@
https://people.djangoproject.com/ .
.. _developers for hire page: https://code.djangoproject.com/wiki/DevelopersForHire
+
+How do I cite Django?
+---------------------
+
+It's difficult to give an official citation format, for two reasons: citation
+formats can vary wildly between publications, and citation standards for
+software are still a matter of some debate.
+
+For example, `APA style`_, would dictate something like::
+
+ Django (Version 1.5) [Computer Software]. (2013). Retrieved from http://djangoproject.com.
+
+However, the only true guide is what your publisher will accept, so get a copy
+of those guidelines and fill in the gaps as best you can.
+
+If your referencing style guide requires a publisher name, use "Django Software
+Foundation".
+
+If you need a publishing location, use "Lawrence, Kansas".
+
+If you need a web address, use http://djangoproject.com.
+
+If you need a name, just use "Django", without any tagline.
+
+If you need a publication date, use the year of release of the version you're
+referencing (e.g., 2013 for v1.5)
+
+.. _APA style: http://www.apastyle.org
diff --git a/lib/django-1.5/docs/howto/auth-remote-user.txt b/lib/django-1.5/docs/howto/auth-remote-user.txt
index d59bb25..09ae928 100644
--- a/lib/django-1.5/docs/howto/auth-remote-user.txt
+++ b/lib/django-1.5/docs/howto/auth-remote-user.txt
@@ -87,6 +87,17 @@
Methods
~~~~~~~
+.. method:: RemoteUserBackend.authenticate(remote_user)
+
+ The username passed as ``remote_user`` is considered trusted. This method
+ simply returns the ``User`` object with the given username, creating a new
+ ``User`` object if :attr:`~RemoteUserBackend.create_unknown_user` is
+ ``True``.
+
+ Returns ``None`` if :attr:`~RemoteUserBackend.create_unknown_user` is
+ ``False`` and a ``User`` object with the given username is not found in the
+ database.
+
.. method:: RemoteUserBackend.clean_username(username)
Performs any cleaning on the ``username`` (e.g. stripping LDAP DN
diff --git a/lib/django-1.5/docs/howto/custom-model-fields.txt b/lib/django-1.5/docs/howto/custom-model-fields.txt
index 90771b7..e87d4f7 100644
--- a/lib/django-1.5/docs/howto/custom-model-fields.txt
+++ b/lib/django-1.5/docs/howto/custom-model-fields.txt
@@ -31,7 +31,7 @@
Creating custom fields requires a bit of attention to detail. To make things
easier to follow, we'll use a consistent example throughout this document:
wrapping a Python object representing the deal of cards in a hand of Bridge_.
-Don't worry, you don't have know how to play Bridge to follow this example.
+Don't worry, you don't have to know how to play Bridge to follow this example.
You only need to know that 52 cards are dealt out equally to four players, who
are traditionally called *north*, *east*, *south* and *west*. Our class looks
something like this::
@@ -222,9 +222,9 @@
* :attr:`~django.db.models.Field.db_tablespace`: Only for index creation, if the
backend supports :doc:`tablespaces </topics/db/tablespaces>`. You can usually
ignore this option.
-* ``auto_created``: True if the field was
- automatically created, as for the `OneToOneField` used by model
- inheritance. For advanced use only.
+* ``auto_created``: ``True`` if the field was automatically created, as for the
+ :class:`~django.db.models.OneToOneField` used by model inheritance. For
+ advanced use only.
All of the options without an explanation in the above list have the same
meaning they do for normal Django fields. See the :doc:`field documentation
@@ -249,7 +249,7 @@
little complex, but the code you need to write in your ``Field`` class is
simple: make sure your field subclass uses a special metaclass:
-For example::
+For example, on Python 2::
class HandField(models.Field):
@@ -258,7 +258,21 @@
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
- # ...
+ ...
+
+On Python 3, in lieu of setting the ``__metaclass__`` attribute, add
+``metaclass`` to the class definition::
+
+ class HandField(models.Field, metaclass=models.SubfieldBase):
+ ...
+
+If you want your code to work on Python 2 & 3, you can use
+:func:`six.with_metaclass`::
+
+ from django.utils.six import with_metaclass
+
+ class HandField(with_metaclass(models.SubfieldBase, models.Field)):
+ ...
This ensures that the :meth:`.to_python` method, documented below, will always
be called when the attribute is initialized.
@@ -300,6 +314,13 @@
the above example, the description displayed by the ``admindocs``
application for a ``HandField`` will be 'A hand of cards (bridge style)'.
+In the :mod:`django.contrib.admindocs` display, the field description is
+interpolated with ``field.__dict__`` which allows the description to
+incorporate arguments of the field. For example, the description for
+:class:`~django.db.models.CharField` is::
+
+ description = _("String (up to %(max_length)s)")
+
Useful methods
--------------
diff --git a/lib/django-1.5/docs/howto/custom-template-tags.txt b/lib/django-1.5/docs/howto/custom-template-tags.txt
index e5644dd..30bb6b1 100644
--- a/lib/django-1.5/docs/howto/custom-template-tags.txt
+++ b/lib/django-1.5/docs/howto/custom-template-tags.txt
@@ -20,7 +20,8 @@
The app should contain a ``templatetags`` directory, at the same level as
``models.py``, ``views.py``, etc. If this doesn't already exist, create it -
don't forget the ``__init__.py`` file to ensure the directory is treated as a
-Python package.
+Python package. After adding this module, you will need to restart your server
+before you can use the tags or filters in templates.
Your custom tags and filters will live in a module inside the ``templatetags``
directory. The name of the module file is the name you'll use to load the tags
@@ -72,6 +73,8 @@
For more information on the :ttag:`load` tag, read its documentation.
+.. _howto-writing-custom-template-filters:
+
Writing custom template filters
-------------------------------
diff --git a/lib/django-1.5/docs/howto/deployment/wsgi/index.txt b/lib/django-1.5/docs/howto/deployment/wsgi/index.txt
index 769d406..95e258a 100644
--- a/lib/django-1.5/docs/howto/deployment/wsgi/index.txt
+++ b/lib/django-1.5/docs/howto/deployment/wsgi/index.txt
@@ -71,3 +71,11 @@
combine a Django application with a WSGI application of another framework.
.. _`WSGI middleware`: http://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides
+
+.. note::
+
+ Some third-party WSGI middleware do not call ``close`` on the response
+ object after handling a request — most notably Sentry's error reporting
+ middleware up to version 2.0.7. In those cases the
+ :data:`~django.core.signals.request_finished` signal isn't sent. This can
+ result in idle connections to database and memcache servers.
diff --git a/lib/django-1.5/docs/howto/deployment/wsgi/modwsgi.txt b/lib/django-1.5/docs/howto/deployment/wsgi/modwsgi.txt
index ead04a4..99e1733 100644
--- a/lib/django-1.5/docs/howto/deployment/wsgi/modwsgi.txt
+++ b/lib/django-1.5/docs/howto/deployment/wsgi/modwsgi.txt
@@ -25,7 +25,8 @@
===================
Once you've got mod_wsgi installed and activated, edit your Apache server's
-``httpd.conf`` file and add
+``httpd.conf`` file and add the following. If you are using a version of Apache
+older than 2.4, replace ``Require all granted`` with ``Allow from all``.
.. code-block:: apache
@@ -35,7 +36,7 @@
<Directory /path/to/mysite.com/mysite>
<Files wsgi.py>
Order deny,allow
- Allow from all
+ Require all granted
</Files>
</Directory>
diff --git a/lib/django-1.5/docs/howto/deployment/wsgi/uwsgi.txt b/lib/django-1.5/docs/howto/deployment/wsgi/uwsgi.txt
index b5d4384..b9e3811 100644
--- a/lib/django-1.5/docs/howto/deployment/wsgi/uwsgi.txt
+++ b/lib/django-1.5/docs/howto/deployment/wsgi/uwsgi.txt
@@ -9,6 +9,14 @@
.. _uWSGI: http://projects.unbit.it/uwsgi/
+.. seealso::
+
+ The uWSGI docs offer a `tutorial`_ covering Django, nginx, and uWSGI (one
+ possible deployment setup of many). The docs below are focused on how to
+ integrate Django with uWSGI.
+
+ .. _tutorial: https://uwsgi.readthedocs.org/en/latest/tutorials/Django_and_nginx.html
+
Prerequisite: uWSGI
===================
@@ -26,6 +34,15 @@
.. _installation procedures: http://projects.unbit.it/uwsgi/wiki/Install
+.. warning::
+
+ Some distributions, including Debian and Ubuntu, ship an outdated version
+ of uWSGI that does not conform to the WSGI specification. Versions prior to
+ 1.2.6 do not call ``close`` on the response object after handling a
+ request. In those cases the :data:`~django.core.signals.request_finished`
+ signal isn't sent. This can result in idle connections to database and
+ memcache servers.
+
uWSGI model
-----------
@@ -93,6 +110,6 @@
uwsgi --ini uwsgi.ini
See the uWSGI docs on `managing the uWSGI process`_ for information on
-starting, stoping and reloading the uWSGI workers.
+starting, stopping and reloading the uWSGI workers.
.. _managing the uWSGI process: http://projects.unbit.it/uwsgi/wiki/Management
diff --git a/lib/django-1.5/docs/howto/index.txt b/lib/django-1.5/docs/howto/index.txt
index d39222b..fe25c11 100644
--- a/lib/django-1.5/docs/howto/index.txt
+++ b/lib/django-1.5/docs/howto/index.txt
@@ -15,13 +15,15 @@
custom-template-tags
custom-file-storage
deployment/index
+ upgrade-version
error-reporting
initial-data
jython
legacy-databases
outputting-csv
outputting-pdf
- static-files
+ static-files/index
+ static-files/deployment
.. seealso::
diff --git a/lib/django-1.5/docs/howto/static-files.txt b/lib/django-1.5/docs/howto/static-files.txt
deleted file mode 100644
index 964b5fa..0000000
--- a/lib/django-1.5/docs/howto/static-files.txt
+++ /dev/null
@@ -1,508 +0,0 @@
-=====================
-Managing static files
-=====================
-
-Django developers mostly concern themselves with the dynamic parts of web
-applications -- the views and templates that render anew for each request. But
-web applications have other parts: the static files (images, CSS,
-Javascript, etc.) that are needed to render a complete web page.
-
-For small projects, this isn't a big deal, because you can just keep the
-static files somewhere your web server can find it. However, in bigger
-projects -- especially those comprised of multiple apps -- dealing with the
-multiple sets of static files provided by each application starts to get
-tricky.
-
-That's what ``django.contrib.staticfiles`` is for: it collects static files
-from each of your applications (and any other places you specify) into a
-single location that can easily be served in production.
-
-.. note::
-
- If you've used the `django-staticfiles`_ third-party app before, then
- ``django.contrib.staticfiles`` will look very familiar. That's because
- they're essentially the same code: ``django.contrib.staticfiles`` started
- its life as `django-staticfiles`_ and was merged into Django 1.3.
-
- If you're upgrading from ``django-staticfiles``, please see `Upgrading from
- django-staticfiles`_, below, for a few minor changes you'll need to make.
-
-.. _django-staticfiles: http://pypi.python.org/pypi/django-staticfiles/
-
-Using ``django.contrib.staticfiles``
-====================================
-
-Basic usage
------------
-
-1. Put your static files somewhere that ``staticfiles`` will find them.
-
- By default, this means within ``static/`` subdirectories of apps in your
- :setting:`INSTALLED_APPS`.
-
- Your project will probably also have static assets that aren't tied to a
- particular app. The :setting:`STATICFILES_DIRS` setting is a tuple of
- filesystem directories to check when loading static files. It's a search
- path that is by default empty. See the :setting:`STATICFILES_DIRS` docs
- how to extend this list of additional paths.
-
- Additionally, see the documentation for the :setting:`STATICFILES_FINDERS`
- setting for details on how ``staticfiles`` finds your files.
-
-2. Make sure that ``django.contrib.staticfiles`` is included in your
- :setting:`INSTALLED_APPS`.
-
- For :ref:`local development<staticfiles-development>`, if you are using
- :ref:`runserver<staticfiles-runserver>` or adding
- :ref:`staticfiles_urlpatterns<staticfiles-development>` to your
- URLconf, you're done with the setup -- your static files will
- automatically be served at the default (for
- :djadmin:`newly created<startproject>` projects) :setting:`STATIC_URL`
- of ``/static/``.
-
-3. You'll probably need to refer to these files in your templates. The
- easiest method is to use the included context processor which allows
- template code like:
-
- .. code-block:: html+django
-
- <img src="{{ STATIC_URL }}images/hi.jpg" alt="Hi!" />
-
- See :ref:`staticfiles-in-templates` for more details, **including** an
- alternate method using a template tag.
-
-Deploying static files in a nutshell
-------------------------------------
-
-When you're ready to move out of local development and deploy your project:
-
-1. Set the :setting:`STATIC_URL` setting to the public URL for your static
- files (in most cases, the default value of ``/static/`` is just fine).
-
-2. Set the :setting:`STATIC_ROOT` setting to point to the filesystem path
- you'd like your static files collected to when you use the
- :djadmin:`collectstatic` management command. For example::
-
- STATIC_ROOT = "/home/jacob/projects/mysite.com/sitestatic"
-
-3. Run the :djadmin:`collectstatic` management command::
-
- ./manage.py collectstatic
-
- This'll churn through your static file storage and copy them into the
- directory given by :setting:`STATIC_ROOT`.
-
-4. Deploy those files by configuring your webserver of choice to serve the
- files in :setting:`STATIC_ROOT` at :setting:`STATIC_URL`.
-
- :ref:`staticfiles-production` covers some common deployment strategies
- for static files.
-
-Those are the **basics**. For more details on common configuration options,
-read on; for a detailed reference of the settings, commands, and other bits
-included with the framework see
-:doc:`the staticfiles reference </ref/contrib/staticfiles>`.
-
-.. note::
-
- In previous versions of Django, it was common to place static assets in
- :setting:`MEDIA_ROOT` along with user-uploaded files, and serve them both
- at :setting:`MEDIA_URL`. Part of the purpose of introducing the
- ``staticfiles`` app is to make it easier to keep static files separate
- from user-uploaded files.
-
- For this reason, you need to make your :setting:`MEDIA_ROOT` and
- :setting:`MEDIA_URL` different from your :setting:`STATIC_ROOT` and
- :setting:`STATIC_URL`. You will need to arrange for serving of files in
- :setting:`MEDIA_ROOT` yourself; ``staticfiles`` does not deal with
- user-uploaded files at all. You can, however, use
- :func:`django.views.static.serve` view for serving :setting:`MEDIA_ROOT`
- in development; see :ref:`staticfiles-other-directories`.
-
-.. _staticfiles-in-templates:
-
-Referring to static files in templates
-======================================
-
-At some point, you'll probably need to link to static files in your templates.
-You could, of course, simply hardcode the path to you assets in the templates:
-
-.. code-block:: html
-
- <img src="http://static.example.com/static/myimage.jpg" alt="Sample image" />
-
-Of course, there are some serious problems with this: it doesn't work well in
-development, and it makes it *very* hard to change where you've deployed your
-static files. If, for example, you wanted to switch to using a content
-delivery network (CDN), then you'd need to change more or less every single
-template.
-
-A far better way is to use the value of the :setting:`STATIC_URL` setting
-directly in your templates. This means that a switch of static files servers
-only requires changing that single value. Much better!
-
-Django includes multiple built-in ways of using this setting in your
-templates: a context processor and a template tag.
-
-With a context processor
-------------------------
-
-The included context processor is the easy way. Simply make sure
-``'django.core.context_processors.static'`` is in your
-:setting:`TEMPLATE_CONTEXT_PROCESSORS`. It's there by default, and if you're
-editing that setting by hand it should look something like::
-
- TEMPLATE_CONTEXT_PROCESSORS = (
- 'django.core.context_processors.debug',
- 'django.core.context_processors.i18n',
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- )
-
-Once that's done, you can refer to :setting:`STATIC_URL` in your templates:
-
-.. code-block:: html+django
-
- <img src="{{ STATIC_URL }}images/hi.jpg" alt="Hi!" />
-
-If ``{{ STATIC_URL }}`` isn't working in your template, you're probably not
-using :class:`~django.template.RequestContext` when rendering the template.
-
-As a brief refresher, context processors add variables into the contexts of
-every template. However, context processors require that you use
-:class:`~django.template.RequestContext` when rendering templates. This happens
-automatically if you're using a :doc:`generic view </ref/class-based-views/index>`,
-but in views written by hand you'll need to explicitly use ``RequestContext``
-To see how that works, and to read more details, check out
-:ref:`subclassing-context-requestcontext`.
-
-Another option is the :ttag:`get_static_prefix` template tag that is part of
-Django's core.
-
-With a template tag
--------------------
-
-The more powerful tool is the :ttag:`static<staticfiles-static>` template
-tag. It builds the URL for the given relative path by using the configured
-:setting:`STATICFILES_STORAGE` storage.
-
-.. code-block:: html+django
-
- {% load staticfiles %}
- <img src="{% static "images/hi.jpg" %}" alt="Hi!"/>
-
-It is also able to consume standard context variables, e.g. assuming a
-``user_stylesheet`` variable is passed to the template:
-
-.. code-block:: html+django
-
- {% load staticfiles %}
- <link rel="stylesheet" href="{% static user_stylesheet %}" type="text/css" media="screen" />
-
-.. note::
-
- There is also a template tag named :ttag:`static` in Django's core set
- of :ref:`built in template tags<ref-templates-builtins-tags>` which has
- the same argument signature but only uses `urlparse.urljoin()`_ with the
- :setting:`STATIC_URL` setting and the given path. This has the
- disadvantage of not being able to easily switch the storage backend
- without changing the templates, so in doubt use the ``staticfiles``
- :ttag:`static<staticfiles-static>`
- template tag.
-
-.. _`urlparse.urljoin()`: http://docs.python.org/library/urlparse.html#urlparse.urljoin
-
-.. _staticfiles-development:
-
-Serving static files in development
-===================================
-
-The static files tools are mostly designed to help with getting static files
-successfully deployed into production. This usually means a separate,
-dedicated static file server, which is a lot of overhead to mess with when
-developing locally. Thus, the ``staticfiles`` app ships with a
-**quick and dirty helper view** that you can use to serve files locally in
-development.
-
-This view is automatically enabled and will serve your static files at
-:setting:`STATIC_URL` when you use the built-in
-:ref:`runserver<staticfiles-runserver>` management command.
-
-To enable this view if you are using some other server for local development,
-you'll add a couple of lines to your URLconf. The first line goes at the top
-of the file, and the last line at the bottom::
-
- from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-
- # ... the rest of your URLconf goes here ...
-
- urlpatterns += staticfiles_urlpatterns()
-
-This will inspect your :setting:`STATIC_URL` setting and wire up the view
-to serve static files accordingly. Don't forget to set the
-:setting:`STATICFILES_DIRS` setting appropriately to let
-``django.contrib.staticfiles`` know where to look for files additionally to
-files in app directories.
-
-.. warning::
-
- This will only work if :setting:`DEBUG` is ``True``.
-
- That's because this view is **grossly inefficient** and probably
- **insecure**. This is only intended for local development, and should
- **never be used in production**.
-
- Additionally, when using ``staticfiles_urlpatterns`` your
- :setting:`STATIC_URL` setting can't be empty or a full URL, such as
- ``http://static.example.com/``.
-
-For a few more details on how the ``staticfiles`` can be used during
-development, see :ref:`staticfiles-development-view`.
-
-.. _staticfiles-other-directories:
-
-Serving other directories
--------------------------
-
-.. currentmodule:: django.views.static
-.. function:: serve(request, path, document_root, show_indexes=False)
-
-There may be files other than your project's static assets that, for
-convenience, you'd like to have Django serve for you in local development.
-The :func:`~django.views.static.serve` view can be used to serve any directory
-you give it. (Again, this view is **not** hardened for production
-use, and should be used only as a development aid; you should serve these files
-in production using a real front-end webserver).
-
-The most likely example is user-uploaded content in :setting:`MEDIA_ROOT`.
-``staticfiles`` is intended for static assets and has no built-in handling
-for user-uploaded files, but you can have Django serve your
-:setting:`MEDIA_ROOT` by appending something like this to your URLconf::
-
- from django.conf import settings
-
- # ... the rest of your URLconf goes here ...
-
- if settings.DEBUG:
- urlpatterns += patterns('',
- url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
- 'document_root': settings.MEDIA_ROOT,
- }),
- )
-
-Note, the snippet assumes your :setting:`MEDIA_URL` has a value of
-``'/media/'``. This will call the :func:`~django.views.static.serve` view,
-passing in the path from the URLconf and the (required) ``document_root``
-parameter.
-
-.. currentmodule:: django.conf.urls.static
-.. function:: static(prefix, view='django.views.static.serve', **kwargs)
-
-Since it can become a bit cumbersome to define this URL pattern, Django
-ships with a small URL helper function
-:func:`~django.conf.urls.static.static` that takes as parameters the prefix
-such as :setting:`MEDIA_URL` and a dotted path to a view, such as
-``'django.views.static.serve'``. Any other function parameter will be
-transparently passed to the view.
-
-An example for serving :setting:`MEDIA_URL` (``'/media/'``) during
-development::
-
- from django.conf import settings
- from django.conf.urls.static import static
-
- urlpatterns = patterns('',
- # ... the rest of your URLconf goes here ...
- ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
-
-.. note::
-
- This helper function will only be operational in debug mode and if
- the given prefix is local (e.g. ``/static/``) and not a URL (e.g.
- ``http://static.example.com/``).
-
-.. _staticfiles-production:
-
-Serving static files in production
-==================================
-
-The basic outline of putting static files into production is simple: run the
-:djadmin:`collectstatic` command when static files change, then arrange for
-the collected static files directory (:setting:`STATIC_ROOT`) to be moved to
-the static file server and served.
-
-Of course, as with all deployment tasks, the devil's in the details. Every
-production setup will be a bit different, so you'll need to adapt the basic
-outline to fit your needs. Below are a few common patterns that might help.
-
-Serving the app and your static files from the same server
-----------------------------------------------------------
-
-If you want to serve your static files from the same server that's already
-serving your site, the basic outline gets modified to look something like:
-
-* Push your code up to the deployment server.
-* On the server, run :djadmin:`collectstatic` to copy all the static files
- into :setting:`STATIC_ROOT`.
-* Point your web server at :setting:`STATIC_ROOT`. For example, here's
- :ref:`how to do this under Apache and mod_wsgi <serving-files>`.
-
-You'll probably want to automate this process, especially if you've got
-multiple web servers. There's any number of ways to do this automation, but
-one option that many Django developers enjoy is `Fabric`__.
-
-__ http://fabfile.org/
-
-Below, and in the following sections, we'll show off a few example fabfiles
-(i.e. Fabric scripts) that automate these file deployment options. The syntax
-of a fabfile is fairly straightforward but won't be covered here; consult
-`Fabric's documentation`__, for a complete explanation of the syntax..
-
-__ http://docs.fabfile.org/
-
-So, a fabfile to deploy static files to a couple of web servers might look
-something like::
-
- from fabric.api import *
-
- # Hosts to deploy onto
- env.hosts = ['www1.example.com', 'www2.example.com']
-
- # Where your project code lives on the server
- env.project_root = '/home/www/myproject'
-
- def deploy_static():
- with cd(env.project_root):
- run('./manage.py collectstatic -v0 --noinput')
-
-Serving static files from a dedicated server
---------------------------------------------
-
-Most larger Django apps use a separate Web server -- i.e., one that's not also
-running Django -- for serving static files. This server often runs a different
-type of web server -- faster but less full-featured. Some good choices are:
-
-* lighttpd_
-* Nginx_
-* TUX_
-* Cherokee_
-* A stripped-down version of Apache_
-
-.. _lighttpd: http://www.lighttpd.net/
-.. _Nginx: http://wiki.nginx.org/Main
-.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
-.. _Apache: http://httpd.apache.org/
-.. _Cherokee: http://www.cherokee-project.com/
-
-Configuring these servers is out of scope of this document; check each
-server's respective documentation for instructions.
-
-Since your static file server won't be running Django, you'll need to modify
-the deployment strategy to look something like:
-
-* When your static files change, run :djadmin:`collectstatic` locally.
-* Push your local :setting:`STATIC_ROOT` up to the static file server
- into the directory that's being served. ``rsync`` is a good
- choice for this step since it only needs to transfer the
- bits of static files that have changed.
-
-Here's how this might look in a fabfile::
-
- from fabric.api import *
- from fabric.contrib import project
-
- # Where the static files get collected locally
- env.local_static_root = '/tmp/static'
-
- # Where the static files should go remotely
- env.remote_static_root = '/home/www/static.example.com'
-
- @roles('static')
- def deploy_static():
- local('./manage.py collectstatic')
- project.rsync_project(
- remote_dir = env.remote_static_root,
- local_dir = env.local_static_root,
- delete = True
- )
-
-.. _staticfiles-from-cdn:
-
-Serving static files from a cloud service or CDN
-------------------------------------------------
-
-Another common tactic is to serve static files from a cloud storage provider
-like Amazon's S3__ and/or a CDN (content delivery network). This lets you
-ignore the problems of serving static files, and can often make for
-faster-loading webpages (especially when using a CDN).
-
-When using these services, the basic workflow would look a bit like the above,
-except that instead of using ``rsync`` to transfer your static files to the
-server you'd need to transfer the static files to the storage provider or CDN.
-
-There's any number of ways you might do this, but if the provider has an API a
-:doc:`custom file storage backend </howto/custom-file-storage>` will make the
-process incredibly simple. If you've written or are using a 3rd party custom
-storage backend, you can tell :djadmin:`collectstatic` to use it by setting
-:setting:`STATICFILES_STORAGE` to the storage engine.
-
-For example, if you've written an S3 storage backend in
-``myproject.storage.S3Storage`` you could use it with::
-
- STATICFILES_STORAGE = 'myproject.storage.S3Storage'
-
-Once that's done, all you have to do is run :djadmin:`collectstatic` and your
-static files would be pushed through your storage package up to S3. If you
-later needed to switch to a different storage provider, it could be as simple
-as changing your :setting:`STATICFILES_STORAGE` setting.
-
-For details on how you'd write one of these backends,
-:doc:`/howto/custom-file-storage`.
-
-.. seealso::
-
- The `django-storages`__ project is a 3rd party app that provides many
- storage backends for many common file storage APIs (including `S3`__).
-
-__ http://s3.amazonaws.com/
-__ http://code.larlet.fr/django-storages/
-__ http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html
-
-Upgrading from ``django-staticfiles``
-=====================================
-
-``django.contrib.staticfiles`` began its life as `django-staticfiles`_. If
-you're upgrading from `django-staticfiles`_ older than 1.0 (e.g. 0.3.4) to
-``django.contrib.staticfiles``, you'll need to make a few changes:
-
-* Application files should now live in a ``static`` directory in each app
- (`django-staticfiles`_ used the name ``media``, which was slightly
- confusing).
-
-* The management commands ``build_static`` and ``resolve_static`` are now
- called :djadmin:`collectstatic` and :djadmin:`findstatic`.
-
-* The settings ``STATICFILES_PREPEND_LABEL_APPS``,
- ``STATICFILES_MEDIA_DIRNAMES`` and ``STATICFILES_EXCLUDED_APPS`` were
- removed.
-
-* The setting ``STATICFILES_RESOLVERS`` was removed, and replaced by the
- new :setting:`STATICFILES_FINDERS`.
-
-* The default for :setting:`STATICFILES_STORAGE` was renamed from
- ``staticfiles.storage.StaticFileStorage`` to
- ``staticfiles.storage.StaticFilesStorage``
-
-* If using :ref:`runserver<staticfiles-runserver>` for local development
- (and the :setting:`DEBUG` setting is ``True``), you no longer need to add
- anything to your URLconf for serving static files in development.
-
-Learn more
-==========
-
-This document has covered the basics and some common usage patterns. For
-complete details on all the settings, commands, template tags, and other pieces
-include in ``django.contrib.staticfiles``, see :doc:`the staticfiles reference
-</ref/contrib/staticfiles>`.
diff --git a/lib/django-1.5/docs/howto/static-files/deployment.txt b/lib/django-1.5/docs/howto/static-files/deployment.txt
new file mode 100644
index 0000000..865a5c6
--- /dev/null
+++ b/lib/django-1.5/docs/howto/static-files/deployment.txt
@@ -0,0 +1,159 @@
+======================
+Deploying static files
+======================
+
+.. seealso::
+
+ For an introduction to the use of :mod:`django.contrib.staticfiles`, see
+ :doc:`/howto/static-files/index`.
+
+.. _staticfiles-production:
+
+Serving static files in production
+==================================
+
+The basic outline of putting static files into production is simple: run the
+:djadmin:`collectstatic` command when static files change, then arrange for
+the collected static files directory (:setting:`STATIC_ROOT`) to be moved to
+the static file server and served. Depending on :setting:`STATICFILES_STORAGE`,
+files may need to be moved to a new location manually or the :func:`post_process
+<django.contrib.staticfiles.storage.StaticFilesStorage.post_process>` method
+of the ``Storage`` class might take care of that.
+
+Of course, as with all deployment tasks, the devil's in the details. Every
+production setup will be a bit different, so you'll need to adapt the basic
+outline to fit your needs. Below are a few common patterns that might help.
+
+Serving the site and your static files from the same server
+-----------------------------------------------------------
+
+If you want to serve your static files from the same server that's already
+serving your site, the process may look something like:
+
+* Push your code up to the deployment server.
+* On the server, run :djadmin:`collectstatic` to copy all the static files
+ into :setting:`STATIC_ROOT`.
+* Configure your web server to serve the files in :setting:`STATIC_ROOT`
+ under the URL :setting:`STATIC_URL`. For example, here's
+ :ref:`how to do this with Apache and mod_wsgi <serving-files>`.
+
+You'll probably want to automate this process, especially if you've got
+multiple web servers. There's any number of ways to do this automation, but
+one option that many Django developers enjoy is `Fabric
+<http://fabfile.org/>`_.
+
+Below, and in the following sections, we'll show off a few example fabfiles
+(i.e. Fabric scripts) that automate these file deployment options. The syntax
+of a fabfile is fairly straightforward but won't be covered here; consult
+`Fabric's documentation <http://docs.fabfile.org/>`_, for a complete
+explanation of the syntax.
+
+So, a fabfile to deploy static files to a couple of web servers might look
+something like::
+
+ from fabric.api import *
+
+ # Hosts to deploy onto
+ env.hosts = ['www1.example.com', 'www2.example.com']
+
+ # Where your project code lives on the server
+ env.project_root = '/home/www/myproject'
+
+ def deploy_static():
+ with cd(env.project_root):
+ run('./manage.py collectstatic -v0 --noinput')
+
+Serving static files from a dedicated server
+--------------------------------------------
+
+Most larger Django sites use a separate Web server -- i.e., one that's not also
+running Django -- for serving static files. This server often runs a different
+type of web server -- faster but less full-featured. Some common choices are:
+
+* lighttpd_
+* Nginx_
+* TUX_
+* Cherokee_
+* A stripped-down version of Apache_
+
+.. _lighttpd: http://www.lighttpd.net/
+.. _Nginx: http://wiki.nginx.org/Main
+.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
+.. _Apache: http://httpd.apache.org/
+.. _Cherokee: http://www.cherokee-project.com/
+
+Configuring these servers is out of scope of this document; check each
+server's respective documentation for instructions.
+
+Since your static file server won't be running Django, you'll need to modify
+the deployment strategy to look something like:
+
+* When your static files change, run :djadmin:`collectstatic` locally.
+
+* Push your local :setting:`STATIC_ROOT` up to the static file server into the
+ directory that's being served. `rsync <https://rsync.samba.org/>`_ is a
+ common choice for this step since it only needs to transfer the bits of
+ static files that have changed.
+
+Here's how this might look in a fabfile::
+
+ from fabric.api import *
+ from fabric.contrib import project
+
+ # Where the static files get collected locally. Your STATIC_ROOT setting.
+ env.local_static_root = '/tmp/static'
+
+ # Where the static files should go remotely
+ env.remote_static_root = '/home/www/static.example.com'
+
+ @roles('static')
+ def deploy_static():
+ local('./manage.py collectstatic')
+ project.rsync_project(
+ remote_dir = env.remote_static_root,
+ local_dir = env.local_static_root,
+ delete = True
+ )
+
+.. _staticfiles-from-cdn:
+
+Serving static files from a cloud service or CDN
+------------------------------------------------
+
+Another common tactic is to serve static files from a cloud storage provider
+like Amazon's S3 and/or a CDN (content delivery network). This lets you
+ignore the problems of serving static files and can often make for
+faster-loading webpages (especially when using a CDN).
+
+When using these services, the basic workflow would look a bit like the above,
+except that instead of using ``rsync`` to transfer your static files to the
+server you'd need to transfer the static files to the storage provider or CDN.
+
+There's any number of ways you might do this, but if the provider has an API a
+:doc:`custom file storage backend </howto/custom-file-storage>` will make the
+process incredibly simple. If you've written or are using a 3rd party custom
+storage backend, you can tell :djadmin:`collectstatic` to use it by setting
+:setting:`STATICFILES_STORAGE` to the storage engine.
+
+For example, if you've written an S3 storage backend in
+``myproject.storage.S3Storage`` you could use it with::
+
+ STATICFILES_STORAGE = 'myproject.storage.S3Storage'
+
+Once that's done, all you have to do is run :djadmin:`collectstatic` and your
+static files would be pushed through your storage package up to S3. If you
+later needed to switch to a different storage provider, it could be as simple
+as changing your :setting:`STATICFILES_STORAGE` setting.
+
+For details on how you'd write one of these backends, see
+:doc:`/howto/custom-file-storage`. There are 3rd party apps available that
+provide storage backends for many common file storage APIs. A good starting
+point is the `overview at djangopackages.com
+<https://www.djangopackages.com/grids/g/storage-backends/>`_.
+
+Learn more
+==========
+
+For complete details on all the settings, commands, template tags, and other
+pieces included in :mod:`django.contrib.staticfiles`, see :doc:`the
+staticfiles reference </ref/contrib/staticfiles>`.
diff --git a/lib/django-1.5/docs/howto/static-files/index.txt b/lib/django-1.5/docs/howto/static-files/index.txt
new file mode 100644
index 0000000..6d67fe6
--- /dev/null
+++ b/lib/django-1.5/docs/howto/static-files/index.txt
@@ -0,0 +1,131 @@
+===================================
+Managing static files (CSS, images)
+===================================
+
+Websites generally need to serve additional files such as images, JavaScript,
+or CSS. In Django, we refer to these files as "static files". Django provides
+:mod:`django.contrib.staticfiles` to help you manage them.
+
+This page describes how you can serve these static files.
+
+Configuring static files
+========================
+
+1. Make sure that ``django.contrib.staticfiles`` is included in your
+ :setting:`INSTALLED_APPS`.
+
+2. In your settings file, define :setting:`STATIC_URL`, for example::
+
+ STATIC_URL = '/static/'
+
+3. In your templates, either hardcode the url like
+ ``/static/my_app/myexample.jpg`` or, preferably, use the
+ :ttag:`static<staticfiles-static>` template tag to build the URL for the given
+ relative path by using the configured :setting:`STATICFILES_STORAGE` storage
+ (this makes it much easier when you want to switch to a content delivery
+ network (CDN) for serving static files).
+
+ .. _staticfiles-in-templates:
+
+ .. code-block:: html+django
+
+ {% load staticfiles %}
+ <img src="{% static "my_app/myexample.jpg" %}" alt="My image"/>
+
+4. Store your static files in a folder called ``static`` in your app. For
+ example ``my_app/static/my_app/myimage.jpg``.
+
+.. admonition:: Serving the files
+
+ In addition to these configuration steps, you'll also need to actually
+ serve the static files.
+
+ During development, this will be done automatically if you use
+ :djadmin:`runserver` and :setting:`DEBUG` is set to ``True`` (see
+ :func:`django.contrib.staticfiles.views.serve`).
+
+ This method is **grossly inefficient** and probably **insecure**,
+ so it is **unsuitable for production**.
+
+ See :doc:`/howto/static-files/deployment` for proper strategies to serve
+ static files in production environments.
+
+Your project will probably also have static assets that aren't tied to a
+particular app. In addition to using a ``static/`` directory inside your apps,
+you can define a list of directories (:setting:`STATICFILES_DIRS`) in your
+settings file where Django will also look for static files. For example::
+
+ STATICFILES_DIRS = (
+ os.path.join(BASE_DIR, "static"),
+ '/var/www/static/',
+ )
+
+See the documentation for the :setting:`STATICFILES_FINDERS` setting for
+details on how ``staticfiles`` finds your files.
+
+.. admonition:: Static file namespacing
+
+ Now we *might* be able to get away with putting our static files directly
+ in ``my_app/static/`` (rather than creating another ``my_app``
+ subdirectory), but it would actually be a bad idea. Django will use the
+ first static file it finds whose name matches, and if you had a static file
+ with the same name in a *different* application, Django would be unable to
+ distinguish between them. We need to be able to point Django at the right
+ one, and the easiest way to ensure this is by *namespacing* them. That is,
+ by putting those static files inside *another* directory named for the
+ application itself.
+
+
+Serving files uploaded by a user
+================================
+
+During development, you can serve user-uploaded media files from
+:setting:`MEDIA_ROOT` using the :func:`django.contrib.staticfiles.views.serve`
+view. This is not suitable for production use! For some common deployment
+strategies, see :doc:`/howto/static-files/deployment`.
+
+For example, if your :setting:`MEDIA_URL` is defined as '/media/', you can do
+this by adding the following snippet to your urls.py::
+
+ from django.conf import settings
+ from django.conf.urls.static import static
+
+ urlpatterns = patterns('',
+ # ... the rest of your URLconf goes here ...
+ ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+
+.. note::
+
+ This helper function works only in debug mode and only if
+ the given prefix is local (e.g. ``/static/``) and not a URL (e.g.
+ ``http://static.example.com/``).
+
+Deployment
+==========
+
+:mod:`django.contrib.staticfiles` provides a convenience management command
+for gathering static files in a single directory so you can serve them easily.
+
+1. Set the :setting:`STATIC_ROOT` setting to the directory from which you'd
+ like to serve these files, for example::
+
+ STATIC_ROOT = "/var/www/example.com/static/"
+
+2. Run the :djadmin:`collectstatic` management command::
+
+ ./manage.py collectstatic
+
+ This will copy all files from your static folders into the
+ :setting:`STATIC_ROOT` directory.
+
+3. Use a webserver of your choice to serve the
+ files. :doc:`/howto/static-files/deployment` covers some common deployment
+ strategies for static files.
+
+Learn more
+==========
+
+This document has covered the basics and some common usage patterns. For
+complete details on all the settings, commands, template tags, and other pieces
+included in :mod:`django.contrib.staticfiles`, see :doc:`the staticfiles
+reference </ref/contrib/staticfiles>`.
diff --git a/lib/django-1.5/docs/howto/upgrade-version.txt b/lib/django-1.5/docs/howto/upgrade-version.txt
new file mode 100644
index 0000000..fd018d4
--- /dev/null
+++ b/lib/django-1.5/docs/howto/upgrade-version.txt
@@ -0,0 +1,99 @@
+===================================
+Upgrading Django to a newer version
+===================================
+
+While it can be a complex process at times, upgrading to the latest Django
+version has several benefits:
+
+* New features and improvements are added.
+* Bugs are fixed.
+* Older version of Django will eventually no longer receive security updates.
+ (see :ref:`backwards-compatibility-policy`).
+* Upgrading as each new Django release is available makes future upgrades less
+ painful by keeping your code base up to date.
+
+Here are some things to consider to help make your upgrade process as smooth as
+possible.
+
+Required Reading
+================
+
+If it's your first time doing an upgrade, it is useful to read the :doc:`guide
+on the different release processes </internals/release-process>`.
+
+Afterwards, you should familiarize yourself with the changes that were made in
+the new Django version(s):
+
+* Read the :doc:`release notes </releases/index>` for each 'final' release from
+ the one after your current Django version, up to and including the version to
+ which you plan to upgrade.
+* Look at the :doc:`deprecation timeline</internals/deprecation>` for the
+ relevant versions.
+
+Pay particular attention to backwards incompatible changes to get a clear idea
+of what will be needed for a successful upgrade.
+
+Dependencies
+============
+
+In most cases it will be necessary to upgrade to the latest version of your
+Django-related dependencies as well. If the Django version was recently
+released or if some of your dependencies are not well-maintained, some of your
+dependencies may not yet support the new Django version. In these cases you may
+have to wait until new versions of your dependencies are released.
+
+Installation
+============
+
+Once you're ready, it is time to :doc:`install the new Django version
+</topics/install>`. If you are using virtualenv_ and it is a major upgrade, you
+might want to set up a new environment will all the dependencies first.
+
+Exactly which steps you will need to take depends on your installation process.
+The most convenient way is to use pip_ with the ``--upgrade`` or ``-U`` flag:
+
+.. code-block:: bash
+
+ pip install -U Django
+
+pip_ also automatically uninstalls the previous version of Django.
+
+If you use some other installation process, you might have to manually
+:ref:`uninstall the old Django version <removing-old-versions-of-django>` and
+should look at the complete installation instructions.
+
+.. _pip: http://www.pip-installer.org/
+.. _virtualenv: http://www.virtualenv.org/
+
+Testing
+=======
+
+When the new environment is set up, :doc:`run the full test suite
+</topics/testing/overview>` for your application. In Python 2.7+, deprecation
+warnings are silenced by default. It is useful to turn the warnings on so they
+are shown in the test output (you can also use the flag if you test your app
+manually using ``manage.py runserver``):
+
+.. code-block:: bash
+
+ python -Wall manage.py test
+
+After you have run the tests, fix any failures. While you have the release
+notes fresh in your mind, it may also be a good time to take advantage of new
+features in Django by refactoring your code to eliminate any deprecation
+warnings.
+
+Deployment
+==========
+
+When you are sufficiently confident your app works with the new version of
+Django, you're ready to go ahead and :doc:`deploy </howto/deployment/index>`
+your upgraded Django project.
+
+If you are using caching provided by Django, you should consider clearing your
+cache after upgrading. Otherwise you may run into problems, for example, if you
+are caching pickled objects as these objects are not guaranteed to be
+pickle-compatible across Django versions. A past instance of incompatibility
+was caching pickled :class:`~django.http.HttpResponse` objects, either
+directly or indirectly via the :func:`~django.views.decorators.cache.cache_page`
+decorator.
diff --git a/lib/django-1.5/docs/index.txt b/lib/django-1.5/docs/index.txt
index d234cdc..b1686c6 100644
--- a/lib/django-1.5/docs/index.txt
+++ b/lib/django-1.5/docs/index.txt
@@ -45,7 +45,8 @@
:doc:`Part 2 <intro/tutorial02>` |
:doc:`Part 3 <intro/tutorial03>` |
:doc:`Part 4 <intro/tutorial04>` |
- :doc:`Part 5 <intro/tutorial05>`
+ :doc:`Part 5 <intro/tutorial05>` |
+ :doc:`Part 6 <intro/tutorial06>`
* **Advanced Tutorials:**
:doc:`How to write reusable apps <intro/reusable-apps>` |
@@ -98,6 +99,7 @@
:doc:`Decorators <topics/http/decorators>`
* **Reference:**
+ :doc:`Built-in Views <ref/views>` |
:doc:`Request/response objects <ref/request-response>` |
:doc:`TemplateResponse objects <ref/template-response>`
@@ -190,7 +192,7 @@
:doc:`Overview <howto/deployment/index>` |
:doc:`WSGI servers <howto/deployment/wsgi/index>` |
:doc:`FastCGI/SCGI/AJP <howto/deployment/fastcgi>` |
- :doc:`Handling static files <howto/static-files>` |
+ :doc:`Deploying static files <howto/static-files/deployment>` |
:doc:`Tracking code errors by email <howto/error-reporting>`
The admin
diff --git a/lib/django-1.5/docs/internals/committers.txt b/lib/django-1.5/docs/internals/committers.txt
index 7900dd8..95ef3c7 100644
--- a/lib/django-1.5/docs/internals/committers.txt
+++ b/lib/django-1.5/docs/internals/committers.txt
@@ -14,8 +14,9 @@
programming", and in technical circles as "the guy who invented Django."
He was lead developer at World Online for 2.5 years, during which time
- Django was developed and implemented on World Online's sites. He's now the
- leader and founder of EveryBlock_, a "news feed for your block".
+ Django was developed and implemented on World Online's sites. He was the
+ leader and founder of EveryBlock_, a "news feed for your block." He now
+ develops for Soundslice_.
Adrian lives in Chicago, USA.
@@ -40,13 +41,15 @@
`Wilson Miner`_
Wilson's design-fu is what makes Django look so nice. He designed the
Web site you're looking at right now, as well as Django's acclaimed admin
- interface. Wilson is the designer for EveryBlock_.
+ interface. Wilson was the designer for EveryBlock and Rdio_. He now
+ designs for Facebook.
Wilson lives in San Francisco, USA.
.. _lawrence journal-world: http://ljworld.com/
.. _adrian holovaty: http://holovaty.com/
.. _everyblock: http://everyblock.com/
+.. _soundslice: http://www.soundslice.com/
.. _simon willison: http://simonwillison.net/
.. _web-development blog: `simon willison`_
.. _jacob kaplan-moss: http://jacobian.org/
@@ -100,9 +103,9 @@
.. _russell keith-magee: http://cecinestpasun.com/
Joseph Kocherhans
- Joseph is currently a developer at EveryBlock_, and previously worked for
- the Lawrence Journal-World where he built most of the backend for their
- Marketplace site. He often disappears for several days into the woods,
+ Joseph was the director of lead development at EveryBlock and previously
+ developed at the Lawrence Journal-World. He is treasurer of the `Django
+ Software Foundation`_. He often disappears for several days into the woods,
attempts to teach himself computational linguistics, and annoys his
neighbors with his Charango_ playing.
@@ -113,6 +116,7 @@
Joseph lives in Chicago, USA.
+.. _django software foundation: https://www.djangoproject.com/foundation/
.. _charango: http://en.wikipedia.org/wiki/Charango
`Luke Plant`_
diff --git a/lib/django-1.5/docs/internals/contributing/committing-code.txt b/lib/django-1.5/docs/internals/contributing/committing-code.txt
index bc2f97a..b81bdd2 100644
--- a/lib/django-1.5/docs/internals/contributing/committing-code.txt
+++ b/lib/django-1.5/docs/internals/contributing/committing-code.txt
@@ -34,6 +34,8 @@
existing committer privately. Public requests for commit access are potential
flame-war starters, and will simply be ignored.
+.. _handling-pull-requests:
+
Handling pull requests
----------------------
diff --git a/lib/django-1.5/docs/internals/contributing/localizing.txt b/lib/django-1.5/docs/internals/contributing/localizing.txt
index 0cde778..01b88d8 100644
--- a/lib/django-1.5/docs/internals/contributing/localizing.txt
+++ b/lib/django-1.5/docs/internals/contributing/localizing.txt
@@ -62,5 +62,5 @@
.. _Transifex: https://www.transifex.com/
.. _Django i18n mailing list: http://groups.google.com/group/django-i18n/
-.. _Django project page: https://www.transifex.com/projects/p/django/
+.. _Django project page: https://www.transifex.com/projects/p/django-core/
.. _Transifex User Guide: http://help.transifex.com/
diff --git a/lib/django-1.5/docs/internals/contributing/new-contributors.txt b/lib/django-1.5/docs/internals/contributing/new-contributors.txt
index b752503..9e3b185 100644
--- a/lib/django-1.5/docs/internals/contributing/new-contributors.txt
+++ b/lib/django-1.5/docs/internals/contributing/new-contributors.txt
@@ -10,6 +10,13 @@
Start with these easy tasks to discover Django's development process.
+* **Sign the Contributor License Agreement**
+
+ The code that you write belongs to you or your employer. If your
+ contribution is more than one or two lines of code, you need to sign the
+ `CLA`_. See the `Contributor License Agreement FAQ`_ for a more thorough
+ explanation.
+
* **Triage tickets**
If an `unreviewed ticket`_ reports a bug, try and reproduce it. If you
@@ -51,6 +58,8 @@
.. _reports page: https://code.djangoproject.com/wiki/Reports
+.. _CLA: https://www.djangoproject.com/foundation/cla/
+.. _Contributor License Agreement FAQ: https://www.djangoproject.com/foundation/cla/faq/
.. _unreviewed ticket: https://code.djangoproject.com/query?status=!closed&stage=Unreviewed
diff --git a/lib/django-1.5/docs/internals/contributing/writing-code/unit-tests.txt b/lib/django-1.5/docs/internals/contributing/writing-code/unit-tests.txt
index afef554..1770e4a 100644
--- a/lib/django-1.5/docs/internals/contributing/writing-code/unit-tests.txt
+++ b/lib/django-1.5/docs/internals/contributing/writing-code/unit-tests.txt
@@ -29,15 +29,13 @@
Running the tests requires a Django settings module that defines the
databases to use. To make it easy to get started, Django provides a
sample settings module that uses the SQLite database. To run the tests
-with this sample ``settings`` module, ``cd`` into the Django
-``tests/`` directory and run:
+with this sample ``settings`` module:
.. code-block:: bash
- ./runtests.py --settings=test_sqlite
-
-If you get an ``ImportError: No module named django.contrib`` error,
-you need to add your install of Django to your ``PYTHONPATH``.
+ git clone git@github.com:django/django.git django-repo
+ cd django-repo/tests
+ PYTHONPATH=..:$PYTHONPATH python ./runtests.py --settings=test_sqlite
.. _running-unit-tests-settings:
@@ -49,14 +47,10 @@
if you're proposing patches for Django, it's a good idea to test
across databases), you may need to define your own settings file.
-To run the tests with different settings, ``cd`` to the ``tests/`` directory
-and type:
+To run the tests with different settings, ensure that the module is on your
+``PYTHONPATH`` and pass the module with ``--settings``.
-.. code-block:: bash
-
- ./runtests.py --settings=path.to.django.settings
-
-The :setting:`DATABASES` setting in this test settings module needs to define
+The :setting:`DATABASES` setting in any test settings module needs to define
two databases:
* A ``default`` database. This database should use the backend that
@@ -144,29 +138,48 @@
If you want to run the full suite of tests, you'll need to install a number of
dependencies:
+* PIL_
+* py-bcrypt_
* PyYAML_
* Markdown_
* Textile_
* Docutils_
+* pytz_
* setuptools_
* memcached_, plus a :ref:`supported Python binding <memcached>`
* gettext_ (:ref:`gettext_on_windows`)
-* selenium_ (if also using Python >= 2.6)
+* selenium_
+
+You can find these dependencies in `pip requirements files`_ inside the
+``tests/requirements`` directory of the Django source tree and install them
+like so::
+
+ pip install -r tests/requirements/py2.txt # Python 3: py3.txt
+
+You can also install the database adapter(s) of your choice using
+``oracle.txt``, ``mysql.txt``, or ``postgres.txt``.
If you want to test the memcached cache backend, you'll also need to define
a :setting:`CACHES` setting that points at your memcached instance.
+To run the GeoDjango tests, you will need to :doc:`setup a spatial database
+and install the Geospatial libraries</ref/contrib/gis/install/index>`.
+
Each of these dependencies is optional. If you're missing any of them, the
associated tests will be skipped.
+.. _PIL: https://pypi.python.org/pypi/PIL
+.. _py-bcrypt: https://pypi.python.org/pypi/py-bcrypt/
.. _PyYAML: http://pyyaml.org/wiki/PyYAML
.. _Markdown: http://pypi.python.org/pypi/Markdown/1.7
.. _Textile: http://pypi.python.org/pypi/textile
.. _docutils: http://pypi.python.org/pypi/docutils/0.4
+.. _pytz: https://pypi.python.org/pypi/pytz/
.. _setuptools: http://pypi.python.org/pypi/setuptools/
.. _memcached: http://memcached.org/
.. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html
.. _selenium: http://pypi.python.org/pypi/selenium
+.. _pip requirements files: http://www.pip-installer.org/en/latest/requirements.html
Code coverage
~~~~~~~~~~~~~
diff --git a/lib/django-1.5/docs/internals/contributing/writing-code/working-with-git.txt b/lib/django-1.5/docs/internals/contributing/writing-code/working-with-git.txt
index dcfdd9e..35ea25f 100644
--- a/lib/django-1.5/docs/internals/contributing/writing-code/working-with-git.txt
+++ b/lib/django-1.5/docs/internals/contributing/writing-code/working-with-git.txt
@@ -212,7 +212,7 @@
makes squashing easier.
After review
-------------
+~~~~~~~~~~~~
It is unusual to get any non-trivial amount of code into core without changes
requested by reviewers. In this case, it is often a good idea to add the
@@ -225,7 +225,8 @@
git rebase -i HEAD~2
-Squash the second commit into the first. Write a commit message along the lines of::
+Squash the second commit into the first. Write a commit message along the lines
+of::
Made changes asked in review by <reviewer>
@@ -239,8 +240,25 @@
Your pull request should now contain the new commit too.
-Note that the committer is likely to squash the review commit into the previous commit
-when committing the code.
+Note that the committer is likely to squash the review commit into the previous
+commit when committing the code.
+
+Working on a patch
+------------------
+
+One of the ways that developers can contribute to Django is by reviewing
+patches. Those patches will typically exist as pull requests on GitHub and
+can be easily integrated into your local repository::
+
+ git checkout -b pull_xxxxx upstream/master
+ curl https://github.com/django/django/pull/xxxxx.patch | git am
+
+This will create a new branch and then apply the changes from the pull request
+to it. At this point you can run the tests or do anything else you need to
+do to investigate the quality of the patch.
+
+For more detail on working with pull requests see the
+:ref:`guidelines for committers <handling-pull-requests>`.
Summary
-------
diff --git a/lib/django-1.5/docs/internals/deprecation.txt b/lib/django-1.5/docs/internals/deprecation.txt
index 55ab58d..dd2548b 100644
--- a/lib/django-1.5/docs/internals/deprecation.txt
+++ b/lib/django-1.5/docs/internals/deprecation.txt
@@ -111,7 +111,7 @@
these changes.
* Starting Django without a :setting:`SECRET_KEY` will result in an exception
- rather than a `DeprecationWarning`. (This is accelerated from the usual
+ rather than a ``DeprecationWarning``. (This is accelerated from the usual
deprecation path; see the :doc:`Django 1.4 release notes</releases/1.4>`.)
* The ``mod_python`` request handler will be removed. The ``mod_wsgi``
diff --git a/lib/django-1.5/docs/internals/security.txt b/lib/django-1.5/docs/internals/security.txt
index 7121ff3..96e1141 100644
--- a/lib/django-1.5/docs/internals/security.txt
+++ b/lib/django-1.5/docs/internals/security.txt
@@ -106,8 +106,12 @@
relevant patches and new releases, and crediting the reporter of
the issue (if the reporter wishes to be publicly identified).
+4. Post a notice to the `django-announce`_ mailing list that links to the blog
+ post.
+
.. _the Python Package Index: http://pypi.python.org/pypi
.. _the official Django development blog: https://www.djangoproject.com/weblog/
+.. _django-announce: http://groups.google.com/group/django-announce
If a reported issue is believed to be particularly time-sensitive --
due to a known exploit in the wild, for example -- the time between
@@ -212,4 +216,4 @@
will be sent to you by Django's release manager, and all notification
emails will be signed with the same key used to sign Django releases;
that key has the ID ``0x3684C0C08C8B2AE1``, and is available from most
-commonly-used keyservers.
\ No newline at end of file
+commonly-used keyservers.
diff --git a/lib/django-1.5/docs/intro/index.txt b/lib/django-1.5/docs/intro/index.txt
index ea6a3c4..6c62eb5 100644
--- a/lib/django-1.5/docs/intro/index.txt
+++ b/lib/django-1.5/docs/intro/index.txt
@@ -14,6 +14,7 @@
tutorial03
tutorial04
tutorial05
+ tutorial06
reusable-apps
whatsnext
contributing
diff --git a/lib/django-1.5/docs/intro/overview.txt b/lib/django-1.5/docs/intro/overview.txt
index 2081c96..77838ff 100644
--- a/lib/django-1.5/docs/intro/overview.txt
+++ b/lib/django-1.5/docs/intro/overview.txt
@@ -16,14 +16,18 @@
=================
Although you can use Django without a database, it comes with an
-object-relational mapper in which you describe your database layout in Python
+`object-relational mapper`_ in which you describe your database layout in Python
code.
+.. _object-relational mapper: http://en.wikipedia.org/wiki/Object-relational_mapping
+
The :doc:`data-model syntax </topics/db/models>` offers many rich ways of
representing your models -- so far, it's been solving two years' worth of
database-schema problems. Here's a quick example, which might be saved in
the file ``mysite/news/models.py``::
+ from django.db import models
+
class Reporter(models.Model):
full_name = models.CharField(max_length=70)
@@ -55,8 +59,9 @@
Enjoy the free API
==================
-With that, you've got a free, and rich, :doc:`Python API </topics/db/queries>` to
-access your data. The API is created on the fly, no code generation necessary:
+With that, you've got a free, and rich, :doc:`Python API </topics/db/queries>`
+to access your data. The API is created on the fly, no code generation
+necessary:
.. code-block:: python
@@ -133,9 +138,9 @@
============================================================================
Once your models are defined, Django can automatically create a professional,
-production ready :doc:`administrative interface </ref/contrib/admin/index>` -- a Web
-site that lets authenticated users add, change and delete objects. It's as easy
-as registering your model in the admin site::
+production ready :doc:`administrative interface </ref/contrib/admin/index>` --
+a Web site that lets authenticated users add, change and delete objects. It's
+as easy as registering your model in the admin site::
# In models.py...
@@ -171,9 +176,9 @@
in URLs, like ``.php`` or ``.asp``.
To design URLs for an app, you create a Python module called a :doc:`URLconf
-</topics/http/urls>`. A table of contents for your app, it contains a simple mapping
-between URL patterns and Python callback functions. URLconfs also serve to
-decouple URLs from Python code.
+</topics/http/urls>`. A table of contents for your app, it contains a simple
+mapping between URL patterns and Python callback functions. URLconfs also serve
+to decouple URLs from Python code.
Here's what a URLconf might look like for the ``Reporter``/``Article``
example above::
@@ -186,7 +191,7 @@
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
-The code above maps URLs, as simple regular expressions, to the location of
+The code above maps URLs, as simple `regular expressions`_, to the location of
Python callback functions ("views"). The regular expressions use parenthesis to
"capture" values from the URLs. When a user requests a page, Django runs
through each pattern, in order, and stops at the first one that matches the
@@ -194,6 +199,8 @@
This is blazingly fast, because the regular expressions are compiled at load
time.
+.. _regular expressions: http://docs.python.org/2/howto/regex.html
+
Once one of the regexes matches, Django imports and calls the given view, which
is a simple Python function. Each view gets passed a request object --
which contains request metadata -- and the values captured in the regex.
@@ -214,6 +221,8 @@
and renders the template with the retrieved data. Here's an example view for
``year_archive`` from above::
+ from django.shortcuts import render_to_response
+
def year_archive(request, year):
a_list = Article.objects.filter(pub_date__year=year)
return render_to_response('news/year_archive.html', {'year': year, 'article_list': a_list})
@@ -229,8 +238,8 @@
Django has a template search path, which allows you to minimize redundancy among
templates. In your Django settings, you specify a list of directories to check
-for templates. If a template doesn't exist in the first directory, it checks the
-second, and so on.
+for templates with :setting:`TEMPLATE_DIRS`. If a template doesn't exist in the
+first directory, it checks the second, and so on.
Let's say the ``news/year_archive.html`` template was found. Here's what that
might look like:
@@ -261,9 +270,10 @@
of a variable. In this case, the date filter formats a Python datetime object in
the given format (as found in PHP's date function).
-You can chain together as many filters as you'd like. You can write custom
-filters. You can write custom template tags, which run custom Python code behind
-the scenes.
+You can chain together as many filters as you'd like. You can write :ref:`custom
+template filters <howto-writing-custom-template-filters>`. You can write
+:doc:`custom template tags </howto/custom-template-tags>`, which run custom
+Python code behind the scenes.
Finally, Django uses the concept of "template inheritance": That's what the
``{% extends "base.html" %}`` does. It means "First load the template called
@@ -272,7 +282,7 @@
in templates: each template has to define only what's unique to that template.
Here's what the "base.html" template, including the use of :doc:`static files
-</howto/static-files>`, might look like:
+</howto/static-files/index>`, might look like:
.. code-block:: html+django
diff --git a/lib/django-1.5/docs/intro/reusable-apps.txt b/lib/django-1.5/docs/intro/reusable-apps.txt
index 9d1630c..c261433 100644
--- a/lib/django-1.5/docs/intro/reusable-apps.txt
+++ b/lib/django-1.5/docs/intro/reusable-apps.txt
@@ -2,11 +2,11 @@
Advanced tutorial: How to write reusable apps
=============================================
-This advanced tutorial begins where :doc:`Tutorial 5 </intro/tutorial05>` left
-off. We'll be turning our Web-poll into a standalone Python package you can
-reuse in new projects and share with other people.
+This advanced tutorial begins where :doc:`Tutorial 6 </intro/tutorial06>`
+left off. We'll be turning our Web-poll into a standalone Python package
+you can reuse in new projects and share with other people.
-If you haven't recently completed Tutorials 1–5, we encourage you to review
+If you haven't recently completed Tutorials 1–6, we encourage you to review
these so that your example project matches the one described below.
Reusability matters
@@ -66,12 +66,17 @@
admin.py
__init__.py
models.py
- tests.py
+ static/
+ polls/
+ images/
+ background.gif
+ style.css
templates/
polls/
detail.html
index.html
results.html
+ tests.py
urls.py
views.py
templates/
@@ -97,7 +102,7 @@
The current state of Python packaging is a bit muddled with various tools. For
this tutorial, we're going to use distribute_ to build our package. It's a
community-maintained fork of the older ``setuptools`` project. We'll also be
-using `pip`_ to uninstall it after we're finished. You should install these
+using `pip`_ to install and uninstall it. You should install these
two packages now. If you need help, you can refer to :ref:`how to install
Django with pip<installing-official-release>`. You can install ``distribute``
the same way.
@@ -125,7 +130,7 @@
2. Move the ``polls`` directory into the ``django-polls`` directory.
-3. Create a file ``django-polls/README.txt`` with the following contents::
+3. Create a file ``django-polls/README.rst`` with the following contents::
=====
Polls
@@ -153,7 +158,7 @@
3. Run `python manage.py syncdb` to create the polls models.
4. Start the development server and visit http://127.0.0.1:8000/admin/
- to create a poll (you'll need the Admin app enabled).
+ to create a poll (you'll need the Admin app enabled).
5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.
@@ -173,23 +178,23 @@
import os
from setuptools import setup
- README = open(os.path.join(os.path.dirname(__file__), 'README.txt')).read()
+ README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
setup(
- name = 'django-polls',
- version = '0.1',
- packages = ['polls'],
- include_package_data = True,
- license = 'BSD License', # example license
- description = 'A simple Django app to conduct Web-based polls.',
- long_description = README,
- url = 'http://www.example.com/',
- author = 'Your Name',
- author_email = 'yourname@example.com',
- classifiers = [
+ name='django-polls',
+ version='0.1',
+ packages=['polls'],
+ include_package_data=True,
+ license='BSD License', # example license
+ description='A simple Django app to conduct Web-based polls.',
+ long_description=README,
+ url='http://www.example.com/',
+ author='Your Name',
+ author_email='yourname@example.com',
+ classifiers=[
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
@@ -212,10 +217,13 @@
6. Only Python modules and packages are included in the package by default. To
include additional files, we'll need to create a ``MANIFEST.in`` file. The
distribute docs referred to in the previous step discuss this file in more
- details. To include the templates and our LICENSE file, create a file
- ``django-polls/MANIFEST.in`` with the following contents::
+ details. To include the templates, the ``README.rst`` and our ``LICENSE``
+ file, create a file ``django-polls/MANIFEST.in`` with the following
+ contents::
include LICENSE
+ include README.rst
+ recursive-include polls/static *
recursive-include polls/templates *
7. It's optional, but recommended, to include detailed documentation with your
@@ -255,28 +263,18 @@
tools that run as that user, so ``virtualenv`` is a more robust solution
(see below).
-1. Inside ``django-polls/dist``, untar the new package
- ``django-polls-0.1.tar.gz`` (e.g. ``tar xzvf django-polls-0.1.tar.gz``). If
- you're using Windows, you can download the command-line tool bsdtar_ to do
- this, or you can use a GUI-based tool such as 7-zip_.
+1. To install the package, use pip (you already :ref:`installed it
+ <installing-reusable-apps-prerequisites>`, right?)::
-2. Change into the directory created in step 1 (e.g. ``cd django-polls-0.1``).
+ pip install --user django-polls/dist/django-polls-0.1.tar.gz
-3. If you're using GNU/Linux, Mac OS X or some other flavor of Unix, enter the
- command ``python setup.py install --user`` at the shell prompt. If you're
- using Windows, start up a command shell and run the command
- ``setup.py install --user``.
-
- With luck, your Django project should now work correctly again. Run the
+2. With luck, your Django project should now work correctly again. Run the
server again to confirm this.
-4. To uninstall the package, use pip (you already :ref:`installed it
- <installing-reusable-apps-prerequisites>`, right?)::
+3. To uninstall the package, use pip::
pip uninstall django-polls
-.. _bsdtar: http://gnuwin32.sourceforge.net/packages/bsdtar.htm
-.. _7-zip: http://www.7-zip.org/
.. _pip: http://pypi.python.org/pypi/pip
Publishing your app
diff --git a/lib/django-1.5/docs/intro/tutorial01.txt b/lib/django-1.5/docs/intro/tutorial01.txt
index b6c2451..d9e8319 100644
--- a/lib/django-1.5/docs/intro/tutorial01.txt
+++ b/lib/django-1.5/docs/intro/tutorial01.txt
@@ -19,10 +19,15 @@
python -c "import django; print(django.get_version())"
-You should see either the version of your Django installation or an error
-telling "No module named django". Check also that the version number matches
-the version of this tutorial. If they don't match, you can refer to the
-tutorial for your version of Django or update Django to the newest version.
+If Django is installed, you should see the version of your installation. If it
+isn't, you'll get an error telling "No module named django".
+
+This tutorial is written for Django |version| and Python 2.x. If the Django
+version doesn't match, you can refer to the tutorial for your version of Django
+or update Django to the newest version. If you are using Python 3.x, be aware
+that your code may need to differ from what is in the tutorial and you should
+continue using the tutorial only if you know what you are doing with Python
+3.x.
See :doc:`How to install Django </topics/install>` for advice on how to remove
older versions of Django and install a newer one.
@@ -93,7 +98,7 @@
These files are:
-* The outer :file:`mysite/` directory is just a container for your
+* The outer :file:`mysite/` root directory is just a container for your
project. Its name doesn't matter to Django; you can rename it to anything
you like.
@@ -103,7 +108,7 @@
* The inner :file:`mysite/` directory is the actual Python package for your
project. Its name is the Python package name you'll need to use to import
- anything inside it (e.g. ``import mysite.settings``).
+ anything inside it (e.g. ``mysite.urls``).
* :file:`mysite/__init__.py`: An empty file that tells Python that this
directory should be considered a Python package. (Read `more about
@@ -259,11 +264,12 @@
python manage.py syncdb
-The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting and
-creates any necessary database tables according to the database settings in your
-:file:`settings.py` file. You'll see a message for each database table it
-creates, and you'll get a prompt asking you if you'd like to create a superuser
-account for the authentication system. Go ahead and do that.
+The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting
+and creates any necessary database tables according to the database settings
+in your :file:`mysite/settings.py` file. You'll see a message for each
+database table it creates, and you'll get a prompt asking you if you'd like to
+create a superuser account for the authentication system. Go ahead and do
+that.
If you're interested, run the command-line client for your database and type
``\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or ``.schema`` (SQLite) to
@@ -527,7 +533,7 @@
We're using this instead of simply typing "python", because :file:`manage.py`
sets the ``DJANGO_SETTINGS_MODULE`` environment variable, which gives Django
-the Python import path to your :file:`settings.py` file.
+the Python import path to your :file:`mysite/settings.py` file.
.. admonition:: Bypassing manage.py
@@ -583,27 +589,33 @@
of this object. Let's fix that by editing the polls model (in the
``polls/models.py`` file) and adding a
:meth:`~django.db.models.Model.__unicode__` method to both ``Poll`` and
-``Choice``::
+``Choice``. On Python 3, simply replace ``__unicode__`` by ``__str__`` in the
+following example::
class Poll(models.Model):
# ...
- def __unicode__(self):
+ def __unicode__(self): # Python 3: def __str__(self):
return self.question
class Choice(models.Model):
# ...
- def __unicode__(self):
+ def __unicode__(self): # Python 3: def __str__(self):
return self.choice_text
-It's important to add :meth:`~django.db.models.Model.__unicode__` methods to
-your models, not only for your own sanity when dealing with the interactive
-prompt, but also because objects' representations are used throughout Django's
-automatically-generated admin.
+It's important to add :meth:`~django.db.models.Model.__unicode__` methods (or
+:meth:`~django.db.models.Model.__str__` on Python 3) to your models, not only
+for your own sanity when dealing with the interactive prompt, but also because
+objects' representations are used throughout Django's automatically-generated
+admin.
-.. admonition:: Why :meth:`~django.db.models.Model.__unicode__` and not
+.. admonition:: :meth:`~django.db.models.Model.__unicode__` or
:meth:`~django.db.models.Model.__str__`?
- If you're familiar with Python, you might be in the habit of adding
+ On Python 3, things are simpler, just use
+ :meth:`~django.db.models.Model.__str__` and forget about
+ :meth:`~django.db.models.Model.__unicode__`.
+
+ If you're familiar with Python 2, you might be in the habit of adding
:meth:`~django.db.models.Model.__str__` methods to your classes, not
:meth:`~django.db.models.Model.__unicode__` methods. We use
:meth:`~django.db.models.Model.__unicode__` here because Django models deal
diff --git a/lib/django-1.5/docs/intro/tutorial02.txt b/lib/django-1.5/docs/intro/tutorial02.txt
index 1f7c0e8..396c1e0 100644
--- a/lib/django-1.5/docs/intro/tutorial02.txt
+++ b/lib/django-1.5/docs/intro/tutorial02.txt
@@ -109,9 +109,9 @@
But where's our poll app? It's not displayed on the admin index page.
-Just one thing to do: We need to tell the admin that ``Poll``
+Just one thing to do: we need to tell the admin that ``Poll``
objects have an admin interface. To do this, create a file called
-``admin.py`` in your ``polls`` directory, and edit it to look like this::
+:file:`admin.py` in your ``polls`` directory, and edit it to look like this::
from django.contrib import admin
from polls.models import Poll
@@ -375,7 +375,7 @@
underscores replaced with spaces), and that each line contains the string
representation of the output.
-You can improve that by giving that method (in ``models.py``) a few
+You can improve that by giving that method (in :file:`polls/models.py`) a few
attributes, as follows::
class Poll(models.Model):
@@ -386,8 +386,8 @@
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
-Edit your admin.py file again and add an improvement to the Poll change list page: Filters. Add the
-following line to ``PollAdmin``::
+Edit your :file:`polls/admin.py` file again and add an improvement to the Poll
+change list page: Filters. Add the following line to ``PollAdmin``::
list_filter = ['pub_date']
@@ -445,8 +445,8 @@
whatever user your server runs.) However, keeping your templates within the
project is a good convention to follow.
-By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to
-tell Django where our templates live::
+Open your settings file (:file:`mysite/settings.py`, remember) and add a
+:setting:`TEMPLATE_DIRS` setting::
TEMPLATE_DIRS = (
'/path/to/mysite/templates', # Change this to your own directory.
diff --git a/lib/django-1.5/docs/intro/tutorial03.txt b/lib/django-1.5/docs/intro/tutorial03.txt
index daab8b7..bdad8f4 100644
--- a/lib/django-1.5/docs/intro/tutorial03.txt
+++ b/lib/django-1.5/docs/intro/tutorial03.txt
@@ -107,7 +107,7 @@
url(r'^admin/', include(admin.site.urls)),
)
-You have now wired an `index` view into the URLconf. Go to
+You have now wired an ``index`` view into the URLconf. Go to
http://localhost:8000/polls/ in your browser, and you should see the text
"*Hello, world. You're at the poll index.*", which you defined in the
``index`` view.
@@ -119,7 +119,7 @@
:func:`~django.conf.urls.url` argument: regex
---------------------------------------------
-The term `regex` is a commonly used short form meaning `regular expression`,
+The term "regex" is a commonly used short form meaning "regular expression",
which is a syntax for matching patterns in strings, or in this case, url
patterns. Django starts at the first regular expression and makes its way down
the list, comparing the requested URL against each regular expression until it
@@ -182,7 +182,7 @@
def vote(request, poll_id):
return HttpResponse("You're voting on poll %s." % poll_id)
-Wire these news views into the ``polls.urls`` module by adding the following
+Wire these new views into the ``polls.urls`` module by adding the following
:func:`~django.conf.urls.url` calls::
from django.conf.urls import patterns, url
@@ -336,17 +336,17 @@
<p>No polls are available.</p>
{% endif %}
-Now let's use that html template in our index view::
+Now let's update our ``index`` view in ``polls/views.py`` to use the template::
from django.http import HttpResponse
- from django.template import Context, loader
+ from django.template import RequestContext, loader
from polls.models import Poll
def index(request):
latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
- context = Context({
+ context = RequestContext(request, {
'latest_poll_list': latest_poll_list,
})
return HttpResponse(template.render(context))
@@ -377,7 +377,7 @@
return render(request, 'polls/index.html', context)
Note that once we've done this in all these views, we no longer need to import
-:mod:`~django.template.loader`, :class:`~django.template.Context` and
+:mod:`~django.template.loader`, :class:`~django.template.RequestContext` and
:class:`~django.http.HttpResponse` (you'll want to keep ``HttpResponse`` if you
still have the stub methods for ``detail``, ``results``, and ``vote``).
@@ -468,6 +468,13 @@
settings module). If you do create the template, add at least some dummy
content like "Page not found".
+.. warning::
+
+ If :setting:`DEBUG` is set to ``False``, all responses will be
+ "Bad Request (400)" unless you specify the proper :setting:`ALLOWED_HOSTS`
+ as well (something like ``['localhost', '127.0.0.1']`` for
+ local development).
+
A couple more things to note about 404 views:
* If :setting:`DEBUG` is set to ``True`` (in your settings module) then your
diff --git a/lib/django-1.5/docs/intro/tutorial04.txt b/lib/django-1.5/docs/intro/tutorial04.txt
index 87d8e58..9f54243 100644
--- a/lib/django-1.5/docs/intro/tutorial04.txt
+++ b/lib/django-1.5/docs/intro/tutorial04.txt
@@ -185,7 +185,7 @@
2. Delete some of the old, unneeded views.
-3. Fix up URL handling for the new views.
+3. Introduce new views based on Django's generic views.
Read on for details.
@@ -205,32 +205,51 @@
First, open the ``polls/urls.py`` URLconf and change it like so::
from django.conf.urls import patterns, url
- from django.views.generic import DetailView, ListView
- from polls.models import Poll
+
+ from polls import views
urlpatterns = patterns('',
- url(r'^$',
- ListView.as_view(
- queryset=Poll.objects.order_by('-pub_date')[:5],
- context_object_name='latest_poll_list',
- template_name='polls/index.html'),
- name='index'),
- url(r'^(?P<pk>\d+)/$',
- DetailView.as_view(
- model=Poll,
- template_name='polls/detail.html'),
- name='detail'),
- url(r'^(?P<pk>\d+)/results/$',
- DetailView.as_view(
- model=Poll,
- template_name='polls/results.html'),
- name='results'),
- url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
+ url(r'^$', views.IndexView.as_view(), name='index'),
+ url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
+ url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'),
+ url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
)
Amend views
-----------
+Next, we're going to remove our old ``index``, ``detail``, and ``results``
+views and use Django's generic views instead. To do so, open the
+``polls/views.py`` file and change it like so::
+
+ from django.shortcuts import get_object_or_404, render
+ from django.http import HttpResponseRedirect
+ from django.core.urlresolvers import reverse
+ from django.views import generic
+
+ from polls.models import Choice, Poll
+
+ class IndexView(generic.ListView):
+ template_name = 'polls/index.html'
+ context_object_name = 'latest_poll_list'
+
+ def get_queryset(self):
+ """Return the last five published polls."""
+ return Poll.objects.order_by('-pub_date')[:5]
+
+
+ class DetailView(generic.DetailView):
+ model = Poll
+ template_name = 'polls/detail.html'
+
+
+ class ResultsView(generic.DetailView):
+ model = Poll
+ template_name = 'polls/results.html'
+
+ def vote(request, poll_id):
+ ....
+
We're using two generic views here:
:class:`~django.views.generic.list.ListView` and
:class:`~django.views.generic.detail.DetailView`. Respectively, those
@@ -238,7 +257,7 @@
"display a detail page for a particular type of object."
* Each generic view needs to know what model it will be acting
- upon. This is provided using the ``model`` parameter.
+ upon. This is provided using the ``model`` attribute.
* The :class:`~django.views.generic.detail.DetailView` generic view
expects the primary key value captured from the URL to be called
@@ -248,7 +267,7 @@
By default, the :class:`~django.views.generic.detail.DetailView` generic
view uses a template called ``<app name>/<model name>_detail.html``.
In our case, it'll use the template ``"polls/poll_detail.html"``. The
-``template_name`` argument is used to tell Django to use a specific
+``template_name`` attribute is used to tell Django to use a specific
template name instead of the autogenerated default template name. We
also specify the ``template_name`` for the ``results`` list view --
this ensures that the results view and the detail view have a
@@ -268,16 +287,11 @@
is able to determine an appropriate name for the context variable.
However, for ListView, the automatically generated context variable is
``poll_list``. To override this we provide the ``context_object_name``
-option, specifying that we want to use ``latest_poll_list`` instead.
+attribute, specifying that we want to use ``latest_poll_list`` instead.
As an alternative approach, you could change your templates to match
the new default context variables -- but it's a lot easier to just
tell Django to use the variable you want.
-You can now delete the ``index()``, ``detail()`` and ``results()`` views from
-``polls/views.py``. We don't need them anymore -- they have been replaced by
-generic views. You can also delete the import for ``HttpResponse``, which is no
-longer required.
-
Run the server, and use your new polling app based on generic views.
For full details on generic views, see the :doc:`generic views documentation
diff --git a/lib/django-1.5/docs/intro/tutorial05.txt b/lib/django-1.5/docs/intro/tutorial05.txt
index d1f9517..60a8996 100644
--- a/lib/django-1.5/docs/intro/tutorial05.txt
+++ b/lib/django-1.5/docs/intro/tutorial05.txt
@@ -121,7 +121,7 @@
So let's do that right away.
-.. _test-driven development: http://en.wikipedia.org/wiki/Test-driven_development/
+.. _test-driven development: http://en.wikipedia.org/wiki/Test-driven_development
Writing our first test
======================
@@ -327,6 +327,13 @@
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
+:meth:`~django.test.utils.setup_test_environment` installs a template renderer
+which will allow us to examine some additional attributes on responses such as
+``response.context`` that otherwise wouldn't be available. Note that this
+method *does not* setup a test database, so the following will be run against
+the existing database and the output may differ slightly depending on what
+polls you already created.
+
Next we need to import the test client class (later in ``tests.py`` we will use
the :class:`django.test.TestCase` class, which comes with its own client, so
this won't be required)::
@@ -371,45 +378,40 @@
The list of polls shows polls that aren't published yet (i.e. those that have a
``pub_date`` in the future). Let's fix that.
-In :doc:`Tutorial 4 </intro/tutorial04>` we deleted the view functions from
-``views.py`` in favor of a :class:`~django.views.generic.list.ListView` in
-``urls.py``::
+In :doc:`Tutorial 4 </intro/tutorial04>` we introduced a class-based view,
+based on :class:`~django.views.generic.list.ListView`::
- url(r'^$',
- ListView.as_view(
- queryset=Poll.objects.order_by('-pub_date')[:5],
- context_object_name='latest_poll_list',
- template_name='polls/index.html'),
- name='index'),
+ class IndexView(generic.ListView):
+ template_name = 'polls/index.html'
+ context_object_name = 'latest_poll_list'
+
+ def get_queryset(self):
+ """Return the last five published polls."""
+ return Poll.objects.order_by('-pub_date')[:5]
``response.context_data['latest_poll_list']`` extracts the data this view
places into the context.
-We need to amend the line that gives us the ``queryset``::
-
- queryset=Poll.objects.order_by('-pub_date')[:5],
-
-Let's change the queryset so that it also checks the date by comparing it with
-``timezone.now()``. First we need to add an import::
+We need to amend the ``get_queryset`` method and change it so that it also
+checks the date by comparing it with ``timezone.now()``. First we need to add
+an import::
from django.utils import timezone
-and then we must amend the existing ``url`` function to::
+and then we must amend the ``get_queryset`` method like so::
- url(r'^$',
- ListView.as_view(
- queryset=Poll.objects.filter(pub_date__lte=timezone.now) \
- .order_by('-pub_date')[:5],
- context_object_name='latest_poll_list',
- template_name='polls/index.html'),
- name='index'),
+ def get_queryset(self):
+ """
+ Return the last five published polls (not including those set to be
+ published in the future).
+ """
+ return Poll.objects.filter(
+ pub_date__lte=timezone.now()
+ ).order_by('-pub_date')[:5]
-``Poll.objects.filter(pub_date__lte=timezone.now)`` returns a queryset
+``Poll.objects.filter(pub_date__lte=timezone.now())`` returns a queryset
containing Polls whose ``pub_date`` is less than or equal to - that is, earlier
-than or equal to - ``timezone.now``. Notice that we use a callable queryset
-argument, ``timezone.now``, which will be evaluated at request time. If we had
-included the parentheses, ``timezone.now()`` would be evaluated just once when
-the web server is started.
+than or equal to - ``timezone.now``.
Testing our new view
--------------------
@@ -520,20 +522,18 @@
What we have works well; however, even though future polls don't appear in the
*index*, users can still reach them if they know or guess the right URL. So we
-need similar constraints in the ``DetailViews``, by adding::
+need to add a similar constraint to ``DetailView``::
- queryset=Poll.objects.filter(pub_date__lte=timezone.now)
-to them - for example::
+ class DetailView(generic.DetailView):
+ ...
+ def get_queryset(self):
+ """
+ Excludes any polls that aren't published yet.
+ """
+ return Poll.objects.filter(pub_date__lte=timezone.now())
- url(r'^(?P<pk>\d+)/$',
- DetailView.as_view(
- queryset=Poll.objects.filter(pub_date__lte=timezone.now),
- model=Poll,
- template_name='polls/detail.html'),
- name='detail'),
-
-and of course, we will add some tests, to check that a ``Poll`` whose
+And of course, we will add some tests, to check that a ``Poll`` whose
``pub_date`` is in the past can be displayed, and that one with a ``pub_date``
in the future is not::
@@ -559,9 +559,9 @@
Ideas for more tests
--------------------
-We ought to add similar ``queryset`` arguments to the other ``DetailView``
-URLs, and create a new test class for each view. They'll be very similar to
-what we have just created; in fact there will be a lot of repetition.
+We ought to add a similar ``get_queryset`` method to ``ResultsView`` and
+create a new test class for that view. It'll be very similar to what we have
+just created; in fact there will be a lot of repetition.
We could also improve our application in other ways, adding tests along the
way. For example, it's silly that ``Polls`` can be published on the site that
@@ -641,10 +641,9 @@
What's next?
============
-The beginner tutorial ends here for the time being. In the meantime, you might
-want to check out some pointers on :doc:`where to go from here
-</intro/whatsnext>`.
+For full details on testing, see :doc:`Testing in Django
+</topics/testing/index>`.
-If you are familiar with Python packaging and interested in learning how to
-turn polls into a "reusable app", check out :doc:`Advanced tutorial: How to
-write reusable apps</intro/reusable-apps>`.
+When you're comfortable with testing Django views, read
+:doc:`part 6 of this tutorial</intro/tutorial06>` to learn about
+static files management.
diff --git a/lib/django-1.5/docs/intro/tutorial06.txt b/lib/django-1.5/docs/intro/tutorial06.txt
new file mode 100644
index 0000000..d5db3ae2
--- /dev/null
+++ b/lib/django-1.5/docs/intro/tutorial06.txt
@@ -0,0 +1,125 @@
+=====================================
+Writing your first Django app, part 6
+=====================================
+
+This tutorial begins where :doc:`Tutorial 5 </intro/tutorial05>` left off.
+We've built a tested Web-poll application, and we'll now add a stylesheet and
+an image.
+
+Aside from the HTML generated by the server, web applications generally need
+to serve additional files — such as images, JavaScript, or CSS — necessary to
+render the complete web page. In Django, we refer to these files as "static
+files".
+
+For small projects, this isn't a big deal, because you can just keep the
+static files somewhere your web server can find it. However, in bigger
+projects -- especially those comprised of multiple apps -- dealing with the
+multiple sets of static files provided by each application starts to get
+tricky.
+
+That's what ``django.contrib.staticfiles`` is for: it collects static files
+from each of your applications (and any other places you specify) into a
+single location that can easily be served in production.
+
+Customize your *app's* look and feel
+====================================
+
+First, create a directory called ``static`` in your ``polls`` directory. Django
+will look for static files there, similarly to how Django finds templates
+inside ``polls/templates/``.
+
+Django's :setting:`STATICFILES_FINDERS` setting contains a list
+of finders that know how to discover static files from various
+sources. One of the defaults is ``AppDirectoriesFinder`` which
+looks for a "static" subdirectory in each of the
+:setting:`INSTALLED_APPS`, like the one in ``polls`` we just created. The admin
+site uses the same directory structure for its static files.
+
+Within the ``static`` directory you have just created, create another directory
+called ``polls`` and within that create a file called ``style.css``. In other
+words, your stylesheet should be at ``polls/static/polls/style.css``. Because
+of how the ``AppDirectoriesFinder`` staticfile finder works, you can refer to
+this static file in Django simply as ``polls/style.css``, similar to how you
+reference the path for templates.
+
+.. admonition:: Static file namespacing
+
+ Just like templates, we *might* be able to get away with putting our static
+ files directly in ``polls/static`` (rather than creating another ``polls``
+ subdirectory), but it would actually be a bad idea. Django will choose the
+ first static file it finds whose name matches, and if you had a static file
+ with the same name in a *different* application, Django would be unable to
+ distinguish between them. We need to be able to point Django at the right
+ one, and the easiest way to ensure this is by *namespacing* them. That is,
+ by putting those static files inside *another* directory named for the
+ application itself.
+
+Put the following code in that stylesheet (``polls/static/polls/style.css``):
+
+.. code-block:: css
+
+ li a {
+ color: green;
+ }
+
+Next, add the following at the top of ``polls/templates/polls/index.html``:
+
+.. code-block:: html+django
+
+ {% load staticfiles %}
+
+ <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />
+
+``{% load staticfiles %}`` loads the :ttag:`{% static %} <staticfiles-static>`
+template tag from the ``staticfiles`` template library. The ``{% static %}``
+template tag generates the absolute URL of the static file.
+
+That's all you need to do for development. Reload
+``http://localhost:8000/polls/`` and you should see that the poll links are
+green (Django style!) which means that your stylesheet was properly loaded.
+
+Adding a background-image
+=========================
+
+Next, we'll create a subdirectory for images. Create an ``images`` subdirectory
+in the ``polls/static/polls/`` directory. Inside this directory, put an image
+called ``background.gif``. In other words, put your image in
+``polls/static/polls/images/background.gif``.
+
+Then, add to your stylesheet (``polls/static/polls/style.css``):
+
+.. code-block:: css
+
+ body {
+ background: white url("images/background.gif") no-repeat right bottom;
+ }
+
+Reload ``http://localhost:8000/polls/`` and you should see the background
+loaded in the bottom right of the screen.
+
+.. warning::
+
+ Of course the ``{% static %}`` template tag is not available for use in
+ static files like your stylesheet which aren't generated by Django. You
+ should always use **relative paths** to link your static files between each
+ other, because then you can change :setting:`STATIC_URL` (used by the
+ :ttag:`static` template tag to generate its URLs) without having to modify
+ a bunch of paths in your static files as well.
+
+These are the **basics**. For more details on settings and other bits included
+with the framework see
+:doc:`the static files howto </howto/static-files/index>` and
+:doc:`the staticfiles reference </ref/contrib/staticfiles>`. :doc:`Deploying
+static files </howto/static-files/deployment>` discusses how to use static
+files on a real server.
+
+What's next?
+============
+
+The beginner tutorial ends here for the time being. In the meantime, you might
+want to check out some pointers on :doc:`where to go from here
+</intro/whatsnext>`.
+
+If you are familiar with Python packaging and interested in learning how to
+turn polls into a "reusable app", check out :doc:`Advanced tutorial: How to
+write reusable apps</intro/reusable-apps>`.
diff --git a/lib/django-1.5/docs/ref/class-based-views/base.txt b/lib/django-1.5/docs/ref/class-based-views/base.txt
index 2073458..93cef1c 100644
--- a/lib/django-1.5/docs/ref/class-based-views/base.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/base.txt
@@ -9,7 +9,7 @@
views.
Many of Django's built-in class-based views inherit from other class-based
-views or various mixins. Because this inheritence chain is very important, the
+views or various mixins. Because this inheritance chain is very important, the
ancestor classes are documented under the section title of **Ancestors (MRO)**.
MRO is an acronym for Method Resolution Order.
@@ -79,10 +79,6 @@
you can override the ``head()`` method. See
:ref:`supporting-other-http-methods` for an example.
- The default implementation also sets ``request``, ``args`` and
- ``kwargs`` as instance variables, so any method on the view can know
- the full details of the request that was made to invoke the view.
-
.. method:: http_method_not_allowed(request, *args, **kwargs)
If the view was called with a HTTP method it doesn't support, this
@@ -113,7 +109,6 @@
This view inherits methods and attributes from the following views:
- * :class:`django.views.generic.base.TemplateView`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.base.View`
@@ -207,7 +202,7 @@
urlpatterns = patterns('',
- url(r'r^(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
+ url(r'^(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
)
diff --git a/lib/django-1.5/docs/ref/class-based-views/flattened-index.txt b/lib/django-1.5/docs/ref/class-based-views/flattened-index.txt
index bc2ac55..db19ec8 100644
--- a/lib/django-1.5/docs/ref/class-based-views/flattened-index.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/flattened-index.txt
@@ -34,7 +34,7 @@
* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.base.View.http_method_names`
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
**Methods**
@@ -86,7 +86,7 @@
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
@@ -121,7 +121,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
@@ -150,7 +150,7 @@
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
@@ -164,11 +164,9 @@
* :meth:`~django.views.generic.edit.FormMixin.get_context_data`
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
-* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
-* ``post()``
-* ``put()``
-* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
+* :meth:`~django.views.generic.edit.ProcessFormView.post`
+* :meth:`~django.views.generic.edit.ProcessFormView.put`
CreateView
~~~~~~~~~~
@@ -184,7 +182,7 @@
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
@@ -223,7 +221,7 @@
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
@@ -260,7 +258,7 @@
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.DeletionMixin.success_url` [:meth:`~django.views.generic.edit.DeletionMixin.get_success_url`]
@@ -300,7 +298,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
@@ -336,7 +334,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -375,7 +373,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -414,7 +412,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.WeekMixin.week` [:meth:`~django.views.generic.dates.WeekMixin.get_week`]
@@ -457,7 +455,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -502,7 +500,7 @@
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -545,7 +543,7 @@
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
diff --git a/lib/django-1.5/docs/ref/class-based-views/generic-date-based.txt b/lib/django-1.5/docs/ref/class-based-views/generic-date-based.txt
index 4144c38..6bff045 100644
--- a/lib/django-1.5/docs/ref/class-based-views/generic-date-based.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/generic-date-based.txt
@@ -33,7 +33,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.ArchiveIndexView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseArchiveIndexView`
@@ -100,7 +99,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.YearArchiveView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseYearArchiveView`
@@ -216,7 +214,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.MonthArchiveView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseMonthArchiveView`
@@ -317,7 +314,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.WeekArchiveView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseWeekArchiveView`
@@ -422,7 +418,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.DayArchiveView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseDayArchiveView`
@@ -526,7 +521,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.TodayArchiveView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseTodayArchiveView`
@@ -585,7 +579,6 @@
**Ancestors (MRO)**
- * :class:`django.views.generic.dates.DateDetailView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.dates.BaseDateDetailView`
diff --git a/lib/django-1.5/docs/ref/class-based-views/generic-display.txt b/lib/django-1.5/docs/ref/class-based-views/generic-display.txt
index b827c00..c133134 100644
--- a/lib/django-1.5/docs/ref/class-based-views/generic-display.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/generic-display.txt
@@ -77,7 +77,6 @@
This view inherits methods and attributes from the following views:
- * :class:`django.views.generic.list.ListView`
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* :class:`django.views.generic.list.BaseListView`
diff --git a/lib/django-1.5/docs/ref/class-based-views/generic-editing.txt b/lib/django-1.5/docs/ref/class-based-views/generic-editing.txt
index f367928..27171e6 100644
--- a/lib/django-1.5/docs/ref/class-based-views/generic-editing.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/generic-editing.txt
@@ -12,7 +12,7 @@
.. note::
- Some of the examples on this page assume that an ``Article`` model has been
+ Some of the examples on this page assume that an ``Author`` model has been
defined as follows in ``myapp/models.py``::
from django.core.urlresolvers import reverse
@@ -36,7 +36,6 @@
This view inherits methods and attributes from the following views:
- * :class:`django.views.generic.edit.FormView`
* :class:`django.views.generic.base.TemplateResponseMixin`
* ``django.views.generic.edit.BaseFormView``
* :class:`django.views.generic.edit.FormMixin`
@@ -83,7 +82,6 @@
This view inherits methods and attributes from the following views:
- * :class:`django.views.generic.edit.CreateView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* ``django.views.generic.edit.BaseCreateView``
@@ -125,7 +123,6 @@
This view inherits methods and attributes from the following views:
- * :class:`django.views.generic.edit.UpdateView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* ``django.views.generic.edit.BaseUpdateView``
@@ -167,7 +164,6 @@
This view inherits methods and attributes from the following views:
- * :class:`django.views.generic.edit.DeleteView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
* ``django.views.generic.edit.BaseDeleteView``
diff --git a/lib/django-1.5/docs/ref/class-based-views/index.txt b/lib/django-1.5/docs/ref/class-based-views/index.txt
index a027953..821edc0 100644
--- a/lib/django-1.5/docs/ref/class-based-views/index.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/index.txt
@@ -32,7 +32,7 @@
.. admonition:: Thread safety with view arguments
Arguments passed to a view are shared between every instance of a view.
- This means that you shoudn't use a list, dictionary, or any other
+ This means that you shouldn't use a list, dictionary, or any other
mutable object as an argument to a view. If you do and the shared object
is modified, the actions of one user visiting your view could have an
effect on subsequent users visiting the same view.
diff --git a/lib/django-1.5/docs/ref/class-based-views/mixins-date-based.txt b/lib/django-1.5/docs/ref/class-based-views/mixins-date-based.txt
index 7ff201e..75f2a77 100644
--- a/lib/django-1.5/docs/ref/class-based-views/mixins-date-based.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/mixins-date-based.txt
@@ -35,8 +35,8 @@
Tries the following sources, in order:
* The value of the :attr:`YearMixin.year` attribute.
- * The value of the `year` argument captured in the URL pattern.
- * The value of the `year` GET query argument.
+ * The value of the ``year`` argument captured in the URL pattern.
+ * The value of the ``year`` ``GET`` query argument.
Raises a 404 if no valid year specification can be found.
@@ -87,8 +87,8 @@
Tries the following sources, in order:
* The value of the :attr:`MonthMixin.month` attribute.
- * The value of the `month` argument captured in the URL pattern.
- * The value of the `month` GET query argument.
+ * The value of the ``month`` argument captured in the URL pattern.
+ * The value of the ``month`` ``GET`` query argument.
Raises a 404 if no valid month specification can be found.
@@ -139,8 +139,8 @@
Tries the following sources, in order:
* The value of the :attr:`DayMixin.day` attribute.
- * The value of the `day` argument captured in the URL pattern.
- * The value of the `day` GET query argument.
+ * The value of the ``day`` argument captured in the URL pattern.
+ * The value of the ``day`` ``GET`` query argument.
Raises a 404 if no valid day specification can be found.
@@ -192,8 +192,8 @@
Tries the following sources, in order:
* The value of the :attr:`WeekMixin.week` attribute.
- * The value of the `week` argument captured in the URL pattern
- * The value of the `week` GET query argument.
+ * The value of the ``week`` argument captured in the URL pattern
+ * The value of the ``week`` ``GET`` query argument.
Raises a 404 if no valid week specification can be found.
diff --git a/lib/django-1.5/docs/ref/class-based-views/mixins-editing.txt b/lib/django-1.5/docs/ref/class-based-views/mixins-editing.txt
index b70e4de..975e8fe 100644
--- a/lib/django-1.5/docs/ref/class-based-views/mixins-editing.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/mixins-editing.txt
@@ -193,7 +193,10 @@
Constructs a form, checks the form for validity, and handles it
accordingly.
- The PUT action is also handled, as an analog of POST.
+ .. method:: put(*args, **kwargs)
+
+ The ``PUT`` action is also handled and just passes all parameters
+ through to :meth:`post`.
.. class:: django.views.generic.edit.DeletionMixin
diff --git a/lib/django-1.5/docs/ref/class-based-views/mixins-single-object.txt b/lib/django-1.5/docs/ref/class-based-views/mixins-single-object.txt
index 3746871..1b75acf 100644
--- a/lib/django-1.5/docs/ref/class-based-views/mixins-single-object.txt
+++ b/lib/django-1.5/docs/ref/class-based-views/mixins-single-object.txt
@@ -63,7 +63,7 @@
this view will display. By default, :meth:`get_queryset` returns the
value of the :attr:`queryset` attribute if it is set, otherwise
it constructs a :class:`~django.db.models.query.QuerySet` by calling
- the `all()` method on the :attr:`model` attribute's default manager.
+ the ``all()`` method on the :attr:`model` attribute's default manager.
.. method:: get_context_object_name(obj)
diff --git a/lib/django-1.5/docs/ref/clickjacking.txt b/lib/django-1.5/docs/ref/clickjacking.txt
index d78f8d3..9648190 100644
--- a/lib/django-1.5/docs/ref/clickjacking.txt
+++ b/lib/django-1.5/docs/ref/clickjacking.txt
@@ -33,10 +33,10 @@
Modern browsers honor the `X-Frame-Options`_ HTTP header that indicates whether
or not a resource is allowed to load within a frame or iframe. If the response
-contains the header with a value of SAMEORIGIN then the browser will only load
-the resource in a frame if the request originated from the same site. If the
-header is set to DENY then the browser will block the resource from loading in a
-frame no matter which site made the request.
+contains the header with a value of ``SAMEORIGIN`` then the browser will only
+load the resource in a frame if the request originated from the same site. If
+the header is set to ``DENY`` then the browser will block the resource from
+loading in a frame no matter which site made the request.
.. _X-Frame-Options: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header
@@ -64,15 +64,16 @@
...
)
-By default, the middleware will set the X-Frame-Options header to SAMEORIGIN for
-every outgoing ``HttpResponse``. If you want DENY instead, set the
-:setting:`X_FRAME_OPTIONS` setting::
+
+By default, the middleware will set the ``X-Frame-Options`` header to
+``SAMEORIGIN`` for every outgoing ``HttpResponse``. If you want ``DENY``
+instead, set the :setting:`X_FRAME_OPTIONS` setting::
X_FRAME_OPTIONS = 'DENY'
When using the middleware there may be some views where you do **not** want the
-X-Frame-Options header set. For those cases, you can use a view decorator that
-tells the middleware not to set the header::
+``X-Frame-Options`` header set. For those cases, you can use a view decorator
+that tells the middleware not to set the header::
from django.http import HttpResponse
from django.views.decorators.clickjacking import xframe_options_exempt
@@ -85,7 +86,7 @@
Setting X-Frame-Options per view
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To set the X-Frame-Options header on a per view basis, Django provides these
+To set the ``X-Frame-Options`` header on a per view basis, Django provides these
decorators::
from django.http import HttpResponse
@@ -106,8 +107,8 @@
Limitations
===========
-The `X-Frame-Options` header will only protect against clickjacking in a modern
-browser. Older browsers will quietly ignore the header and need `other
+The ``X-Frame-Options`` header will only protect against clickjacking in a
+modern browser. Older browsers will quietly ignore the header and need `other
clickjacking prevention techniques`_.
Browsers that support X-Frame-Options
@@ -122,7 +123,7 @@
See also
~~~~~~~~
-A `complete list`_ of browsers supporting X-Frame-Options.
+A `complete list`_ of browsers supporting ``X-Frame-Options``.
.. _complete list: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header#Browser_compatibility
.. _other clickjacking prevention techniques: http://en.wikipedia.org/wiki/Clickjacking#Prevention
diff --git a/lib/django-1.5/docs/ref/contrib/admin/actions.txt b/lib/django-1.5/docs/ref/contrib/admin/actions.txt
index 0a302ec..c79f978 100644
--- a/lib/django-1.5/docs/ref/contrib/admin/actions.txt
+++ b/lib/django-1.5/docs/ref/contrib/admin/actions.txt
@@ -175,7 +175,7 @@
make_published.short_description = "Mark selected stories as published"
Notice first that we've moved ``make_published`` into a method and renamed the
-`modeladmin` parameter to `self`, and second that we've now put the string
+``modeladmin`` parameter to ``self``, and second that we've now put the string
``'make_published'`` in ``actions`` instead of a direct function reference. This
tells the :class:`ModelAdmin` to look up the action as a method.
diff --git a/lib/django-1.5/docs/ref/contrib/admin/admindocs.txt b/lib/django-1.5/docs/ref/contrib/admin/admindocs.txt
index b3e26ec..394d078 100644
--- a/lib/django-1.5/docs/ref/contrib/admin/admindocs.txt
+++ b/lib/django-1.5/docs/ref/contrib/admin/admindocs.txt
@@ -57,9 +57,10 @@
===============
The **models** section of the ``admindocs`` page describes each model in the
-system along with all the fields and methods available on it. Relationships to
-other models appear as hyperlinks. Descriptions are pulled from ``help_text``
-attributes on fields or from docstrings on model methods.
+system along with all the fields and methods (without any arguments) available
+on it. While model properties don't have any arguments, they are not listed.
+Relationships to other models appear as hyperlinks. Descriptions are pulled
+from ``help_text`` attributes on fields or from docstrings on model methods.
A model with useful documentation might look like this::
diff --git a/lib/django-1.5/docs/ref/contrib/admin/index.txt b/lib/django-1.5/docs/ref/contrib/admin/index.txt
index 0891645..26ee086 100644
--- a/lib/django-1.5/docs/ref/contrib/admin/index.txt
+++ b/lib/django-1.5/docs/ref/contrib/admin/index.txt
@@ -1585,9 +1585,9 @@
.. attribute:: InlineModelAdmin.formset
- This defaults to ``BaseInlineFormSet``. Using your own formset can give you
- many possibilities of customization. Inlines are built around
- :ref:`model formsets <model-formsets>`.
+ This defaults to :class:`~django.forms.models.BaseInlineFormSet`. Using
+ your own formset can give you many possibilities of customization. Inlines
+ are built around :ref:`model formsets <model-formsets>`.
.. attribute:: InlineModelAdmin.form
@@ -1653,8 +1653,9 @@
.. method:: InlineModelAdmin.get_formset(self, request, obj=None, **kwargs)
- Returns a ``BaseInlineFormSet`` class for use in admin add/change views.
- See the example for :class:`ModelAdmin.get_formsets`.
+ Returns a :class:`~django.forms.models.BaseInlineFormSet` class for use in
+ admin add/change views. See the example for
+ :class:`ModelAdmin.get_formsets`.
Working with a model with two or more foreign keys to the same parent model
---------------------------------------------------------------------------
@@ -1921,7 +1922,7 @@
.. note::
- Some of the admin templates, such as ``change_list_request.html`` are used
+ Some of the admin templates, such as ``change_list_results.html`` are used
to render custom inclusion tags. These may be overridden, but in such cases
you are probably better off creating your own version of the tag in
question and giving it a different name. That way you can use it
@@ -1969,6 +1970,10 @@
Path to a custom template that will be used by the admin site main index
view.
+.. attribute:: AdminSite.app_index_template
+
+ Path to a custom template that will be used by the admin site app index view.
+
.. attribute:: AdminSite.login_template
Path to a custom template that will be used by the admin site login view.
diff --git a/lib/django-1.5/docs/ref/contrib/auth.txt b/lib/django-1.5/docs/ref/contrib/auth.txt
index b12c99f..838784c 100644
--- a/lib/django-1.5/docs/ref/contrib/auth.txt
+++ b/lib/django-1.5/docs/ref/contrib/auth.txt
@@ -111,10 +111,16 @@
.. method:: is_authenticated()
- Always returns ``True``. This is a way to tell if the user has been
- authenticated. This does not imply any permissions, and doesn't check
- if the user is active - it only indicates that the user has provided a
- valid username and password.
+ Always returns ``True`` (as opposed to
+ ``AnonymousUser.is_authenticated()`` which always returns ``False``).
+ This is a way to tell if the user has been authenticated. This does not
+ imply any permissions, and doesn't check if the user is active or has
+ a valid session. Even though normally you will call this method on
+ ``request.user`` to find out whether it has been populated by the
+ :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
+ (representing the currently logged-in user), you should know this method
+ returns ``True`` for any :class:`~django.contrib.auth.models.User`
+ instance.
.. method:: get_full_name()
@@ -346,8 +352,8 @@
.. module:: django.contrib.auth.signals
-The auth framework uses two :doc:`signals </topics/signals>` that can be used
-for notification when a user logs in or out.
+The auth framework uses the following :doc:`signals </topics/signals>` that
+can be used for notification when a user logs in or out.
.. function:: user_logged_in
diff --git a/lib/django-1.5/docs/ref/contrib/contenttypes.txt b/lib/django-1.5/docs/ref/contrib/contenttypes.txt
index fb85653..e83af76 100644
--- a/lib/django-1.5/docs/ref/contrib/contenttypes.txt
+++ b/lib/django-1.5/docs/ref/contrib/contenttypes.txt
@@ -359,6 +359,9 @@
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
+Likewise, :class:`~django.contrib.contenttypes.generic.GenericForeignKey`\s
+does not appear in :class:`~django.forms.ModelForm`\s.
+
Reverse generic relations
-------------------------
@@ -497,8 +500,8 @@
Returns a ``GenericInlineFormSet`` using
:func:`~django.forms.models.modelformset_factory`.
- You must provide ``ct_field`` and ``object_id`` if they different from the
- defaults, ``content_type`` and ``object_id`` respectively. Other parameters
- are similar to those documented in
+ You must provide ``ct_field`` and ``fk_field`` if they are different from
+ the defaults, ``content_type`` and ``object_id`` respectively. Other
+ parameters are similar to those documented in
:func:`~django.forms.models.modelformset_factory` and
:func:`~django.forms.models.inlineformset_factory`.
diff --git a/lib/django-1.5/docs/ref/contrib/csrf.txt b/lib/django-1.5/docs/ref/contrib/csrf.txt
index 32d8a70..22e7547 100644
--- a/lib/django-1.5/docs/ref/contrib/csrf.txt
+++ b/lib/django-1.5/docs/ref/contrib/csrf.txt
@@ -120,7 +120,7 @@
var csrftoken = getCookie('csrftoken');
The above code could be simplified by using the `jQuery cookie plugin
-<http://plugins.jquery.com/project/Cookie>`_ to replace ``getCookie``:
+<http://plugins.jquery.com/cookie/>`_ to replace ``getCookie``:
.. code-block:: javascript
@@ -181,7 +181,7 @@
correctly on that version. Make sure you are running at least jQuery 1.5.1.
You can use `settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in
-jQuery 1.5 and newer in order to replace the `sameOrigin` logic above:
+jQuery 1.5 and newer in order to replace the ``sameOrigin`` logic above:
.. code-block:: javascript
diff --git a/lib/django-1.5/docs/ref/contrib/flatpages.txt b/lib/django-1.5/docs/ref/contrib/flatpages.txt
index 2b54ea9..43cd1d2 100644
--- a/lib/django-1.5/docs/ref/contrib/flatpages.txt
+++ b/lib/django-1.5/docs/ref/contrib/flatpages.txt
@@ -82,9 +82,15 @@
# Your other patterns here
urlpatterns += patterns('django.contrib.flatpages.views',
- (r'^(?P<url>.*)$', 'flatpage'),
+ (r'^(?P<url>.*/)$', 'flatpage'),
)
+.. warning::
+
+ If you set :setting:`APPEND_SLASH` to ``False``, you must remove the slash
+ in the catchall pattern or flatpages without a trailing slash will not be
+ matched.
+
Another common setup is to use flat pages for a limited set of known pages and
to hard code the urls, so you can reference them with the :ttag:`url` template
tag::
diff --git a/lib/django-1.5/docs/ref/contrib/formtools/form-wizard.txt b/lib/django-1.5/docs/ref/contrib/formtools/form-wizard.txt
index bcffb77..18586ed 100644
--- a/lib/django-1.5/docs/ref/contrib/formtools/form-wizard.txt
+++ b/lib/django-1.5/docs/ref/contrib/formtools/form-wizard.txt
@@ -166,7 +166,7 @@
it:
* ``form`` -- The :class:`~django.forms.Form` or
- :class:`~django.forms.formset.BaseFormSet` instance for the current step
+ :class:`~django.forms.formsets.BaseFormSet` instance for the current step
(either empty or with errors).
* ``steps`` -- A helper object to access the various steps related data:
@@ -405,8 +405,10 @@
.. method:: WizardView.get_form(step=None, data=None, files=None)
This method constructs the form for a given ``step``. If no ``step`` is
- defined, the current step will be determined automatically.
- The method gets three arguments:
+ defined, the current step will be determined automatically. If you override
+ ``get_form``, however, you will need to set ``step`` yourself using
+ ``self.steps.current`` as in the example below. The method gets three
+ arguments:
* ``step`` -- The step for which the form instance should be generated.
* ``data`` -- Gets passed to the form's data argument
@@ -418,6 +420,11 @@
def get_form(self, step=None, data=None, files=None):
form = super(MyWizard, self).get_form(step, data, files)
+
+ # determine the step if not given
+ if step is None:
+ step = self.steps.current
+
if step == '1':
form.user = self.request.user
return form
diff --git a/lib/django-1.5/docs/ref/contrib/gis/gdal.txt b/lib/django-1.5/docs/ref/contrib/gis/gdal.txt
index 161efa3..c680306 100644
--- a/lib/django-1.5/docs/ref/contrib/gis/gdal.txt
+++ b/lib/django-1.5/docs/ref/contrib/gis/gdal.txt
@@ -634,8 +634,8 @@
or any other input accepted by :class:`SpatialReference` (including
spatial reference WKT and PROJ.4 strings, or an integer SRID).
By default nothing is returned and the geometry is transformed in-place.
- However, if the `clone` keyword is set to ``True`` then a transformed clone
- of this geometry is returned instead.
+ However, if the ``clone`` keyword is set to ``True`` then a transformed
+ clone of this geometry is returned instead.
.. method:: intersects(other)
diff --git a/lib/django-1.5/docs/ref/contrib/gis/geoip.txt b/lib/django-1.5/docs/ref/contrib/gis/geoip.txt
index e37c4c6..49f54f3 100644
--- a/lib/django-1.5/docs/ref/contrib/gis/geoip.txt
+++ b/lib/django-1.5/docs/ref/contrib/gis/geoip.txt
@@ -17,8 +17,7 @@
in ``utils``, but will be removed in Django 1.6.
The :class:`GeoIP` object is a ctypes wrapper for the
-`MaxMind GeoIP C API`__. [#]_ This interface is a BSD-licensed alternative
-to the GPL-licensed `Python GeoIP`__ interface provided by MaxMind.
+`MaxMind GeoIP C API`__. [#]_
In order to perform IP-based geolocation, the :class:`GeoIP` object requires
the GeoIP C libary and either the GeoIP `Country`__ or `City`__
@@ -29,7 +28,6 @@
reference below for more details.
__ http://www.maxmind.com/app/c
-__ http://www.maxmind.com/app/python
__ http://www.maxmind.com/app/country
__ http://www.maxmind.com/app/city
__ http://www.maxmind.com/download/geoip/database/
diff --git a/lib/django-1.5/docs/ref/contrib/gis/geoquerysets.txt b/lib/django-1.5/docs/ref/contrib/gis/geoquerysets.txt
index 66afc3d..97217e3 100644
--- a/lib/django-1.5/docs/ref/contrib/gis/geoquerysets.txt
+++ b/lib/django-1.5/docs/ref/contrib/gis/geoquerysets.txt
@@ -949,6 +949,9 @@
*Availability*: PostGIS, SpatiaLite
+.. versionchanged:: 1.5
+ ``geojson`` support for Spatialite > 3.0 has been added.
+
Attaches a ``geojson`` attribute to every model in the queryset that contains the
`GeoJSON`__ representation of the geometry.
diff --git a/lib/django-1.5/docs/ref/contrib/localflavor.txt b/lib/django-1.5/docs/ref/contrib/localflavor.txt
index 83d7837..63c641e 100644
--- a/lib/django-1.5/docs/ref/contrib/localflavor.txt
+++ b/lib/django-1.5/docs/ref/contrib/localflavor.txt
@@ -8,12 +8,13 @@
Historically, Django has shipped with ``django.contrib.localflavor`` --
assorted pieces of code that are useful for particular countries or cultures.
-Starting with Django 1.5, we've started the process of moving the code to
-outside packages (i.e., packages distributed separately from Django), for
+Starting with Django 1.5, we've started the process of moving the code to an
+external package (i.e., a package distributed separately from Django), for
easier maintenance and to trim the size of Django's codebase.
-The localflavor packages are named ``django-localflavor-*``, where the asterisk
-is an `ISO 3166 country code`_. For example: ``django-localflavor-us`` is the
+The new localflavor package is named ``django-localflavor``, with a main
+module called ``localflavor`` and many subpackages using an
+`ISO 3166 country code`_. For example: ``localflavor.us`` is the
localflavor package for the U.S.A.
Most of these ``localflavor`` add-ons are country-specific fields for the
@@ -27,7 +28,7 @@
French telephone number::
from django import forms
- from django_localflavor_fr.forms import FRPhoneNumberField
+ from localflavor.fr.forms import FRPhoneNumberField
class MyForm(forms.Form):
my_french_phone_no = FRPhoneNumberField()
@@ -42,13 +43,13 @@
How to migrate
==============
-If you've used the old ``django.contrib.localflavor`` package, follow these two
-easy steps to update your code:
+If you've used the old ``django.contrib.localflavor`` package or one of the
+temporary ``django-localflavor-*`` releases, follow these two easy steps to
+update your code:
-1. Install the appropriate third-party ``django-localflavor-*`` package(s).
- Go to https://github.com/django/ and find the package for your country.
+1. Install the third-party ``django-localflavor`` package from PyPI.
-2. Change your app's import statements to reference the new packages.
+2. Change your app's import statements to reference the new package.
For example, change this::
@@ -56,9 +57,9 @@
...to this::
- from django_localflavor_fr.forms import FRPhoneNumberField
+ from localflavor.fr.forms import FRPhoneNumberField
-The code in the new packages is the same (it was copied directly from Django),
+The code in the new package is the same (it was copied directly from Django),
so you don't have to worry about backwards compatibility in terms of
functionality. Only the imports have changed.
@@ -79,59 +80,15 @@
Supported countries
===================
-The following countries have django-localflavor- packages.
+See the official documentation for more information:
-* Argentina: https://github.com/django/django-localflavor-ar
-* Australia: https://github.com/django/django-localflavor-au
-* Austria: https://github.com/django/django-localflavor-at
-* Belgium: https://github.com/django/django-localflavor-be
-* Brazil: https://github.com/django/django-localflavor-br
-* Canada: https://github.com/django/django-localflavor-ca
-* Chile: https://github.com/django/django-localflavor-cl
-* China: https://github.com/django/django-localflavor-cn
-* Colombia: https://github.com/django/django-localflavor-co
-* Croatia: https://github.com/django/django-localflavor-cr
-* Czech Republic: https://github.com/django/django-localflavor-cz
-* Ecuador: https://github.com/django/django-localflavor-ec
-* Finland: https://github.com/django/django-localflavor-fi
-* France: https://github.com/django/django-localflavor-fr
-* Germany: https://github.com/django/django-localflavor-de
-* Hong Kong: https://github.com/django/django-localflavor-hk
-* Iceland: https://github.com/django/django-localflavor-is
-* India: https://github.com/django/django-localflavor-in
-* Indonesia: https://github.com/django/django-localflavor-id
-* Ireland: https://github.com/django/django-localflavor-ie
-* Israel: https://github.com/django/django-localflavor-il
-* Italy: https://github.com/django/django-localflavor-it
-* Japan: https://github.com/django/django-localflavor-jp
-* Kuwait: https://github.com/django/django-localflavor-kw
-* Lithuania: https://github.com/simukis/django-localflavor-lt
-* Macedonia: https://github.com/django/django-localflavor-mk
-* Mexico: https://github.com/django/django-localflavor-mx
-* The Netherlands: https://github.com/django/django-localflavor-nl
-* Norway: https://github.com/django/django-localflavor-no
-* Peru: https://github.com/django/django-localflavor-pe
-* Poland: https://github.com/django/django-localflavor-pl
-* Portugal: https://github.com/django/django-localflavor-pt
-* Paraguay: https://github.com/django/django-localflavor-py
-* Romania: https://github.com/django/django-localflavor-ro
-* Russia: https://github.com/django/django-localflavor-ru
-* Slovakia: https://github.com/django/django-localflavor-sk
-* Slovenia: https://github.com/django/django-localflavor-si
-* South Africa: https://github.com/django/django-localflavor-za
-* Spain: https://github.com/django/django-localflavor-es
-* Sweden: https://github.com/django/django-localflavor-se
-* Switzerland: https://github.com/django/django-localflavor-ch
-* Turkey: https://github.com/django/django-localflavor-tr
-* United Kingdom: https://github.com/django/django-localflavor-gb
-* United States of America: https://github.com/django/django-localflavor-us
-* Uruguay: https://github.com/django/django-localflavor-uy
+ https://django-localflavor.readthedocs.org/
django.contrib.localflavor.generic
==================================
The ``django.contrib.localflavor.generic`` package, which hasn't been removed from
-Django yet, contains useful code that is not specific to one particular country
+Django 1.5 yet, contains useful code that is not specific to one particular country
or culture. Currently, it defines date, datetime and split datetime input
fields based on those from :doc:`forms </topics/forms/index>`, but with non-US
default formats. Here's an example of how to use them::
@@ -142,13 +99,16 @@
class MyForm(forms.Form):
my_date_field = generic.forms.DateField()
+Of course the same package can also be found in the ``django-localflavor``
+packages and it's recommended to use that instead.
+
Internationalization of localflavors
====================================
-To activate translations for a newly-created ``localflavor`` application, you
-must include the application's name (e.g. ``django_localflavor_jp``) in the
-:setting:`INSTALLED_APPS` setting, so the internationalization system can find
-the catalog, as explained in :ref:`how-django-discovers-translations`.
+To activate translations for the ``localflavor`` application, you must include
+the application's name in the :setting:`INSTALLED_APPS` setting, so the
+internationalization system can find the catalog, as explained in
+:ref:`how-django-discovers-translations`.
If you're still using the legacy ``localflavor`` application, you must include
:mod:`django.contrib.localflavor` in :setting:`INSTALLED_APPS` (that will
diff --git a/lib/django-1.5/docs/ref/contrib/messages.txt b/lib/django-1.5/docs/ref/contrib/messages.txt
index 661d7f2..71d6c95 100644
--- a/lib/django-1.5/docs/ref/contrib/messages.txt
+++ b/lib/django-1.5/docs/ref/contrib/messages.txt
@@ -54,20 +54,21 @@
The messages framework can use different backends to store temporary messages.
-Django provides three built-in storage classes:
+Django provides three built-in storage classes in
+:mod:`django.contrib.messages`:
-.. class:: django.contrib.messages.storage.session.SessionStorage
+.. class:: storage.session.SessionStorage
This class stores all messages inside of the request's session. Therefore
it requires Django's ``contrib.sessions`` application.
-.. class:: django.contrib.messages.storage.cookie.CookieStorage
+.. class:: storage.cookie.CookieStorage
This class stores the message data in a cookie (signed with a secret hash
to prevent manipulation) to persist notifications across requests. Old
messages are dropped if the cookie data size would exceed 2048 bytes.
-.. class:: django.contrib.messages.storage.fallback.FallbackStorage
+.. class:: storage.fallback.FallbackStorage
This class first uses ``CookieStorage``, and falls back to using
``SessionStorage`` for the messages that could not fit in a single cookie.
@@ -83,6 +84,8 @@
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
+.. class:: storage.base.BaseStorage
+
To write your own storage class, subclass the ``BaseStorage`` class in
``django.contrib.messages.storage.base`` and implement the ``_get`` and
``_store`` methods.
diff --git a/lib/django-1.5/docs/ref/contrib/sitemaps.txt b/lib/django-1.5/docs/ref/contrib/sitemaps.txt
index 3e8985b..6d37c03 100644
--- a/lib/django-1.5/docs/ref/contrib/sitemaps.txt
+++ b/lib/django-1.5/docs/ref/contrib/sitemaps.txt
@@ -322,7 +322,7 @@
from django.views.decorators.cache import cache_page
urlpatterns = patterns('',
- url(r'^sitemap.xml$',
+ url(r'^sitemap\.xml$',
cache_page(86400)(sitemaps_views.index),
{'sitemaps': sitemaps, 'sitemap_url_name': 'sitemaps'}),
url(r'^sitemap-(?P<section>.+)\.xml$',
@@ -462,8 +462,8 @@
to Google's servers, so you may not want to introduce that network overhead
each time you call ``save()``.
-Pinging Google via `manage.py`
-------------------------------
+Pinging Google via ``manage.py``
+--------------------------------
.. django-admin:: ping_google
diff --git a/lib/django-1.5/docs/ref/contrib/staticfiles.txt b/lib/django-1.5/docs/ref/contrib/staticfiles.txt
index f32e1a0..3b22127 100644
--- a/lib/django-1.5/docs/ref/contrib/staticfiles.txt
+++ b/lib/django-1.5/docs/ref/contrib/staticfiles.txt
@@ -12,7 +12,8 @@
.. seealso::
For an introduction to the static files app and some usage examples, see
- :doc:`/howto/static-files`.
+ :doc:`/howto/static-files/index`. For guidelines on deploying static files,
+ see :doc:`/howto/static-files/deployment`.
.. _staticfiles-settings:
@@ -105,7 +106,9 @@
The default will find files stored in the :setting:`STATICFILES_DIRS` setting
(using ``django.contrib.staticfiles.finders.FileSystemFinder``) and in a
``static`` subdirectory of each app (using
-``django.contrib.staticfiles.finders.AppDirectoriesFinder``)
+``django.contrib.staticfiles.finders.AppDirectoriesFinder``). If multiple
+files with the same name are present, the first file that is found will be
+used.
One finder is disabled by default:
``django.contrib.staticfiles.finders.DefaultStorageFinder``. If added to
@@ -255,7 +258,9 @@
**grossly inefficient** and probably **insecure**. This is only intended for
local development, should **never be used in production** and is only
available if the :doc:`staticfiles </ref/contrib/staticfiles>` app is
-in your project's :setting:`INSTALLED_APPS` setting.
+in your project's :setting:`INSTALLED_APPS` setting. :djadmin:`runserver`
+``--insecure`` doesn't work with
+:class:`~django.contrib.staticfiles.storage.CachedStaticFilesStorage`.
Example usage::
@@ -422,9 +427,18 @@
Static file development view
----------------------------
+.. currentmodule:: django.contrib.staticfiles
+
+The static files tools are mostly designed to help with getting static files
+successfully deployed into production. This usually means a separate,
+dedicated static file server, which is a lot of overhead to mess with when
+developing locally. Thus, the ``staticfiles`` app ships with a
+**quick and dirty helper view** that you can use to serve files locally in
+development.
+
.. highlight:: python
-.. function:: django.contrib.staticfiles.views.serve(request, path)
+.. function:: views.serve(request, path)
This view function serves static files in development.
@@ -451,9 +465,10 @@
Note, the beginning of the pattern (``r'^static/'``) should be your
:setting:`STATIC_URL` setting.
-Since this is a bit finicky, there's also a helper function that'll do this for you:
+Since this is a bit finicky, there's also a helper function that'll do this for
+you:
-.. function:: django.contrib.staticfiles.urls.staticfiles_urlpatterns()
+.. function:: urls.staticfiles_urlpatterns()
This will return the proper URL pattern for serving static files to your
already defined pattern list. Use it like this::
@@ -464,8 +479,18 @@
urlpatterns += staticfiles_urlpatterns()
+This will inspect your :setting:`STATIC_URL` setting and wire up the view
+to serve static files accordingly. Don't forget to set the
+:setting:`STATICFILES_DIRS` setting appropriately to let
+``django.contrib.staticfiles`` know where to look for files in addition to
+files in app directories.
+
.. warning::
This helper function will only work if :setting:`DEBUG` is ``True``
and your :setting:`STATIC_URL` setting is neither empty nor a full
URL such as ``http://static.example.com/``.
+
+ That's because this view is **grossly inefficient** and probably
+ **insecure**. This is only intended for local development, and should
+ **never be used in production**.
diff --git a/lib/django-1.5/docs/ref/contrib/syndication.txt b/lib/django-1.5/docs/ref/contrib/syndication.txt
index 2955d7d..789c2d7 100644
--- a/lib/django-1.5/docs/ref/contrib/syndication.txt
+++ b/lib/django-1.5/docs/ref/contrib/syndication.txt
@@ -17,7 +17,7 @@
lower-level way.
.. _RSS: http://www.whatisrss.com/
-.. _Atom: http://www.atomenabled.org/
+.. _Atom: http://tools.ietf.org/html/rfc4287
The high-level framework
========================
@@ -49,17 +49,17 @@
A simple example
----------------
-This simple example, taken from `chicagocrime.org`_, describes a feed of the
-latest five news items::
+This simple example, taken from a hypothetical police beat news site describes
+a feed of the latest five news items::
from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse
- from chicagocrime.models import NewsItem
+ from policebeat.models import NewsItem
class LatestEntriesFeed(Feed):
- title = "Chicagocrime.org site news"
+ title = "Police beat site news"
link = "/sitenews/"
- description = "Updates on changes and additions to chicagocrime.org."
+ description = "Updates on changes and additions to police beat central."
def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
@@ -150,22 +150,20 @@
are responsible for doing all necessary URL quoting and conversion to
ASCII inside the method itself.
-.. _chicagocrime.org: http://www.chicagocrime.org/
-
A complex example
-----------------
The framework also supports more complex feeds, via arguments.
-For example, `chicagocrime.org`_ offers an RSS feed of recent crimes for every
-police beat in Chicago. It'd be silly to create a separate
+For example, a website could offer an RSS feed of recent crimes for every
+police beat in a city. It'd be silly to create a separate
:class:`~django.contrib.syndication.views.Feed` class for each police beat; that
would violate the :ref:`DRY principle <dry>` and would couple data to
programming logic. Instead, the syndication framework lets you access the
arguments passed from your :doc:`URLconf </topics/http/urls>` so feeds can output
items based on information in the feed's URL.
-On chicagocrime.org, the police-beat feeds are accessible via URLs like this:
+The police beat feeds could be accessible via URLs like this:
* :file:`/beats/613/rss/` -- Returns recent crimes for beat 613.
* :file:`/beats/1424/rss/` -- Returns recent crimes for beat 1424.
@@ -189,7 +187,7 @@
return get_object_or_404(Beat, pk=beat_id)
def title(self, obj):
- return "Chicagocrime.org: Crimes for beat %s" % obj.beat
+ return "Police beat central: Crimes for beat %s" % obj.beat
def link(self, obj):
return obj.get_absolute_url()
@@ -290,13 +288,13 @@
Here's a full example::
from django.contrib.syndication.views import Feed
- from chicagocrime.models import NewsItem
+ from policebeat.models import NewsItem
from django.utils.feedgenerator import Atom1Feed
class RssSiteNewsFeed(Feed):
- title = "Chicagocrime.org site news"
+ title = "Police beat site news"
link = "/sitenews/"
- description = "Updates on changes and additions to chicagocrime.org."
+ description = "Updates on changes and additions to police beat central."
def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
diff --git a/lib/django-1.5/docs/ref/databases.txt b/lib/django-1.5/docs/ref/databases.txt
index fb8425d..f4656ad 100644
--- a/lib/django-1.5/docs/ref/databases.txt
+++ b/lib/django-1.5/docs/ref/databases.txt
@@ -151,8 +151,8 @@
Storage engines
---------------
-MySQL has several `storage engines`_ (previously called table types). You can
-change the default storage engine in the server configuration.
+MySQL has several `storage engines`_. You can change the default storage engine
+in the server configuration.
Until MySQL 5.5.4, the default engine was MyISAM_ [#]_. The main drawbacks of
MyISAM are that it doesn't support transactions or enforce foreign-key
@@ -161,7 +161,10 @@
Since MySQL 5.5.5, the default storage engine is InnoDB_. This engine is fully
transactional and supports foreign key references. It's probably the best
-choice at this point.
+choice at this point. However, note that the the InnoDB autoincrement counter
+is lost on a MySQL restart because it does not remember the
+``AUTO_INCREMENT`` value, instead recreating it as "max(id)+1". This may
+result in an inadvertent reuse of :class:`~django.db.models.AutoField` values.
If you upgrade an existing project to MySQL 5.5.5 and subsequently add some
tables, ensure that your tables are using the same storage engine (i.e. MyISAM
@@ -187,7 +190,7 @@
.. _storage engines: http://dev.mysql.com/doc/refman/5.5/en/storage-engines.html
.. _MyISAM: http://dev.mysql.com/doc/refman/5.5/en/myisam-storage-engine.html
-.. _InnoDB: http://dev.mysql.com/doc/refman/5.5/en/innodb.html
+.. _InnoDB: http://dev.mysql.com/doc/refman/5.5/en/innodb-storage-engine.html
.. [#] Unless this was changed by the packager of your MySQL package. We've
had reports that the Windows Community Server installer sets up InnoDB as
@@ -209,6 +212,15 @@
.. _MySQLdb: http://sourceforge.net/projects/mysql-python
+Python 3
+--------
+
+At the time of writing, the latest release of MySQLdb (1.2.4) doesn't support
+Python 3. In order to use MySQL under Python 3, you'll have to install an
+unofficial fork, such as `MySQL-for-Python-3`_.
+
+.. _MySQL-for-Python-3: https://github.com/clelland/MySQL-for-Python-3
+
Creating your database
----------------------
@@ -318,8 +330,8 @@
default-character-set = utf8
Several other MySQLdb connection options may be useful, such as ``ssl``,
-``use_unicode``, ``init_command``, and ``sql_mode``. Consult the
-`MySQLdb documentation`_ for more details.
+``init_command``, and ``sql_mode``. Consult the `MySQLdb documentation`_ for
+more details.
.. _MySQL option file: http://dev.mysql.com/doc/refman/5.0/en/option-files.html
.. _MySQLdb documentation: http://mysql-python.sourceforge.net/
@@ -608,6 +620,14 @@
* CONNECT WITH ADMIN OPTION
* RESOURCE WITH ADMIN OPTION
+The Oracle database backend uses the ``SYS.DBMS_LOB`` package, so your user
+will require execute permissions on it. It's normally accessible to all users
+by default, but in case it is not, you'll need to grant permissions like so:
+
+.. code-block:: sql
+
+ GRANT EXECUTE ON SYS.DBMS_LOB TO user;
+
Connecting to the database
--------------------------
@@ -646,9 +666,9 @@
Threaded option
----------------
-If you plan to run Django in a multithreaded environment (e.g. Apache in Windows
-using the default MPM module), then you **must** set the ``threaded`` option of
-your Oracle database configuration to True::
+If you plan to run Django in a multithreaded environment (e.g. Apache using the
+the default MPM module on any modern operating system), then you **must** set
+the ``threaded`` option of your Oracle database configuration to True::
'OPTIONS': {
'threaded': True,
diff --git a/lib/django-1.5/docs/ref/django-admin.txt b/lib/django-1.5/docs/ref/django-admin.txt
index a1f5cd5..4552440 100644
--- a/lib/django-1.5/docs/ref/django-admin.txt
+++ b/lib/django-1.5/docs/ref/django-admin.txt
@@ -25,9 +25,10 @@
Environment...``) to point to its installed location.
Generally, when working on a single Django project, it's easier to use
-``manage.py``. Use ``django-admin.py`` with ``DJANGO_SETTINGS_MODULE``, or the
-``--settings`` command line option, if you need to switch between multiple
-Django settings files.
+``manage.py`` than ``django-admin.py``. If you need to switch between multiple
+Django settings files, use ``django-admin.py`` with
+:envvar:`DJANGO_SETTINGS_MODULE` or the :djadminopt:`--settings` command line
+option.
The command-line examples throughout this document use ``django-admin.py`` to
be consistent, but any example can use ``manage.py`` just as well.
@@ -228,8 +229,8 @@
The :djadminopt:`--database` option may be used to specify the database
to flush.
---no-initial-data
-~~~~~~~~~~~~~~~~~
+``--no-initial-data``
+~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.5
@@ -343,7 +344,46 @@
:setting:`FIXTURE_DIRS`, and the literal path ``foo/bar/mydata.json``.
When fixture files are processed, the data is saved to the database as is.
-Model defined ``save`` methods and ``pre_save`` signals are not called.
+Model defined :meth:`~django.db.models.Model.save` methods are not called, and
+any :data:`~django.db.models.signals.pre_save` or
+:data:`~django.db.models.signals.post_save` signals will be called with
+``raw=True`` since the instance only contains attributes that are local to the
+model. You may, for example, want to disable handlers that access
+related fields that aren't present during fixture loading and would otherwise
+raise an exception::
+
+ from django.db.models.signals import post_save
+ from .models import MyModel
+
+ def my_handler(**kwargs):
+ # disable the handler during fixture loading
+ if kwargs['raw']:
+ return
+ ...
+
+ post_save.connect(my_handler, sender=MyModel)
+
+You could also write a simple decorator to encapsulate this logic::
+
+ from functools import wraps
+
+ def disable_for_loaddata(signal_handler):
+ """
+ Decorator that turns off signal handlers when loading fixture data.
+ """
+ @wraps(signal_handler)
+ def wrapper(*args, **kwargs):
+ if kwargs['raw']:
+ return
+ signal_handler(*args, **kwargs)
+ return wrapper
+
+ @disable_for_loaddata
+ def my_handler(**kwargs):
+ ...
+
+Just be aware that this logic will disable the signals whenever fixtures are
+deserialized, not just during ``loaddata``.
Note that the order in which fixture files are processed is undefined. However,
all fixture data is installed as a single transaction, so data in
@@ -720,7 +760,8 @@
By default, the development server doesn't serve any static files for your site
(such as CSS files, images, things under :setting:`MEDIA_URL` and so forth). If
-you want to configure Django to serve static media, read :doc:`/howto/static-files`.
+you want to configure Django to serve static media, read
+:doc:`/howto/static-files/index`.
shell
-----
@@ -976,7 +1017,8 @@
with the ``--name`` option. The :class:`template context
<django.template.Context>` used is:
-- Any option passed to the startproject command
+- Any option passed to the startapp command (among the command's supported
+ options)
- ``project_name`` -- the project name as passed to the command
- ``project_directory`` -- the full path of the newly created project
- ``secret_key`` -- a random key for the :setting:`SECRET_KEY` setting
@@ -1026,8 +1068,8 @@
The :djadminopt:`--database` option can be used to specify the database to
synchronize.
---no-initial-data
-~~~~~~~~~~~~~~~~~
+``--no-initial-data``
+~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.5
@@ -1214,6 +1256,8 @@
.. django-admin:: clearsessions
+.. versionadded:: 1.5
+
Can be run as a cron job or directly to clean out expired sessions.
``django.contrib.sitemaps``
@@ -1235,7 +1279,7 @@
~~~~~~~~~~~~~
This command is only available if the :doc:`static files application
-</howto/static-files>` (``django.contrib.staticfiles``) is installed.
+</howto/static-files/index>` (``django.contrib.staticfiles``) is installed.
Please refer to its :djadmin:`description <collectstatic>` in the
:doc:`staticfiles </ref/contrib/staticfiles>` documentation.
@@ -1244,7 +1288,7 @@
~~~~~~~~~~
This command is only available if the :doc:`static files application
-</howto/static-files>` (``django.contrib.staticfiles``) is installed.
+</howto/static-files/index>` (``django.contrib.staticfiles``) is installed.
Please refer to its :djadmin:`description <findstatic>` in the :doc:`staticfiles
</ref/contrib/staticfiles>` documentation.
@@ -1326,7 +1370,7 @@
.. django-admin-option:: --exclude
Exclude a specific application from the applications whose contents is
-output. For example, to specifically exclude the `auth` application from
+output. For example, to specifically exclude the ``auth`` application from
the output of dumpdata, you would call::
django-admin.py dumpdata --exclude=auth
@@ -1486,6 +1530,15 @@
management.call_command('flush', verbosity=0, interactive=False)
management.call_command('loaddata', 'test_data', verbosity=0)
+Note that command options that take no arguments are passed as keywords
+with ``True`` or ``False``::
+
+ management.call_command('dumpdata', use_natural_keys=True)
+
+Command options which take multiple options are passed a list::
+
+ management.call_command('dumpdata', exclude=['contenttypes', 'auth'])
+
Output redirection
==================
diff --git a/lib/django-1.5/docs/ref/files/file.txt b/lib/django-1.5/docs/ref/files/file.txt
index 7562f9b..3f121e0 100644
--- a/lib/django-1.5/docs/ref/files/file.txt
+++ b/lib/django-1.5/docs/ref/files/file.txt
@@ -11,15 +11,15 @@
.. class:: File(file_object)
- The :class:`File` is a thin wrapper around Python's built-in file object
- with some Django-specific additions. Internally, Django uses this class
- any time it needs to represent a file.
+ The :class:`File` class is a thin wrapper around Python's :py:ref:`built-in
+ file object<bltin-file-objects>` with some Django-specific additions.
+ Internally, Django uses this class when it needs to represent a file.
:class:`File` objects have the following attributes and methods:
.. attribute:: name
- The name of file including the relative path from
+ The name of the file including the relative path from
:setting:`MEDIA_ROOT`.
.. attribute:: size
@@ -28,8 +28,8 @@
.. attribute:: file
- The underlying Python ``file`` object passed to
- :class:`~django.core.files.File`.
+ The underlying :py:ref:`built-in file object<bltin-file-objects>` that
+ this class wraps.
.. attribute:: mode
@@ -37,9 +37,9 @@
.. method:: open([mode=None])
- Open or reopen the file (which by definition also does
- ``File.seek(0)``). The ``mode`` argument allows the same values
- as Python's standard ``open()``.
+ Open or reopen the file (which also does ``File.seek(0)``).
+ The ``mode`` argument allows the same values
+ as Python's built-in :func:`python:open()`.
When reopening a file, ``mode`` will override whatever mode the file
was originally opened with; ``None`` means to reopen with the original
@@ -71,14 +71,14 @@
Writes the specified content string to the file. Depending on the
storage system behind the scenes, this content might not be fully
- committed until ``close()`` is called on the file.
+ committed until :func:`close()` is called on the file.
.. method:: close()
Close the file.
In addition to the listed methods, :class:`~django.core.files.File` exposes
- the following attributes and methods of the underlying ``file`` object:
+ the following attributes and methods of its ``file`` object:
``encoding``, ``fileno``, ``flush``, ``isatty``, ``newlines``,
``read``, ``readinto``, ``readlines``, ``seek``, ``softspace``, ``tell``,
``truncate``, ``writelines``, ``xreadlines``.
@@ -129,7 +129,7 @@
Additional methods on files attached to objects
-----------------------------------------------
-Any :class:`File` that's associated with an object (as with ``Car.photo``,
+Any :class:`File` that is associated with an object (as with ``Car.photo``,
below) will also have a couple of extra methods:
.. method:: File.save(name, content, [save=True])
@@ -142,7 +142,7 @@
>>> car.photo.save('myphoto.jpg', content, save=False)
>>> car.save()
- are the same as this one line::
+ are equivalent to::
>>> car.photo.save('myphoto.jpg', content, save=True)
diff --git a/lib/django-1.5/docs/ref/forms/api.txt b/lib/django-1.5/docs/ref/forms/api.txt
index d1f877f..e6b0d3e 100644
--- a/lib/django-1.5/docs/ref/forms/api.txt
+++ b/lib/django-1.5/docs/ref/forms/api.txt
@@ -638,6 +638,19 @@
>>> str(f['subject'].errors)
''
+.. method:: BoundField.label_tag(contents=None, attrs=None)
+
+To separately render the label tag of a form field, you can call its
+``label_tag`` method::
+
+ >>> f = ContactForm(data)
+ >>> print(f['message'].label_tag())
+ <label for="id_message">Message</label>
+
+Optionally, you can provide the ``contents`` parameter which will replace the
+auto-generated label tag. An optional ``attrs`` dictionary may contain
+additional attributes for the ``<label>`` tag.
+
.. method:: BoundField.css_classes()
When you use Django's rendering shortcuts, CSS classes are used to
@@ -670,6 +683,29 @@
>>> print(bound_form['subject'].value())
hi
+.. attribute:: BoundField.id_for_label
+
+Use this property to render the ID of this field. For example, if you are
+manually constructing a ``<label>`` in your template (despite the fact that
+:meth:`~BoundField.label_tag` will do this for you):
+
+.. code-block:: html+django
+
+ <label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
+
+By default, this will be the field's name prefixed by ``id_``
+("``id_my_field``" for the example above). You may modify the ID by setting
+:attr:`~django.forms.Widget.attrs` on the field's widget. For example,
+declaring a field like this::
+
+ my_field = forms.CharField(widget=forms.TextInput(attrs={'id': 'myFIELD'}))
+
+and using the template above, would render something like:
+
+.. code-block:: html
+
+ <label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" />
+
.. _binding-uploaded-files:
Binding uploaded files to a form
diff --git a/lib/django-1.5/docs/ref/forms/models.txt b/lib/django-1.5/docs/ref/forms/models.txt
index c388f40..3d83521 100644
--- a/lib/django-1.5/docs/ref/forms/models.txt
+++ b/lib/django-1.5/docs/ref/forms/models.txt
@@ -42,8 +42,8 @@
.. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None)
Returns an ``InlineFormSet`` using :func:`modelformset_factory` with
- defaults of ``formset=BaseInlineFormSet``, ``can_delete=True``, and
- ``extra=3``.
+ defaults of ``formset=``:class:`~django.forms.models.BaseInlineFormSet`,
+ ``can_delete=True``, and ``extra=3``.
If your model has more than one :class:`~django.db.models.ForeignKey` to
the ``parent_model``, you must specify a ``fk_name``.
diff --git a/lib/django-1.5/docs/ref/forms/validation.txt b/lib/django-1.5/docs/ref/forms/validation.txt
index e89bce7..6690ada 100644
--- a/lib/django-1.5/docs/ref/forms/validation.txt
+++ b/lib/django-1.5/docs/ref/forms/validation.txt
@@ -272,6 +272,8 @@
Cleaning and validating fields that depend on each other
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. method:: django.forms.Form.clean
+
Suppose we add another requirement to our contact form: if the ``cc_myself``
field is ``True``, the ``subject`` must contain the word ``"help"``. We are
performing validation on more than one field at a time, so the form's
diff --git a/lib/django-1.5/docs/ref/forms/widgets.txt b/lib/django-1.5/docs/ref/forms/widgets.txt
index 98383b8..13d2fd5 100644
--- a/lib/django-1.5/docs/ref/forms/widgets.txt
+++ b/lib/django-1.5/docs/ref/forms/widgets.txt
@@ -52,8 +52,7 @@
:attr:`~django.forms.extras.widgets.SelectDateWidget.years` attribute is set
for a :class:`~django.forms.extras.widgets.SelectDateWidget`::
- from django.forms.fields import DateField, ChoiceField, MultipleChoiceField
- from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
+ from django import forms
from django.forms.extras.widgets import SelectDateWidget
BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
@@ -62,9 +61,9 @@
('black', 'Black'))
class SimpleForm(forms.Form):
- birth_year = DateField(widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES))
+ birth_year = forms.DateField(widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES))
favorite_colors = forms.MultipleChoiceField(required=False,
- widget=CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)
+ widget=forms.CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)
See the :ref:`built-in widgets` for more information about which widgets
are available and which arguments they accept.
@@ -163,6 +162,9 @@
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
+You can also set the HTML ``id`` using :attr:`~Widget.attrs`. See
+:attr:`BoundField.id_for_label` for an example.
+
.. _styling-widget-classes:
Styling widget classes
@@ -249,6 +251,8 @@
:class:`~datetime.datetime` value into a list with date and time split
into two separate values::
+ from django.forms import MultiWidget
+
class SplitDateTimeWidget(MultiWidget):
# ...
diff --git a/lib/django-1.5/docs/ref/index.txt b/lib/django-1.5/docs/ref/index.txt
index fc874a9..1d71b62 100644
--- a/lib/django-1.5/docs/ref/index.txt
+++ b/lib/django-1.5/docs/ref/index.txt
@@ -25,3 +25,4 @@
urls
utils
validators
+ views
diff --git a/lib/django-1.5/docs/ref/middleware.txt b/lib/django-1.5/docs/ref/middleware.txt
index f36c1c3..92b4965 100644
--- a/lib/django-1.5/docs/ref/middleware.txt
+++ b/lib/django-1.5/docs/ref/middleware.txt
@@ -37,7 +37,7 @@
Adds a few conveniences for perfectionists:
* Forbids access to user agents in the :setting:`DISALLOWED_USER_AGENTS`
- setting, which should be a list of strings.
+ setting, which should be a list of compiled regular expression objects.
* Performs URL rewriting based on the :setting:`APPEND_SLASH` and
:setting:`PREPEND_WWW` settings.
@@ -90,6 +90,20 @@
.. class:: GZipMiddleware
+.. warning::
+
+ Security researchers recently revealed that when compression techniques
+ (including ``GZipMiddleware``) are used on a website, the site becomes
+ exposed to a number of possible attacks. These approaches can be used to
+ compromise, amongst other things, Django's CSRF protection. Before using
+ ``GZipMiddleware`` on your site, you should consider very carefully whether
+ you are subject to these attacks. If you're in *any* doubt about whether
+ you're affected, you should avoid using ``GZipMiddleware``. For more
+ details, see the `the BREACH paper (PDF)`_ and `breachattack.com`_.
+
+ .. _the BREACH paper (PDF): http://breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf
+ .. _breachattack.com: http://breachattack.com
+
Compresses content for browsers that understand GZip compression (all modern
browsers).
diff --git a/lib/django-1.5/docs/ref/models/fields.txt b/lib/django-1.5/docs/ref/models/fields.txt
index 0cd0169..3ac0acf 100644
--- a/lib/django-1.5/docs/ref/models/fields.txt
+++ b/lib/django-1.5/docs/ref/models/fields.txt
@@ -269,8 +269,8 @@
field, a :exc:`django.db.IntegrityError` will be raised by the model's
:meth:`~django.db.models.Model.save` method.
-This option is valid on all field types except :class:`ManyToManyField` and
-:class:`FileField`.
+This option is valid on all field types except :class:`ManyToManyField`,
+:class:`OneToOneField`, and :class:`FileField`.
Note that when ``unique`` is ``True``, you don't need to specify
:attr:`~Field.db_index`, because ``unique`` implies the creation of an index.
@@ -456,7 +456,7 @@
.. attribute:: DecimalField.max_digits
The maximum number of digits allowed in the number. Note that this number
- must be greater than or equal to ``decimal_places``, if it exists.
+ must be greater than or equal to ``decimal_places``.
.. attribute:: DecimalField.decimal_places
@@ -858,8 +858,8 @@
.. class:: PositiveIntegerField([**options])
-Like an :class:`IntegerField`, but must be either positive or zero (`0`).
-The value `0` is accepted for backward compatibility reasons.
+Like an :class:`IntegerField`, but must be either positive or zero (``0``).
+The value ``0`` is accepted for backward compatibility reasons.
``PositiveSmallIntegerField``
-----------------------------
@@ -867,7 +867,8 @@
.. class:: PositiveSmallIntegerField([**options])
Like a :class:`PositiveIntegerField`, but only allows values under a certain
-(database-dependent) point.
+(database-dependent) point. Values up to 32767 are safe in all databases
+supported by Django.
``SlugField``
-------------
@@ -895,7 +896,8 @@
.. class:: SmallIntegerField([**options])
Like an :class:`IntegerField`, but only allows values under a certain
-(database-dependent) point.
+(database-dependent) point. Values from -32768 to 32767 are safe in all databases
+supported by Django.
``TextField``
-------------
@@ -1115,10 +1117,10 @@
.. class:: ManyToManyField(othermodel, [**options])
-A many-to-many relationship. Requires a positional argument: the class to which
-the model is related. This works exactly the same as it does for
-:class:`ForeignKey`, including all the options regarding :ref:`recursive
-<recursive-relationships>` and :ref:`lazy <lazy-relationships>` relationships.
+A many-to-many relationship. Requires a positional argument: the class to
+which the model is related, which works exactly the same as it does for
+:class:`ForeignKey`, including :ref:`recursive <recursive-relationships>` and
+:ref:`lazy <lazy-relationships>` relationships.
Related objects can be added, removed, or created with the field's
:class:`~django.db.models.fields.related.RelatedManager`.
diff --git a/lib/django-1.5/docs/ref/models/instances.txt b/lib/django-1.5/docs/ref/models/instances.txt
index 92071b8..3d4c1fc 100644
--- a/lib/django-1.5/docs/ref/models/instances.txt
+++ b/lib/django-1.5/docs/ref/models/instances.txt
@@ -661,8 +661,11 @@
returns the next and previous object with respect to the date field, raising
a :exc:`~django.core.exceptions.DoesNotExist` exception when appropriate.
-Both methods accept optional keyword arguments, which should be in the format
-described in :ref:`Field lookups <field-lookups>`.
+Both of these methods will perform their queries using the default
+manager for the model. If you need to emulate filtering used by a
+custom manager, or want to perform one-off custom filtering, both
+methods also accept optional keyword arguments, which should be in the
+format described in :ref:`Field lookups <field-lookups>`.
Note that in the case of identical date values, these methods will use the
primary key as a tie-breaker. This guarantees that no records are skipped or
diff --git a/lib/django-1.5/docs/ref/models/options.txt b/lib/django-1.5/docs/ref/models/options.txt
index 307b462..c2a3430 100644
--- a/lib/django-1.5/docs/ref/models/options.txt
+++ b/lib/django-1.5/docs/ref/models/options.txt
@@ -262,6 +262,7 @@
an explicit :attr:`through <ManyToManyField.through>` model.
``index_together``
+------------------
.. attribute:: Options.index_together
diff --git a/lib/django-1.5/docs/ref/models/querysets.txt b/lib/django-1.5/docs/ref/models/querysets.txt
index ca2e64a..ac7ea2d 100644
--- a/lib/django-1.5/docs/ref/models/querysets.txt
+++ b/lib/django-1.5/docs/ref/models/querysets.txt
@@ -110,7 +110,7 @@
.. admonition:: You can't share pickles between versions
- Pickles of QuerySets are only valid for the version of Django that
+ Pickles of ``QuerySets`` are only valid for the version of Django that
was used to generate them. If you generate a pickle using Django
version N, there is no guarantee that pickle will be readable with
Django version N+1. Pickles should not be used as part of a long-term
@@ -299,14 +299,30 @@
:meth:`distinct()`. See the note in :meth:`distinct` for an explanation of how
related model ordering can change the expected results.
-It is permissible to specify a multi-valued field to order the results by (for
-example, a :class:`~django.db.models.ManyToManyField` field). Normally
-this won't be a sensible thing to do and it's really an advanced usage
-feature. However, if you know that your queryset's filtering or available data
-implies that there will only be one ordering piece of data for each of the main
-items you are selecting, the ordering may well be exactly what you want to do.
-Use ordering on multi-valued fields with care and make sure the results are
-what you expect.
+.. note::
+ It is permissible to specify a multi-valued field to order the results by
+ (for example, a :class:`~django.db.models.ManyToManyField` field, or the
+ reverse relation of a :class:`~django.db.models.ForeignKey` field).
+
+ Consider this case::
+
+ class Event(Model):
+ parent = models.ForeignKey('self', related_name='children')
+ date = models.DateField()
+
+ Event.objects.order_by('children__date')
+
+ Here, there could potentially be multiple ordering data for each ``Event``;
+ each ``Event`` with multiple ``children`` will be returned multiple times
+ into the new ``QuerySet`` that ``order_by()`` creates. In other words,
+ using ``order_by()`` on the ``QuerySet`` could return more items than you
+ were working on to begin with - which is probably neither expected nor
+ useful.
+
+ Thus, take care when using multi-valued field to order the results. **If**
+ you can be sure that there will only be one ordering piece of data for each
+ of the items you're ordering, this approach should not present problems. If
+ not, make sure the results are what you expect.
There's no way to specify whether ordering should be case sensitive. With
respect to case-sensitivity, Django will order results however your database
@@ -328,7 +344,7 @@
elements are returned. Calling ``reverse()`` a second time restores the
ordering back to the normal direction.
-To retrieve the ''last'' five items in a queryset, you could do this::
+To retrieve the "last" five items in a queryset, you could do this::
my_queryset.reverse()[:5]
@@ -394,7 +410,7 @@
.. note::
When you specify field names, you *must* provide an ``order_by()`` in the
- QuerySet, and the fields in ``order_by()`` must start with the fields in
+ ``QuerySet``, and the fields in ``order_by()`` must start with the fields in
``distinct()``, in the same order.
For example, ``SELECT DISTINCT ON (a)`` gives you the first row for each
@@ -750,8 +766,8 @@
but the strategy is quite different.
``select_related`` works by creating a SQL join and including the fields of the
-related object in the SELECT statement. For this reason, ``select_related`` gets
-the related objects in the same database query. However, to avoid the much
+related object in the ``SELECT`` statement. For this reason, ``select_related``
+gets the related objects in the same database query. However, to avoid the much
larger result set that would result from joining across a 'many' relationship,
``select_related`` is limited to single-valued relationships - foreign key and
one-to-one.
@@ -777,39 +793,54 @@
return u"%s (%s)" % (self.name, u", ".join([topping.name
for topping in self.toppings.all()]))
-and run this code::
+and run::
>>> Pizza.objects.all()
[u"Hawaiian (ham, pineapple)", u"Seafood (prawns, smoked salmon)"...
-The problem with this code is that it will run a query on the Toppings table for
-**every** item in the Pizza ``QuerySet``. Using ``prefetch_related``, this can
-be reduced to two:
+The problem with this is that every time ``Pizza.__unicode__()`` asks for
+``self.toppings.all()`` it has to query the database, so
+``Pizza.objects.all()`` will run a query on the Toppings table for **every**
+item in the Pizza ``QuerySet``.
+
+We can reduce to just two queries using ``prefetch_related``:
>>> Pizza.objects.all().prefetch_related('toppings')
-All the relevant toppings will be fetched in a single query, and used to make
-``QuerySets`` that have a pre-filled cache of the relevant results. These
-``QuerySets`` are then used in the ``self.toppings.all()`` calls.
+This implies a ``self.toppings.all()`` for each ``Pizza``; now each time
+``self.toppings.all()`` is called, instead of having to go to the database for
+the items, it will find them in a prefetched ``QuerySet`` cache that was
+populated in a single query.
-The additional queries are executed after the QuerySet has begun to be evaluated
-and the primary query has been executed. Note that the result cache of the
-primary QuerySet and all specified related objects will then be fully loaded
-into memory, which is often avoided in other cases - even after a query has been
-executed in the database, QuerySet normally tries to make uses of chunking
-between the database to avoid loading all objects into memory before you need
-them.
+That is, all the relevant toppings will have been fetched in a single query,
+and used to make ``QuerySets`` that have a pre-filled cache of the relevant
+results; these ``QuerySets`` are then used in the ``self.toppings.all()`` calls.
-Also remember that, as always with QuerySets, any subsequent chained methods
-which imply a different database query will ignore previously cached results,
-and retrieve data using a fresh database query. So, if you write the following:
+The additional queries in ``prefetch_related()`` are executed after the
+``QuerySet`` has begun to be evaluated and the primary query has been executed.
- >>> pizzas = Pizza.objects.prefetch_related('toppings')
- >>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
+Note that the result cache of the primary ``QuerySet`` and all specified related
+objects will then be fully loaded into memory. This changes the typical
+behavior of ``QuerySets``, which normally try to avoid loading all objects into
+memory before they are needed, even after a query has been executed in the
+database.
-...then the fact that ``pizza.toppings.all()`` has been prefetched will not help
-you - in fact it hurts performance, since you have done a database query that
-you haven't used. So use this feature with caution!
+.. note::
+
+ Remember that, as always with ``QuerySets``, any subsequent chained methods
+ which imply a different database query will ignore previously cached
+ results, and retrieve data using a fresh database query. So, if you write
+ the following:
+
+ >>> pizzas = Pizza.objects.prefetch_related('toppings')
+ >>> [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
+
+ ...then the fact that ``pizza.toppings.all()`` has been prefetched will not
+ help you. The ``prefetch_related('toppings')`` implied
+ ``pizza.toppings.all()``, but ``pizza.toppings.filter()`` is a new and
+ different query. The prefetched cache can't help here; in fact it hurts
+ performance, since you have done a database query that you haven't used. So
+ use this feature with caution!
You can also use the normal join syntax to do related fields of related
fields. Suppose we have an additional model to the example above::
@@ -862,7 +893,7 @@
already been fetched.
``prefetch_related`` in most cases will be implemented using a SQL query that
-uses the 'IN' operator. This means that for a large QuerySet a large 'IN' clause
+uses the 'IN' operator. This means that for a large ``QuerySet`` a large 'IN' clause
could be generated, which, depending on the database, might have performance
problems of its own when it comes to parsing or executing the SQL query. Always
profile for your use case!
@@ -1297,15 +1328,14 @@
.. method:: get_or_create(**kwargs)
-A convenience method for looking up an object with the given kwargs, creating
-one if necessary.
+A convenience method for looking up an object with the given ``kwargs``,
+creating one if necessary.
Returns a tuple of ``(object, created)``, where ``object`` is the retrieved or
created object and ``created`` is a boolean specifying whether a new object was
created.
-This is meant as a shortcut to boilerplatish code and is mostly useful for
-data-import scripts. For example::
+This is meant as a shortcut to boilerplatish code. For example::
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
@@ -1353,13 +1383,25 @@
created and the key already exists in the database, an
:exc:`~django.db.IntegrityError` will be raised.
-Finally, a word on using ``get_or_create()`` in Django views. As mentioned
-earlier, ``get_or_create()`` is mostly useful in scripts that need to parse
-data and create new records if existing ones aren't available. But if you need
-to use ``get_or_create()`` in a view, please make sure to use it only in
-``POST`` requests unless you have a good reason not to. ``GET`` requests
-shouldn't have any effect on data; use ``POST`` whenever a request to a page
-has a side effect on your data. For more, see `Safe methods`_ in the HTTP spec.
+This method is atomic assuming correct usage, correct database configuration,
+and correct behavior of the underlying database. However, if uniqueness is not
+enforced at the database level for the ``kwargs`` used in a ``get_or_create``
+call (see :attr:`~django.db.models.Field.unique` or
+:attr:`~django.db.models.Options.unique_together`), this method is prone to a
+race-condition which can result in multiple rows with the same parameters being
+inserted simultaneously.
+
+If you are using MySQL, be sure to use the ``READ COMMITTED`` isolation level
+rather than ``REPEATABLE READ`` (the default), otherwise you may see cases
+where ``get_or_create`` will raise an :exc:`~django.db.IntegrityError` but the
+object won't appear in a subsequent :meth:`~django.db.models.query.QuerySet.get`
+call.
+
+Finally, a word on using ``get_or_create()`` in Django views: please make sure
+to use it only in ``POST`` requests unless you have a good reason not to
+``GET`` requests shouldn't have any effect on data; use ``POST`` whenever a
+request to a page as a side effect on your data. For more, see `Safe methods`_
+in the HTTP spec.
.. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
@@ -1451,7 +1493,7 @@
contrast, ``iterator()`` will read results directly, without doing any caching
at the ``QuerySet`` level (internally, the default iterator calls ``iterator()``
and caches the return value). For a ``QuerySet`` which returns a large number of
-objects that you only need to access once, this can results in better
+objects that you only need to access once, this can result in better
performance and a significant reduction in memory.
Note that using ``iterator()`` on a ``QuerySet`` which has already been
diff --git a/lib/django-1.5/docs/ref/models/relations.txt b/lib/django-1.5/docs/ref/models/relations.txt
index 37986ec..c693f5f 100644
--- a/lib/django-1.5/docs/ref/models/relations.txt
+++ b/lib/django-1.5/docs/ref/models/relations.txt
@@ -44,6 +44,14 @@
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.add(e) # Associates Entry e with Blog b.
+ In the example above, ``e.save()`` is called to perform the update.
+ Using ``add()`` with a many-to-many relationship, however, will not
+ call any ``save()`` methods, but rather create the relationships
+ using :meth:`QuerySet.bulk_create()
+ <django.db.models.query.QuerySet.bulk_create>`. If you need to execute
+ some custom logic when a relationship is created, listen to the
+ :data:`~django.db.models.signals.m2m_changed` signal.
+
.. method:: create(**kwargs)
Creates a new object, saves it and puts it in the related object set.
@@ -82,14 +90,21 @@
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.remove(e) # Disassociates Entry e from Blog b.
- In order to prevent database inconsistency, this method only exists on
- :class:`~django.db.models.ForeignKey` objects where ``null=True``. If
- the related field can't be set to ``None`` (``NULL``), then an object
- can't be removed from a relation without being added to another. In the
- above example, removing ``e`` from ``b.entry_set()`` is equivalent to
- doing ``e.blog = None``, and because the ``blog``
- :class:`~django.db.models.ForeignKey` doesn't have ``null=True``, this
- is invalid.
+ Similar to :meth:`add()`, ``e.save()`` is called in the example above
+ to perform the update. Using ``remove()`` with a many-to-many
+ relationship, however, will delete the relationships using
+ :meth:`QuerySet.delete()<django.db.models.query.QuerySet.delete>` which
+ means no model ``save()`` methods are called; listen to the
+ :data:`~django.db.models.signals.m2m_changed` signal if you wish to
+ execute custom code when a relationship is deleted.
+
+ For :class:`~django.db.models.ForeignKey` objects, this method only
+ exists if ``null=True``. If the related field can't be set to ``None``
+ (``NULL``), then an object can't be removed from a relation without
+ being added to another. In the above example, removing ``e`` from
+ ``b.entry_set()`` is equivalent to doing ``e.blog = None``, and because
+ the ``blog`` :class:`~django.db.models.ForeignKey` doesn't have
+ ``null=True``, this is invalid.
.. method:: clear()
diff --git a/lib/django-1.5/docs/ref/request-response.txt b/lib/django-1.5/docs/ref/request-response.txt
index faf8708..da02fb7 100644
--- a/lib/django-1.5/docs/ref/request-response.txt
+++ b/lib/django-1.5/docs/ref/request-response.txt
@@ -169,10 +169,11 @@
.. attribute:: HttpRequest.user
- A ``django.contrib.auth.models.User`` object representing the currently
+ An object of type :setting:`AUTH_USER_MODEL` representing the currently
logged-in user. If the user isn't currently logged in, ``user`` will be set
- to an instance of ``django.contrib.auth.models.AnonymousUser``. You
- can tell them apart with ``is_authenticated()``, like so::
+ to an instance of :class:`django.contrib.auth.models.AnonymousUser`. You
+ can tell them apart with
+ :meth:`~django.contrib.auth.models.User.is_authenticated`, like so::
if request.user.is_authenticated():
# Do something for logged-in users.
@@ -180,8 +181,8 @@
# Do something for anonymous users.
``user`` is only available if your Django installation has the
- ``AuthenticationMiddleware`` activated. For more, see
- :doc:`/topics/auth/index`.
+ :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
+ activated. For more, see :doc:`/topics/auth/index`.
.. attribute:: HttpRequest.session
@@ -503,6 +504,26 @@
>>> q.lists()
[(u'a', [u'1', u'2', u'3'])]
+.. method:: QueryDict.pop(key)
+
+ Returns a list of values for the given key and removes them from the
+ dictionary. Raises ``KeyError`` if the key does not exist. For example::
+
+ >>> q = QueryDict('a=1&a=2&a=3', mutable=True)
+ >>> q.pop('a')
+ [u'1', u'2', u'3']
+
+.. method:: QueryDict.popitem()
+
+ Removes an arbitrary member of the dictionary (since there's no concept
+ of ordering), and returns a two value tuple containing the key and a list
+ of all values for the key. Raises ``KeyError`` when called on an empty
+ dictionary. For example::
+
+ >>> q = QueryDict('a=1&a=2&a=3', mutable=True)
+ >>> q.popitem()
+ (u'a', [u'1', u'2', u'3'])
+
.. method:: QueryDict.dict()
.. versionadded:: 1.4
@@ -708,7 +729,7 @@
.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly
-.. method:: HttpResponse.set_signed_cookie(key, value='', salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
+.. method:: HttpResponse.set_signed_cookie(key, value, salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
.. versionadded:: 1.4
diff --git a/lib/django-1.5/docs/ref/settings.txt b/lib/django-1.5/docs/ref/settings.txt
index 8624b9e..1db9b40 100644
--- a/lib/django-1.5/docs/ref/settings.txt
+++ b/lib/django-1.5/docs/ref/settings.txt
@@ -823,6 +823,10 @@
turned on, Django will remember every SQL query it executes. This is useful
when you're debugging, but it'll rapidly consume memory on a production server.
+Finally, if :setting:`DEBUG` is ``False``, you also need to properly set
+the :setting:`ALLOWED_HOSTS` setting. Failing to do so will result in all
+requests being returned as "Bad Request (400)".
+
.. _django/views/debug.py: https://github.com/django/django/blob/master/django/views/debug.py
DEBUG_PROPAGATE_EXCEPTIONS
@@ -1224,8 +1228,22 @@
Default: ``'en-us'``
A string representing the language code for this installation. This should be in
-standard :term:`language format<language code>`. For example, U.S. English is
-``"en-us"``. See :doc:`/topics/i18n/index`.
+standard :term:`language ID format <language code>`. For example, U.S. English
+is ``"en-us"``. See also the `list of language identifiers`_ and
+:doc:`/topics/i18n/index`.
+
+:setting:`USE_I18N` must be active for this setting to have any effect.
+
+It serves two purposes:
+
+* If the locale middleware isn't in use, it decides which translation is served
+ to all users.
+* If the locale middleware is active, it provides the fallback translation when
+ no translation exist for a given literal to the user's preferred language.
+
+See :ref:`how-django-discovers-language-preference` for more details.
+
+.. _list of language identifiers: http://www.i18nguy.com/unicode/language-identifiers.html
.. setting:: LANGUAGE_COOKIE_NAME
@@ -1250,9 +1268,9 @@
.. _online source: https://github.com/django/django/blob/master/django/conf/global_settings.py
-The list is a tuple of two-tuples in the format ``(language code, language
-name)``, the ``language code`` part should be a
-:term:`language name<language code>` -- for example, ``('ja', 'Japanese')``.
+The list is a tuple of two-tuples in the format
+(:term:`language code<language code>`, ``language name``) -- for example,
+``('ja', 'Japanese')``.
This specifies which languages are available for language selection. See
:doc:`/topics/i18n/index`.
@@ -1420,12 +1438,14 @@
MESSAGE_LEVEL
-------------
-Default: `messages.INFO`
+Default: ``messages.INFO``
Sets the minimum message level that will be recorded by the messages
framework. See the :doc:`messages documentation </ref/contrib/messages>` for
more details.
+.. setting:: MESSAGE_STORAGE
+
MESSAGE_STORAGE
---------------
@@ -1799,7 +1819,7 @@
Default: ``django.contrib.sessions.backends.db``
-Controls where Django stores session data. Valid values are:
+Controls where Django stores session data. Included engines are:
* ``'django.contrib.sessions.backends.db'``
* ``'django.contrib.sessions.backends.file'``
@@ -1841,6 +1861,30 @@
Whether to save the session data on every request. See
:doc:`/topics/http/sessions`.
+.. setting:: SESSION_SERIALIZER
+
+SESSION_SERIALIZER
+------------------
+
+.. versionadded:: 1.5.3
+
+Default: ``'django.contrib.sessions.serializers.PickleSerializer'``
+
+Full import path of a serializer class to use for serializing session data.
+Included serializers are:
+
+* ``'django.contrib.sessions.serializers.PickleSerializer'``
+* ``'django.contrib.sessions.serializers.JSONSerializer'``
+
+See :ref:`session_serialization` for details, including a warning regarding
+possible remote code execution when using
+:class:`~django.contrib.sessions.serializers.PickleSerializer`.
+
+In Django 1.5.3, the default in newly created projects using
+:djadmin:`django-admin.py startproject <startproject>` is
+:class:`django.contrib.sessions.serializers.JSONSerializer`, and the global
+default will switch to this class in Django 1.6.
+
.. setting:: SHORT_DATE_FORMAT
SHORT_DATE_FORMAT
@@ -1912,7 +1956,7 @@
If the :doc:`staticfiles</ref/contrib/staticfiles>` contrib app is enabled
(default) the :djadmin:`collectstatic` management command will collect static
files into this directory. See the howto on :doc:`managing static
-files</howto/static-files>` for more details about usage.
+files</howto/static-files/index>` for more details about usage.
.. warning::
diff --git a/lib/django-1.5/docs/ref/signals.txt b/lib/django-1.5/docs/ref/signals.txt
index 37477bb..7cce1b7 100644
--- a/lib/django-1.5/docs/ref/signals.txt
+++ b/lib/django-1.5/docs/ref/signals.txt
@@ -255,7 +255,7 @@
``pk_set``
For the ``pre_add``, ``post_add``, ``pre_remove`` and ``post_remove``
- actions, this is a list of primary key values that have been added to
+ actions, this is a set of primary key values that have been added to
or removed from the relation.
For the ``pre_clear`` and ``post_clear`` actions, this is ``None``.
@@ -284,7 +284,7 @@
and then did something like this::
- >>> p = Pizza.object.create(...)
+ >>> p = Pizza.objects.create(...)
>>> t = Topping.objects.create(...)
>>> p.toppings.add(t)
@@ -307,7 +307,7 @@
``model`` ``Topping`` (the class of the objects added to the
``Pizza``)
-``pk_set`` ``[t.id]`` (since only ``Topping t`` was added to the relation)
+``pk_set`` ``set([t.id])`` (since only ``Topping t`` was added to the relation)
``using`` ``"default"`` (since the default router sends writes here)
============== ============================================================
@@ -334,7 +334,7 @@
``model`` ``Pizza`` (the class of the objects removed from the
``Topping``)
-``pk_set`` ``[p.id]`` (since only ``Pizza p`` was removed from the
+``pk_set`` ``set([p.id])`` (since only ``Pizza p`` was removed from the
relation)
``using`` ``"default"`` (since the default router sends writes here)
@@ -447,17 +447,20 @@
Sent when Django finishes processing an HTTP request.
-.. note::
-
- When a view returns a :ref:`streaming response <httpresponse-streaming>`,
- this signal is sent only after the entire response is consumed by the
- client (strictly speaking, by the WSGI gateway).
-
.. versionchanged:: 1.5
- Before Django 1.5, this signal was fired before sending the content to the
- client. In order to accomodate streaming responses, it is now fired after
- sending the content.
+ Before Django 1.5, this signal was sent before delivering content to the
+ client. In order to accommodate :ref:`streaming responses
+ <httpresponse-streaming>`, it is now sent after the response has been fully
+ delivered to the client.
+
+.. note::
+
+ Some WSGI servers and middleware do not always call ``close`` on the
+ response object after handling a request, most notably uWSGI prior to 1.2.6
+ and Sentry's error reporting middleware up to 2.0.7. In those cases this
+ signal isn't sent at all. This can result in idle connections to database
+ and memcache servers.
Arguments sent with this signal:
diff --git a/lib/django-1.5/docs/ref/template-response.txt b/lib/django-1.5/docs/ref/template-response.txt
index 844b5fa..5c13ec7 100644
--- a/lib/django-1.5/docs/ref/template-response.txt
+++ b/lib/django-1.5/docs/ref/template-response.txt
@@ -120,7 +120,7 @@
rendered :class:`~django.template.response.SimpleTemplateResponse`
instance.
- If the callback returns a value that is not `None`, this will be
+ If the callback returns a value that is not ``None``, this will be
used as the response instead of the original response object (and
will be passed to the next post rendering callback etc.)
diff --git a/lib/django-1.5/docs/ref/templates/api.txt b/lib/django-1.5/docs/ref/templates/api.txt
index 54f578a..f77455d 100644
--- a/lib/django-1.5/docs/ref/templates/api.txt
+++ b/lib/django-1.5/docs/ref/templates/api.txt
@@ -2,6 +2,9 @@
The Django template language: For Python programmers
====================================================
+.. module:: django.template
+ :synopsis: Django's template system
+
This document explains the Django template system from a technical
perspective -- how it works and how to extend it. If you're just looking for
reference on the language syntax, see :doc:`/topics/templates`.
@@ -52,7 +55,7 @@
Using the template system
=========================
-.. class:: django.template.Template
+.. class:: Template
Using the template system in Python is a two-step process:
@@ -221,13 +224,13 @@
self.database_record.delete()
sensitive_function.alters_data = True
-* .. versionadded:: 1.4
- Occasionally you may want to turn off this feature for other reasons,
- and tell the template system to leave a variable un-called no matter
- what. To do so, set a ``do_not_call_in_templates`` attribute on the
- callable with the value ``True``. The template system then will act as
- if your variable is not callable (allowing you to access attributes of
- the callable, for example).
+.. versionadded:: 1.4
+ Occasionally you may want to turn off this feature for other reasons,
+ and tell the template system to leave a variable un-called no matter
+ what. To do so, set a ``do_not_call_in_templates`` attribute on the
+ callable with the value ``True``. The template system then will act as
+ if your variable is not callable (allowing you to access attributes of
+ the callable, for example).
.. _invalid-template-variables:
@@ -279,7 +282,7 @@
Playing with Context objects
----------------------------
-.. class:: django.template.Context
+.. class:: Context
Most of the time, you'll instantiate ``Context`` objects by passing in a
fully-populated dictionary to ``Context()``. But you can add and delete items
@@ -296,9 +299,9 @@
>>> c['newvariable']
'hello'
-.. method:: pop()
-.. method:: push()
-.. exception:: django.template.ContextPopException
+.. method:: Context.pop()
+.. method:: Context.push()
+.. exception:: ContextPopException
A ``Context`` object is a stack. That is, you can ``push()`` and ``pop()`` it.
If you ``pop()`` too much, it'll raise
@@ -347,7 +350,7 @@
Subclassing Context: RequestContext
-----------------------------------
-.. class:: django.template.RequestContext
+.. class:: RequestContext
Django comes with a special ``Context`` class,
``django.template.RequestContext``, that acts slightly differently than the
@@ -443,6 +446,8 @@
``django.contrib.auth.context_processors.PermWrapper``, representing the
permissions that the currently logged-in user has.
+.. currentmodule:: django.core.context_processors
+
django.core.context_processors.debug
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -479,7 +484,7 @@
django.core.context_processors.static
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. function:: django.core.context_processors.static
+.. function:: static
If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every
``RequestContext`` will contain a variable ``STATIC_URL``, providing the
diff --git a/lib/django-1.5/docs/ref/templates/builtins.txt b/lib/django-1.5/docs/ref/templates/builtins.txt
index fadf2db..1572b15 100644
--- a/lib/django-1.5/docs/ref/templates/builtins.txt
+++ b/lib/django-1.5/docs/ref/templates/builtins.txt
@@ -835,6 +835,8 @@
* New York: 20,000,000
* India
* Calcutta: 15,000,000
+* USA
+ * Chicago: 7,000,000
* Japan
* Tokyo: 33,000,000
@@ -2162,7 +2164,7 @@
urlize
^^^^^^
-Converts URLs in text into clickable links.
+Converts URLs and email addresses in text into clickable links.
This template tag works on links prefixed with ``http://``, ``https://``, or
``www.``. For example, ``http://goo.gl/aia1t`` will get converted but
@@ -2191,6 +2193,11 @@
``"Check out <a href="http://www.djangoproject.com"
rel="nofollow">www.djangoproject.com</a>"``.
+In addition to web links, ``urlize`` also converts email addresses into
+``mailto:`` links. If ``value`` is
+``"Send questions to foo@example.com"``, the output will be
+``"Send questions to <a href="mailto:foo@example.com">foo@example</a>"``.
+
The ``urlize`` filter also takes an optional parameter ``autoescape``. If
``autoescape`` is ``True``, the link text and URLs will be escaped using
Django's built-in :tfilter:`escape` filter. The default value for
@@ -2206,7 +2213,7 @@
urlizetrunc
^^^^^^^^^^^
-Converts URLs into clickable links just like urlize_, but truncates URLs
+Converts URLs and email addresses into clickable links just like urlize_, but truncates URLs
longer than the given character limit.
**Argument:** Number of characters that link text should be truncated to,
@@ -2373,7 +2380,7 @@
<link rel="stylesheet" href="{% static user_stylesheet %}" type="text/css" media="screen" />
If you'd like to retrieve a static URL without displaying it, you can use a
-slightly different call::
+slightly different call:
.. versionadded:: 1.5
@@ -2388,8 +2395,10 @@
The :mod:`staticfiles<django.contrib.staticfiles>` contrib app also ships
with a :ttag:`static template tag<staticfiles-static>` which uses
``staticfiles'`` :setting:`STATICFILES_STORAGE` to build the URL of the
- given path. Use that instead if you have an advanced use case such as
- :ref:`using a cloud service to serve static files<staticfiles-from-cdn>`::
+ given path (rather than simply using :func:`urlparse.urljoin` with the
+ :setting:`STATIC_URL` setting and the given path). Use that instead if you
+ have an advanced use case such as :ref:`using a cloud service to serve
+ static files<staticfiles-from-cdn>`::
{% load static from staticfiles %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />
diff --git a/lib/django-1.5/docs/ref/unicode.txt b/lib/django-1.5/docs/ref/unicode.txt
index 784ff33..3743d0a 100644
--- a/lib/django-1.5/docs/ref/unicode.txt
+++ b/lib/django-1.5/docs/ref/unicode.txt
@@ -240,6 +240,13 @@
Choosing between ``__str__()`` and ``__unicode__()``
----------------------------------------------------
+.. note::
+
+ If you are on Python 3, you can skip this section because you'll always
+ create ``__str__()`` rather than ``__unicode__()``. If you'd like
+ compatibility with Python 2, you can decorate your model class with
+ :func:`~django.utils.encoding.python_2_unicode_compatible`.
+
One consequence of using Unicode by default is that you have to take some care
when printing data from the model.
diff --git a/lib/django-1.5/docs/ref/urls.txt b/lib/django-1.5/docs/ref/urls.txt
index 77dcc24..c0542ef 100644
--- a/lib/django-1.5/docs/ref/urls.txt
+++ b/lib/django-1.5/docs/ref/urls.txt
@@ -31,12 +31,12 @@
:ref:`Passing extra options to view functions <views-extra-options>`.
.. note::
- Because `patterns()` is a function call, it accepts a maximum of 255
+ Because ``patterns()`` is a function call, it accepts a maximum of 255
arguments (URL patterns, in this case). This is a limit for all Python
function calls. This is rarely a problem in practice, because you'll
- typically structure your URL patterns modularly by using `include()`
+ typically structure your URL patterns modularly by using ``include()``
sections. However, on the off-chance you do hit the 255-argument limit,
- realize that `patterns()` returns a Python list, so you can split up the
+ realize that ``patterns()`` returns a Python list, so you can split up the
construction of the list.
::
@@ -52,6 +52,20 @@
patterns you can construct. The only limit is that you can only create 254
at a time (the 255th argument is the initial prefix argument).
+static()
+--------
+
+.. function:: static.static(prefix, view='django.views.static.serve', **kwargs)
+
+Helper function to return a URL pattern for serving files in debug mode::
+
+ from django.conf import settings
+ from django.conf.urls.static import static
+
+ urlpatterns = patterns('',
+ # ... the rest of your URLconf goes here ...
+ ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+
url()
-----
diff --git a/lib/django-1.5/docs/ref/utils.txt b/lib/django-1.5/docs/ref/utils.txt
index 35d7653..5d234e7 100644
--- a/lib/django-1.5/docs/ref/utils.txt
+++ b/lib/django-1.5/docs/ref/utils.txt
@@ -441,7 +441,7 @@
.. class:: Atom1Feed(SyndicationFeed)
- Spec: http://www.atomenabled.org/developers/syndication/atom-format-spec.php
+ Spec: http://tools.ietf.org/html/rfc4287
``django.utils.functional``
===========================
@@ -449,6 +449,64 @@
.. module:: django.utils.functional
:synopsis: Functional programming tools.
+.. class:: cached_property(object)
+
+ The ``@cached_property`` decorator caches the result of a method with a
+ single ``self`` argument as a property. The cached result will persist
+ as long as the instance does, so if the instance is passed around and the
+ function subsequently invoked, the cached result will be returned.
+
+ Consider a typical case, where a view might need to call a model's method
+ to perform some computation, before placing the model instance into the
+ context, where the template might invoke the method once more::
+
+ # the model
+ class Person(models.Model):
+
+ def friends(self):
+ # expensive computation
+ ...
+ return friends
+
+ # in the view:
+ if person.friends():
+
+ # in the template:
+ {% for friend in person.friends %}
+
+ Here, ``friends()`` will be called twice. Since the instance ``person`` in
+ the view and the template are the same, ``@cached_property`` can avoid
+ that::
+
+ from django.utils.functional import cached_property
+
+ @cached_property
+ def friends(self):
+ # expensive computation
+ ...
+ return friends
+
+ Note that as the method is now a property, in Python code it will need to
+ be invoked appropriately::
+
+ # in the view:
+ if person.friends:
+
+ The cached value can be treated like an ordinary attribute of the instance::
+
+ # clear it, requiring re-computation next time it's called
+ del person.friends # or delattr(person, "friends")
+
+ # set a value manually, that will persist on the instance until cleared
+ person.friends = ["Huckleberry Finn", "Tom Sawyer"]
+
+ As well as offering potential performance advantages, ``@cached_property``
+ can ensure that an attribute's value does not change unexpectedly over the
+ life of an instance. This could occur with a method whose computation is
+ based on ``datetime.now()``, or simply if a change were saved to the
+ database by some other process in the brief interval between subsequent
+ invocations of a method on the same instance.
+
.. function:: allow_lazy(func, *resultclasses)
Django offers many utility functions (particularly in ``django.utils``) that
@@ -570,11 +628,11 @@
.. function:: remove_tags(value, tags)
- Removes a list of [X]HTML tag names from the output.
+ Removes a space-separated list of [X]HTML tag names from the output.
For example::
- remove_tags(value, ["b", "span"])
+ remove_tags(value, "b span")
If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"`` the
return value will be ``"Joel <button>is</button> a slug"``.
diff --git a/lib/django-1.5/docs/ref/views.txt b/lib/django-1.5/docs/ref/views.txt
new file mode 100644
index 0000000..3753f83
--- /dev/null
+++ b/lib/django-1.5/docs/ref/views.txt
@@ -0,0 +1,48 @@
+==============
+Built-in Views
+==============
+
+.. module:: django.views
+ :synopsis: Django's built-in views.
+
+Several of Django's built-in views are documented in
+:doc:`/topics/http/views` as well as elsewhere in the documentation.
+
+Serving files in development
+----------------------------
+
+.. function:: static.serve(request, path, document_root, show_indexes=False)
+
+There may be files other than your project's static assets that, for
+convenience, you'd like to have Django serve for you in local development.
+The :func:`~django.views.static.serve` view can be used to serve any directory
+you give it. (This view is **not** hardened for production use and should be
+used only as a development aid; you should serve these files in production
+using a real front-end webserver).
+
+The most likely example is user-uploaded content in :setting:`MEDIA_ROOT`.
+``django.contrib.staticfiles`` is intended for static assets and has no
+built-in handling for user-uploaded files, but you can have Django serve your
+:setting:`MEDIA_ROOT` by appending something like this to your URLconf::
+
+ from django.conf import settings
+
+ # ... the rest of your URLconf goes here ...
+
+ if settings.DEBUG:
+ urlpatterns += patterns('',
+ url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
+ 'document_root': settings.MEDIA_ROOT,
+ }),
+ )
+
+Note, the snippet assumes your :setting:`MEDIA_URL` has a value of
+``'/media/'``. This will call the :func:`~django.views.static.serve` view,
+passing in the path from the URLconf and the (required) ``document_root``
+parameter.
+
+Since it can become a bit cumbersome to define this URL pattern, Django
+ships with a small URL helper function :func:`~django.conf.urls.static.static`
+that takes as parameters the prefix such as :setting:`MEDIA_URL` and a dotted
+path to a view, such as ``'django.views.static.serve'``. Any other function
+parameter will be transparently passed to the view.
diff --git a/lib/django-1.5/docs/releases/1.2-alpha-1.txt b/lib/django-1.5/docs/releases/1.2-alpha-1.txt
index 16e1940..8c905f6 100644
--- a/lib/django-1.5/docs/releases/1.2-alpha-1.txt
+++ b/lib/django-1.5/docs/releases/1.2-alpha-1.txt
@@ -428,7 +428,7 @@
Django 1.2 adds the ability to use :doc:`more than one database
</topics/db/multi-db>` in your Django project. Queries can be
-issued at a specific database with the `using()` method on
+issued at a specific database with the ``using()`` method on
querysets; individual objects can be saved to a specific database
by providing a ``using`` argument when you save the instance.
diff --git a/lib/django-1.5/docs/releases/1.2.txt b/lib/django-1.5/docs/releases/1.2.txt
index 50c049f..ad39062 100644
--- a/lib/django-1.5/docs/releases/1.2.txt
+++ b/lib/django-1.5/docs/releases/1.2.txt
@@ -123,9 +123,9 @@
Django 1.2 adds the ability to use :doc:`more than one database
</topics/db/multi-db>` in your Django project. Queries can be issued at a
-specific database with the `using()` method on ``QuerySet`` objects. Individual
-objects can be saved to a specific database by providing a ``using`` argument
-when you call ``save()``.
+specific database with the ``using()`` method on ``QuerySet`` objects.
+Individual objects can be saved to a specific database by providing a ``using``
+argument when you call ``save()``.
Model validation
----------------
@@ -765,7 +765,7 @@
Code taking advantage of any of the features below will raise a
``PendingDeprecationWarning`` in Django 1.2. This warning will be
silent by default, but may be turned on using Python's :mod:`warnings`
-module, or by running Python with a ``-Wd`` or `-Wall` flag.
+module, or by running Python with a ``-Wd`` or ``-Wall`` flag.
In Django 1.3, these warnings will become a ``DeprecationWarning``,
which is *not* silent. In Django 1.4 support for these features will
diff --git a/lib/django-1.5/docs/releases/1.3-alpha-1.txt b/lib/django-1.5/docs/releases/1.3-alpha-1.txt
index ba8a4fc..80e260f 100644
--- a/lib/django-1.5/docs/releases/1.3-alpha-1.txt
+++ b/lib/django-1.5/docs/releases/1.3-alpha-1.txt
@@ -72,7 +72,7 @@
See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
for more details or learn how to :doc:`manage static files
-</howto/static-files>`.
+</howto/static-files/index>`.
``unittest2`` support
~~~~~~~~~~~~~~~~~~~~~
@@ -279,7 +279,7 @@
Code taking advantage of any of the features below will raise a
``PendingDeprecationWarning`` in Django 1.3. This warning will be
silent by default, but may be turned on using Python's :mod:`warnings`
-module, or by running Python with a ``-Wd`` or `-Wall` flag.
+module, or by running Python with a ``-Wd`` or ``-Wall`` flag.
In Django 1.4, these warnings will become a ``DeprecationWarning``,
which is *not* silent. In Django 1.5 support for these features will
diff --git a/lib/django-1.5/docs/releases/1.3-beta-1.txt b/lib/django-1.5/docs/releases/1.3-beta-1.txt
index 14897ed..aa7800b 100644
--- a/lib/django-1.5/docs/releases/1.3-beta-1.txt
+++ b/lib/django-1.5/docs/releases/1.3-beta-1.txt
@@ -37,7 +37,7 @@
See the :doc:`staticfiles reference documentation </ref/contrib/staticfiles>`
for more details, or learn :doc:`how to manage static files
-</howto/static-files>`.
+</howto/static-files/index>`.
Translation comments
~~~~~~~~~~~~~~~~~~~~
@@ -154,7 +154,7 @@
In Django 1.3 we're taking a new approach to this problem, implemented
as a pair of changes:
-* The choice list for `USStateField` has changed. Previously, it
+* The choice list for ``USStateField`` has changed. Previously, it
consisted of the 50 U.S. states, the District of Columbia and
U.S. overseas territories. As of Django 1.3 it includes all previous
choices, plus the U.S. Armed Forces postal codes.
@@ -163,7 +163,7 @@
``django.contrib.localflavor.us.models.USPostalCodeField``, has
been added which draws its choices from a list of all postal
abbreviations recognized by the U.S Postal Service. This includes
- all abbreviations recognized by `USStateField`, plus three
+ all abbreviations recognized by ``USStateField``, plus three
independent nations -- the Federated States of Micronesia, the
Republic of the Marshall Islands and the Republic of Palau -- which
are serviced under treaty by the U.S. postal system. A new form
@@ -176,7 +176,7 @@
system. Consult the ``django.contrib.localflavor`` documentation
for more details.
-The change to `USStateField` is technically backwards-incompatible for
+The change to ``USStateField`` is technically backwards-incompatible for
users who expect this field to exclude Armed Forces locations. If you
need to support U.S. mailing addresses without Armed Forces locations,
see the list of choice tuples available in the localflavor
diff --git a/lib/django-1.5/docs/releases/1.3.3.txt b/lib/django-1.5/docs/releases/1.3.3.txt
new file mode 100644
index 0000000..437cbfb
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.3.3.txt
@@ -0,0 +1,11 @@
+==========================
+Django 1.3.3 release notes
+==========================
+
+*August 1, 2012*
+
+Following Monday's security release of :doc:`Django 1.3.2 </releases/1.3.2>`,
+we began receiving reports that one of the fixes applied was breaking Python
+2.4 compatibility for Django 1.3. Since Python 2.4 is a supported Python
+version for that release series, this release fixes compatibility with
+Python 2.4.
diff --git a/lib/django-1.5/docs/releases/1.3.4.txt b/lib/django-1.5/docs/releases/1.3.4.txt
new file mode 100644
index 0000000..3a174b3
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.3.4.txt
@@ -0,0 +1,37 @@
+==========================
+Django 1.3.4 release notes
+==========================
+
+*October 17, 2012*
+
+This is the fourth release in the Django 1.3 series.
+
+Host header poisoning
+---------------------
+
+Some parts of Django -- independent of end-user-written applications -- make
+use of full URLs, including domain name, which are generated from the HTTP Host
+header. Some attacks against this are beyond Django's ability to control, and
+require the web server to be properly configured; Django's documentation has
+for some time contained notes advising users on such configuration.
+
+Django's own built-in parsing of the Host header is, however, still vulnerable,
+as was reported to us recently. The Host header parsing in Django 1.3.3 and
+Django 1.4.1 -- specifically, ``django.http.HttpRequest.get_host()`` -- was
+incorrectly handling username/password information in the header. Thus, for
+example, the following Host header would be accepted by Django when running on
+"validsite.com"::
+
+ Host: validsite.com:random@evilsite.com
+
+Using this, an attacker can cause parts of Django -- particularly the
+password-reset mechanism -- to generate and display arbitrary URLs to users.
+
+To remedy this, the parsing in ``HttpRequest.get_host()`` is being modified;
+Host headers which contain potentially dangerous content (such as
+username/password pairs) now raise the exception
+:exc:`django.core.exceptions.SuspiciousOperation`.
+
+Details of this issue were initially posted online as a `security advisory`_.
+
+.. _security advisory: https://www.djangoproject.com/weblog/2012/oct/17/security/
diff --git a/lib/django-1.5/docs/releases/1.3.5.txt b/lib/django-1.5/docs/releases/1.3.5.txt
new file mode 100644
index 0000000..65c4032
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.3.5.txt
@@ -0,0 +1,60 @@
+==========================
+Django 1.3.5 release notes
+==========================
+
+*December 10, 2012*
+
+Django 1.3.5 addresses two security issues present in previous Django releases
+in the 1.3 series.
+
+Please be aware that this security release is slightly different from previous
+ones. Both issues addressed here have been dealt with in prior security updates
+to Django. In one case, we have received ongoing reports of problems, and in
+the other we've chosen to take further steps to tighten up Django's code in
+response to independent discovery of potential problems from multiple sources.
+
+Host header poisoning
+---------------------
+
+Several earlier Django security releases focused on the issue of poisoning the
+HTTP Host header, causing Django to generate URLs pointing to arbitrary,
+potentially-malicious domains.
+
+In response to further input received and reports of continuing issues
+following the previous release, we're taking additional steps to tighten Host
+header validation. Rather than attempt to accommodate all features HTTP
+supports here, Django's Host header validation attempts to support a smaller,
+but far more common, subset:
+
+* Hostnames must consist of characters [A-Za-z0-9] plus hyphen ('-') or dot
+ ('.').
+* IP addresses -- both IPv4 and IPv6 -- are permitted.
+* Port, if specified, is numeric.
+
+Any deviation from this will now be rejected, raising the exception
+:exc:`django.core.exceptions.SuspiciousOperation`.
+
+Redirect poisoning
+------------------
+
+Also following up on a previous issue: in July of this year, we made changes to
+Django's HTTP redirect classes, performing additional validation of the scheme
+of the URL to redirect to (since, both within Django's own supplied
+applications and many third-party applications, accepting a user-supplied
+redirect target is a common pattern).
+
+Since then, two independent audits of the code turned up further potential
+problems. So, similar to the Host-header issue, we are taking steps to provide
+tighter validation in response to reported problems (primarily with third-party
+applications, but to a certain extent also within Django itself). This comes in
+two parts:
+
+1. A new utility function, ``django.utils.http.is_safe_url``, is added; this
+function takes a URL and a hostname, and checks that the URL is either
+relative, or if absolute matches the supplied hostname. This function is
+intended for use whenever user-supplied redirect targets are accepted, to
+ensure that such redirects cannot lead to arbitrary third-party sites.
+
+2. All of Django's own built-in views -- primarily in the authentication system
+-- which allow user-supplied redirect targets now use ``is_safe_url`` to
+validate the supplied URL.
diff --git a/lib/django-1.5/docs/releases/1.3.6.txt b/lib/django-1.5/docs/releases/1.3.6.txt
new file mode 100644
index 0000000..d55199a
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.3.6.txt
@@ -0,0 +1,78 @@
+==========================
+Django 1.3.6 release notes
+==========================
+
+*February 19, 2013*
+
+Django 1.3.6 fixes four security issues present in previous Django releases in
+the 1.3 series.
+
+This is the sixth bugfix/security release in the Django 1.3 series.
+
+
+Host header poisoning
+---------------------
+
+Some parts of Django -- independent of end-user-written applications -- make
+use of full URLs, including domain name, which are generated from the HTTP Host
+header. Django's documentation has for some time contained notes advising users
+on how to configure webservers to ensure that only valid Host headers can reach
+the Django application. However, it has been reported to us that even with the
+recommended webserver configurations there are still techniques available for
+tricking many common webservers into supplying the application with an
+incorrect and possibly malicious Host header.
+
+For this reason, Django 1.3.6 adds a new setting, ``ALLOWED_HOSTS``, which
+should contain an explicit list of valid host/domain names for this site. A
+request with a Host header not matching an entry in this list will raise
+``SuspiciousOperation`` if ``request.get_host()`` is called. For full details
+see the documentation for the :setting:`ALLOWED_HOSTS` setting.
+
+The default value for this setting in Django 1.3.6 is ``['*']`` (matching any
+host), for backwards-compatibility, but we strongly encourage all sites to set
+a more restrictive value.
+
+This host validation is disabled when ``DEBUG`` is ``True`` or when running tests.
+
+
+XML deserialization
+-------------------
+
+The XML parser in the Python standard library is vulnerable to a number of
+attacks via external entities and entity expansion. Django uses this parser for
+deserializing XML-formatted database fixtures. The fixture deserializer is not
+intended for use with untrusted data, but in order to err on the side of safety
+in Django 1.3.6 the XML deserializer refuses to parse an XML document with a
+DTD (DOCTYPE definition), which closes off these attack avenues.
+
+These issues in the Python standard library are CVE-2013-1664 and
+CVE-2013-1665. More information available `from the Python security team`_.
+
+Django's XML serializer does not create documents with a DTD, so this should
+not cause any issues with the typical round-trip from ``dumpdata`` to
+``loaddata``, but if you feed your own XML documents to the ``loaddata``
+management command, you will need to ensure they do not contain a DTD.
+
+.. _from the Python security team: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
+
+
+Formset memory exhaustion
+-------------------------
+
+Previous versions of Django did not validate or limit the form-count data
+provided by the client in a formset's management form, making it possible to
+exhaust a server's available memory by forcing it to create very large numbers
+of forms.
+
+In Django 1.3.6, all formsets have a strictly-enforced maximum number of forms
+(1000 by default, though it can be set higher via the ``max_num`` formset
+factory argument).
+
+
+Admin history view information leakage
+--------------------------------------
+
+In previous versions of Django, an admin user without change permission on a
+model could still view the unicode representation of instances via their admin
+history log. Django 1.3.6 now limits the admin history log view for an object
+to users with change permission for that model.
diff --git a/lib/django-1.5/docs/releases/1.3.7.txt b/lib/django-1.5/docs/releases/1.3.7.txt
new file mode 100644
index 0000000..3cccfcf
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.3.7.txt
@@ -0,0 +1,13 @@
+==========================
+Django 1.3.7 release notes
+==========================
+
+*February 20, 2013*
+
+Django 1.3.7 corrects a packaging problem with yesterday's :doc:`1.3.6 release
+</releases/1.3.6>`.
+
+The release contained stray ``.pyc`` files that caused "bad magic number"
+errors when running with some versions of Python. This releases corrects this,
+and also fixes a bad documentation link in the project template ``settings.py``
+file generated by ``manage.py startproject``.
diff --git a/lib/django-1.5/docs/releases/1.3.txt b/lib/django-1.5/docs/releases/1.3.txt
index 4c8dd2f..543942e 100644
--- a/lib/django-1.5/docs/releases/1.3.txt
+++ b/lib/django-1.5/docs/releases/1.3.txt
@@ -115,7 +115,7 @@
See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
for more details or learn how to :doc:`manage static files
-</howto/static-files>`.
+</howto/static-files/index>`.
unittest2 support
~~~~~~~~~~~~~~~~~
@@ -652,6 +652,15 @@
and reset their password. In Django 1.3 inactive users will receive the same
message as a nonexistent account.
+Password reset view now accepts ``from_email``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :func:`django.contrib.auth.views.password_reset` view now accepts a
+``from_email`` parameter, which is passed to the ``password_reset_form``'s
+``save()`` method as a keyword argument. If you are using this view with a
+custom password reset form, then you will need to ensure your form's ``save()``
+method accepts this keyword argument.
+
.. _deprecated-features-1.3:
Features deprecated in 1.3
@@ -664,7 +673,7 @@
Code taking advantage of any of the features below will raise a
``PendingDeprecationWarning`` in Django 1.3. This warning will be
silent by default, but may be turned on using Python's :mod:`warnings`
-module, or by running Python with a ``-Wd`` or `-Wall` flag.
+module, or by running Python with a ``-Wd`` or ``-Wall`` flag.
In Django 1.4, these warnings will become a ``DeprecationWarning``,
which is *not* silent. In Django 1.5 support for these features will
diff --git a/lib/django-1.5/docs/releases/1.4-alpha-1.txt b/lib/django-1.5/docs/releases/1.4-alpha-1.txt
index 52d9b29..36b70ba 100644
--- a/lib/django-1.5/docs/releases/1.4-alpha-1.txt
+++ b/lib/django-1.5/docs/releases/1.4-alpha-1.txt
@@ -578,7 +578,7 @@
``/media/``) simply make sure :setting:`STATIC_URL` and :setting:`STATIC_ROOT`
are configured and your web server serves the files correctly. The development
server continues to serve the admin files just like before. Don't hesitate to
-consult the :doc:`static files howto </howto/static-files>` for further
+consult the :doc:`static files howto </howto/static-files/index>` for further
details.
In case your ``ADMIN_MEDIA_PREFIX`` is set to an specific domain (e.g.
@@ -877,7 +877,7 @@
The ``open`` method of the base Storage class took an obscure parameter
``mixin`` which allowed you to dynamically change the base classes of the
returned file object. This has been removed. In the rare case you relied on the
-`mixin` parameter, you can easily achieve the same by overriding the `open`
+``mixin`` parameter, you can easily achieve the same by overriding the ``open``
method, e.g.::
from django.core.files import File
diff --git a/lib/django-1.5/docs/releases/1.4-beta-1.txt b/lib/django-1.5/docs/releases/1.4-beta-1.txt
index b2e280c..dd5d9ab 100644
--- a/lib/django-1.5/docs/releases/1.4-beta-1.txt
+++ b/lib/django-1.5/docs/releases/1.4-beta-1.txt
@@ -646,7 +646,7 @@
``/media/``) simply make sure :setting:`STATIC_URL` and :setting:`STATIC_ROOT`
are configured and your web server serves the files correctly. The development
server continues to serve the admin files just like before. Don't hesitate to
-consult the :doc:`static files howto </howto/static-files>` for further
+consult the :doc:`static files howto </howto/static-files/index>` for further
details.
In case your ``ADMIN_MEDIA_PREFIX`` is set to an specific domain (e.g.
@@ -945,7 +945,7 @@
The ``open`` method of the base Storage class took an obscure parameter
``mixin`` which allowed you to dynamically change the base classes of the
returned file object. This has been removed. In the rare case you relied on the
-`mixin` parameter, you can easily achieve the same by overriding the `open`
+``mixin`` parameter, you can easily achieve the same by overriding the ``open``
method, e.g.::
from django.core.files import File
diff --git a/lib/django-1.5/docs/releases/1.4.2.txt b/lib/django-1.5/docs/releases/1.4.2.txt
index 07eec39..a6150f5 100644
--- a/lib/django-1.5/docs/releases/1.4.2.txt
+++ b/lib/django-1.5/docs/releases/1.4.2.txt
@@ -17,7 +17,7 @@
Django's own built-in parsing of the Host header is, however, still vulnerable,
as was reported to us recently. The Host header parsing in Django 1.3.3 and
-Django 1.4.1 -- specifically, django.http.HttpRequest.get_host() -- was
+Django 1.4.1 -- specifically, ``django.http.HttpRequest.get_host()`` -- was
incorrectly handling username/password information in the header. Thus, for
example, the following Host header would be accepted by Django when running on
"validsite.com"::
@@ -27,9 +27,10 @@
Using this, an attacker can cause parts of Django -- particularly the
password-reset mechanism -- to generate and display arbitrary URLs to users.
-To remedy this, the parsing in HttpRequest.get_host() is being modified; Host
-headers which contain potentially dangerous content (such as username/password
-pairs) now raise the exception django.core.exceptions.SuspiciousOperation
+To remedy this, the parsing in ``HttpRequest.get_host()`` is being modified;
+Host headers which contain potentially dangerous content (such as
+username/password pairs) now raise the exception
+:exc:`django.core.exceptions.SuspiciousOperation`.
Details of this issue were initially posted online as a `security advisory`_.
diff --git a/lib/django-1.5/docs/releases/1.4.3.txt b/lib/django-1.5/docs/releases/1.4.3.txt
new file mode 100644
index 0000000..aadf623
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.4.3.txt
@@ -0,0 +1,60 @@
+==========================
+Django 1.4.3 release notes
+==========================
+
+*December 10, 2012*
+
+Django 1.4.3 addresses two security issues present in previous Django releases
+in the 1.4 series.
+
+Please be aware that this security release is slightly different from previous
+ones. Both issues addressed here have been dealt with in prior security updates
+to Django. In one case, we have received ongoing reports of problems, and in
+the other we've chosen to take further steps to tighten up Django's code in
+response to independent discovery of potential problems from multiple sources.
+
+Host header poisoning
+---------------------
+
+Several earlier Django security releases focused on the issue of poisoning the
+HTTP Host header, causing Django to generate URLs pointing to arbitrary,
+potentially-malicious domains.
+
+In response to further input received and reports of continuing issues
+following the previous release, we're taking additional steps to tighten Host
+header validation. Rather than attempt to accommodate all features HTTP
+supports here, Django's Host header validation attempts to support a smaller,
+but far more common, subset:
+
+* Hostnames must consist of characters [A-Za-z0-9] plus hyphen ('-') or dot
+ ('.').
+* IP addresses -- both IPv4 and IPv6 -- are permitted.
+* Port, if specified, is numeric.
+
+Any deviation from this will now be rejected, raising the exception
+:exc:`django.core.exceptions.SuspiciousOperation`.
+
+Redirect poisoning
+------------------
+
+Also following up on a previous issue: in July of this year, we made changes to
+Django's HTTP redirect classes, performing additional validation of the scheme
+of the URL to redirect to (since, both within Django's own supplied
+applications and many third-party applications, accepting a user-supplied
+redirect target is a common pattern).
+
+Since then, two independent audits of the code turned up further potential
+problems. So, similar to the Host-header issue, we are taking steps to provide
+tighter validation in response to reported problems (primarily with third-party
+applications, but to a certain extent also within Django itself). This comes in
+two parts:
+
+1. A new utility function, ``django.utils.http.is_safe_url``, is added; this
+function takes a URL and a hostname, and checks that the URL is either
+relative, or if absolute matches the supplied hostname. This function is
+intended for use whenever user-supplied redirect targets are accepted, to
+ensure that such redirects cannot lead to arbitrary third-party sites.
+
+2. All of Django's own built-in views -- primarily in the authentication system
+-- which allow user-supplied redirect targets now use ``is_safe_url`` to
+validate the supplied URL.
diff --git a/lib/django-1.5/docs/releases/1.4.4.txt b/lib/django-1.5/docs/releases/1.4.4.txt
new file mode 100644
index 0000000..c5fcbc3
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.4.4.txt
@@ -0,0 +1,88 @@
+==========================
+Django 1.4.4 release notes
+==========================
+
+*February 19, 2013*
+
+Django 1.4.4 fixes four security issues present in previous Django releases in
+the 1.4 series, as well as several other bugs and numerous documentation
+improvements.
+
+This is the fourth bugfix/security release in the Django 1.4 series.
+
+
+Host header poisoning
+---------------------
+
+Some parts of Django -- independent of end-user-written applications -- make
+use of full URLs, including domain name, which are generated from the HTTP Host
+header. Django's documentation has for some time contained notes advising users
+on how to configure webservers to ensure that only valid Host headers can reach
+the Django application. However, it has been reported to us that even with the
+recommended webserver configurations there are still techniques available for
+tricking many common webservers into supplying the application with an
+incorrect and possibly malicious Host header.
+
+For this reason, Django 1.4.4 adds a new setting, ``ALLOWED_HOSTS``, containing
+an explicit list of valid host/domain names for this site. A request with a
+Host header not matching an entry in this list will raise
+``SuspiciousOperation`` if ``request.get_host()`` is called. For full details
+see the documentation for the :setting:`ALLOWED_HOSTS` setting.
+
+The default value for this setting in Django 1.4.4 is ``['*']`` (matching any
+host), for backwards-compatibility, but we strongly encourage all sites to set
+a more restrictive value.
+
+This host validation is disabled when ``DEBUG`` is ``True`` or when running tests.
+
+
+XML deserialization
+-------------------
+
+The XML parser in the Python standard library is vulnerable to a number of
+attacks via external entities and entity expansion. Django uses this parser for
+deserializing XML-formatted database fixtures. This deserializer is not
+intended for use with untrusted data, but in order to err on the side of safety
+in Django 1.4.4 the XML deserializer refuses to parse an XML document with a
+DTD (DOCTYPE definition), which closes off these attack avenues.
+
+These issues in the Python standard library are CVE-2013-1664 and
+CVE-2013-1665. More information available `from the Python security team`_.
+
+Django's XML serializer does not create documents with a DTD, so this should
+not cause any issues with the typical round-trip from ``dumpdata`` to
+``loaddata``, but if you feed your own XML documents to the ``loaddata``
+management command, you will need to ensure they do not contain a DTD.
+
+.. _from the Python security team: http://blog.python.org/2013/02/announcing-defusedxml-fixes-for-xml.html
+
+
+Formset memory exhaustion
+-------------------------
+
+Previous versions of Django did not validate or limit the form-count data
+provided by the client in a formset's management form, making it possible to
+exhaust a server's available memory by forcing it to create very large numbers
+of forms.
+
+In Django 1.4.4, all formsets have a strictly-enforced maximum number of forms
+(1000 by default, though it can be set higher via the ``max_num`` formset
+factory argument).
+
+
+Admin history view information leakage
+--------------------------------------
+
+In previous versions of Django, an admin user without change permission on a
+model could still view the unicode representation of instances via their admin
+history log. Django 1.4.4 now limits the admin history log view for an object
+to users with change permission for that model.
+
+
+Other bugfixes and changes
+==========================
+
+* Prevented transaction state from leaking from one request to the next (#19707).
+* Changed a SQL command syntax to be MySQL 4 compatible (#19702).
+* Added backwards-compatibility with old unsalted MD5 passwords (#18144).
+* Numerous documentation improvements and fixes.
diff --git a/lib/django-1.5/docs/releases/1.4.5.txt b/lib/django-1.5/docs/releases/1.4.5.txt
new file mode 100644
index 0000000..9ba5235
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.4.5.txt
@@ -0,0 +1,13 @@
+==========================
+Django 1.4.5 release notes
+==========================
+
+*February 20, 2013*
+
+Django 1.4.5 corrects a packaging problem with yesterday's :doc:`1.4.4 release
+</releases/1.4.4>`.
+
+The release contained stray ``.pyc`` files that caused "bad magic number"
+errors when running with some versions of Python. This releases corrects this,
+and also fixes a bad documentation link in the project template ``settings.py``
+file generated by ``manage.py startproject``.
diff --git a/lib/django-1.5/docs/releases/1.4.6.txt b/lib/django-1.5/docs/releases/1.4.6.txt
new file mode 100644
index 0000000..575e9fa
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.4.6.txt
@@ -0,0 +1,31 @@
+==========================
+Django 1.4.6 release notes
+==========================
+
+*August 13, 2013*
+
+Django 1.4.6 fixes one security issue present in previous Django releases in
+the 1.4 series, as well as one other bug.
+
+This is the sixth bugfix/security release in the Django 1.4 series.
+
+Mitigated possible XSS attack via user-supplied redirect URLs
+-------------------------------------------------------------
+
+Django relies on user input in some cases (e.g.
+:func:`django.contrib.auth.views.login`, :mod:`django.contrib.comments`, and
+:doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
+The security checks for these redirects (namely
+``django.util.http.is_safe_url()``) didn't check if the scheme is ``http(s)``
+and as such allowed ``javascript:...`` URLs to be entered. If a developer
+relied on ``is_safe_url()`` to provide safe redirect targets and put such a
+URL into a link, he could suffer from a XSS attack. This bug doesn't affect
+Django currently, since we only put this URL into the ``Location`` response
+header and browsers seem to ignore JavaScript there.
+
+Bugfixes
+========
+
+* Fixed an obscure bug with the :func:`~django.test.utils.override_settings`
+ decorator. If you hit an ``AttributeError: 'Settings' object has no attribute
+ '_original_allowed_hosts'`` exception, it's probably fixed (#20636).
diff --git a/lib/django-1.5/docs/releases/1.4.7.txt b/lib/django-1.5/docs/releases/1.4.7.txt
new file mode 100644
index 0000000..64d3088
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.4.7.txt
@@ -0,0 +1,25 @@
+==========================
+Django 1.4.7 release notes
+==========================
+
+*September 10, 2013*
+
+Django 1.4.7 fixes one security issue present in previous Django releases in
+the 1.4 series.
+
+Directory traversal vulnerability in :ttag:`ssi` template tag
+-------------------------------------------------------------
+
+In previous versions of Django it was possible to bypass the
+:setting:`ALLOWED_INCLUDE_ROOTS` setting used for security with the :ttag:`ssi`
+template tag by specifying a relative path that starts with one of the allowed
+roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
+would be possible:
+
+.. code-block:: html+django
+
+ {% ssi "/var/www/../../etc/passwd" %}
+
+In practice this is not a very common problem, as it would require the template
+author to put the :ttag:`ssi` file in a user-controlled variable, but it's
+possible in principle.
diff --git a/lib/django-1.5/docs/releases/1.4.8.txt b/lib/django-1.5/docs/releases/1.4.8.txt
new file mode 100644
index 0000000..bec5a4b
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.4.8.txt
@@ -0,0 +1,21 @@
+==========================
+Django 1.4.7 release notes
+==========================
+
+*September 14, 2013*
+
+Django 1.4.8 fixes one security issue present in previous Django releases in
+the 1.4 series.
+
+Denial-of-service via password hashers
+--------------------------------------
+
+In previous versions of Django no limit was imposed on the plaintext
+length of a password. This allows a denial-of-service attack through
+submission of bogus but extremely large passwords, tying up server
+resources performing the (expensive, and increasingly expensive with
+the length of the password) calculation of the corresponding hash.
+
+As of 1.4.8, Django's authentication framework imposes a 4096-byte
+limit on passwords, and will fail authentication with any submitted
+password of greater length.
diff --git a/lib/django-1.5/docs/releases/1.4.txt b/lib/django-1.5/docs/releases/1.4.txt
index bd7c647..d109428 100644
--- a/lib/django-1.5/docs/releases/1.4.txt
+++ b/lib/django-1.5/docs/releases/1.4.txt
@@ -708,7 +708,7 @@
``/media/``) simply make sure :setting:`STATIC_URL` and :setting:`STATIC_ROOT`
are configured and your Web server serves those files correctly. The
development server continues to serve the admin files just like before. Read
-the :doc:`static files howto </howto/static-files>` for more details.
+the :doc:`static files howto </howto/static-files/index>` for more details.
If your ``ADMIN_MEDIA_PREFIX`` is set to an specific domain (e.g.
``http://media.example.com/admin/``), make sure to also set your
@@ -1039,7 +1039,7 @@
The ``open`` method of the base Storage class used to take an obscure parameter
``mixin`` that allowed you to dynamically change the base classes of the
returned file object. This has been removed. In the rare case you relied on the
-`mixin` parameter, you can easily achieve the same by overriding the `open`
+``mixin`` parameter, you can easily achieve the same by overriding the ``open``
method, like this::
from django.core.files import File
diff --git a/lib/django-1.5/docs/releases/1.5-alpha-1.txt b/lib/django-1.5/docs/releases/1.5-alpha-1.txt
index bb3f32a..2588b85 100644
--- a/lib/django-1.5/docs/releases/1.5-alpha-1.txt
+++ b/lib/django-1.5/docs/releases/1.5-alpha-1.txt
@@ -227,7 +227,9 @@
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
(so-called linear referencing).
-* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
+* The ``wkb`` and ``hex`` properties of
+ :class:`~django.contrib.gis.geos.GEOSGeometry` objects preserve the Z
+ dimension.
* Support for PostGIS 2.0 has been added and support for GDAL < 1.5 has been
dropped.
@@ -283,8 +285,8 @@
* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
the request as ``resolver_match``.
-* By default, all logging messages reaching the `django` logger when
- :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
+* By default, all logging messages reaching the ``django`` logger when
+ :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting).
* When using :class:`~django.template.RequestContext`, it is now possible to
@@ -301,8 +303,9 @@
whenever a user fails to login successfully. See
:data:`~django.contrib.auth.signals.user_login_failed`
-* The loaddata management command now supports an `ignorenonexistent` option to
- ignore data for fields that no longer exist.
+* The loaddata management command now supports an
+ :djadminopt:`--ignorenonexistent` option to ignore data for fields that no
+ longer exist.
* :meth:`~django.test.SimpleTestCase.assertXMLEqual` and
:meth:`~django.test.SimpleTestCase.assertXMLNotEqual` new assertions allow
@@ -556,7 +559,7 @@
* Uploaded files are no longer created as executable by default. If you need
them to be executable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
- needs. The new default value is `0666` (octal) and the current umask value
+ needs. The new default value is ``0666`` (octal) and the current umask value
is first masked out.
* The :ref:`F() expressions <query-expressions>` supported bitwise operators by
diff --git a/lib/django-1.5/docs/releases/1.5-beta-1.txt b/lib/django-1.5/docs/releases/1.5-beta-1.txt
index 4dbe77f..57b13ea 100644
--- a/lib/django-1.5/docs/releases/1.5-beta-1.txt
+++ b/lib/django-1.5/docs/releases/1.5-beta-1.txt
@@ -225,7 +225,9 @@
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
(so-called linear referencing).
-* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
+* The ``wkb`` and ``hex`` properties of
+ :class:`~django.contrib.gis.geos.GEOSGeometry` objects preserve the Z
+ dimension.
* Support for PostGIS 2.0 has been added and support for GDAL < 1.5 has been
dropped.
@@ -281,8 +283,8 @@
* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
the request as ``resolver_match``.
-* By default, all logging messages reaching the `django` logger when
- :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
+* By default, all logging messages reaching the ``django`` logger when
+ :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting).
* When using :class:`~django.template.RequestContext`, it is now possible to
@@ -299,8 +301,9 @@
whenever a user fails to login successfully. See
:data:`~django.contrib.auth.signals.user_login_failed`
-* The loaddata management command now supports an `ignorenonexistent` option to
- ignore data for fields that no longer exist.
+* The loaddata management command now supports an
+ :djadminopt:`--ignorenonexistent` option to ignore data for fields that no
+ longer exist.
* :meth:`~django.test.SimpleTestCase.assertXMLEqual` and
:meth:`~django.test.SimpleTestCase.assertXMLNotEqual` new assertions allow
@@ -595,7 +598,7 @@
* Uploaded files are no longer created as executable by default. If you need
them to be executable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
- needs. The new default value is `0666` (octal) and the current umask value
+ needs. The new default value is ``0666`` (octal) and the current umask value
is first masked out.
* The :ref:`F() expressions <query-expressions>` supported bitwise operators by
diff --git a/lib/django-1.5/docs/releases/1.5.1.txt b/lib/django-1.5/docs/releases/1.5.1.txt
new file mode 100644
index 0000000..9999861
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.5.1.txt
@@ -0,0 +1,28 @@
+==========================
+Django 1.5.1 release notes
+==========================
+
+*March 28, 2013*
+
+This is Django 1.5.1, a bugfix release for Django 1.5. It's completely backwards
+compatible with Django 1.5, but includes a handful of fixes.
+
+The biggest fix is for a memory leak introduced in Django 1.5. Under certain
+circumstances, repeated iteration over querysets could leak memory - sometimes
+quite a bit of it. If you'd like more information, the details are in
+`our ticket tracker`__ (and in `a related issue`__ in Python itself).
+
+__ https://code.djangoproject.com/ticket/19895
+__ http://bugs.python.org/issue17468
+
+If you've noticed memory problems under Django 1.5, upgrading to 1.5.1 should
+fix those issues.
+
+Django 1.5.1 also includes a couple smaller fixes:
+
+* Module-level warnings emitted during tests are no longer silently hidden
+ (`#18985`__).
+* Prevented filtering on password hashes in the user admin (`#20078`__).
+
+__ https://code.djangoproject.com/ticket/18985
+__ https://code.djangoproject.com/ticket/20078
diff --git a/lib/django-1.5/docs/releases/1.5.2.txt b/lib/django-1.5/docs/releases/1.5.2.txt
new file mode 100644
index 0000000..f128548
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.5.2.txt
@@ -0,0 +1,62 @@
+==========================
+Django 1.5.2 release notes
+==========================
+
+*August 13, 2013*
+
+This is Django 1.5.2, a bugfix and security release for Django 1.5.
+
+Mitigated possible XSS attack via user-supplied redirect URLs
+-------------------------------------------------------------
+
+Django relies on user input in some cases (e.g.
+:func:`django.contrib.auth.views.login`, :mod:`django.contrib.comments`, and
+:doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
+The security checks for these redirects (namely
+``django.util.http.is_safe_url()``) didn't check if the scheme is ``http(s)``
+and as such allowed ``javascript:...`` URLs to be entered. If a developer
+relied on ``is_safe_url()`` to provide safe redirect targets and put such a
+URL into a link, he could suffer from a XSS attack. This bug doesn't affect
+Django currently, since we only put this URL into the ``Location`` response
+header and browsers seem to ignore JavaScript there.
+
+XSS vulnerability in :mod:`django.contrib.admin`
+------------------------------------------------
+
+If a :class:`~django.db.models.URLField` is used in Django 1.5, it displays the
+current value of the field and a link to the target on the admin change page.
+The display routine of this widget was flawed and allowed for XSS.
+
+Bugfixes
+========
+
+* Fixed a crash with :meth:`~django.db.models.query.QuerySet.prefetch_related`
+ (#19607) as well as some ``pickle`` regressions with ``prefetch_related``
+ (#20157 and #20257).
+* Fixed a regression in :mod:`django.contrib.gis` in the Google Map output on
+ Python 3 (#20773).
+* Made ``DjangoTestSuiteRunner.setup_databases`` properly handle aliases for
+ the default database (#19940) and prevented ``teardown_databases`` from
+ attempting to tear down aliases (#20681).
+* Fixed the ``django.core.cache.backends.memcached.MemcachedCache`` backend's
+ ``get_many()`` method on Python 3 (#20722).
+* Fixed :mod:`django.contrib.humanize` translation syntax errors. Affected
+ languages: Mexican Spanish, Mongolian, Romanian, Turkish (#20695).
+* Added support for wheel packages (#19252).
+* The CSRF token now rotates when a user logs in.
+* Some Python 3 compatibility fixes including #20212 and #20025.
+* Fixed some rare cases where :meth:`~django.db.models.query.QuerySet.get`
+ exceptions recursed infinitely (#20278).
+* :djadmin:`makemessages` no longer crashes with ``UnicodeDecodeError``
+ (#20354).
+* Fixed ``geojson`` detection with Spatialite.
+* :meth:`~django.test.TestCase.assertContains` once again works with
+ binary content (#20237).
+* Fixed :class:`~django.db.models.ManyToManyField` if it has a unicode ``name``
+ parameter (#20207).
+* Ensured that the WSGI request's path is correctly based on the
+ ``SCRIPT_NAME`` environment variable or the :setting:`FORCE_SCRIPT_NAME`
+ setting, regardless of whether or not either has a trailing slash (#20169).
+* Fixed an obscure bug with the :func:`~django.test.utils.override_settings`
+ decorator. If you hit an ``AttributeError: 'Settings' object has no attribute
+ '_original_allowed_hosts'`` exception, it's probably fixed (#20636).
diff --git a/lib/django-1.5/docs/releases/1.5.3.txt b/lib/django-1.5/docs/releases/1.5.3.txt
new file mode 100644
index 0000000..bdf68d5
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.5.3.txt
@@ -0,0 +1,50 @@
+==========================
+Django 1.5.3 release notes
+==========================
+
+*September 10, 2013*
+
+This is Django 1.5.3, the third release in the Django 1.5 series. It addresses
+one security issue and also contains an opt-in feature to enhance the security
+of :mod:`django.contrib.sessions`.
+
+Directory traversal vulnerability in :ttag:`ssi` template tag
+-------------------------------------------------------------
+
+In previous versions of Django it was possible to bypass the
+:setting:`ALLOWED_INCLUDE_ROOTS` setting used for security with the :ttag:`ssi`
+template tag by specifying a relative path that starts with one of the allowed
+roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
+would be possible:
+
+.. code-block:: html+django
+
+ {% ssi "/var/www/../../etc/passwd" %}
+
+In practice this is not a very common problem, as it would require the template
+author to put the :ttag:`ssi` file in a user-controlled variable, but it's
+possible in principle.
+
+Mitigating a remote-code execution vulnerability in :mod:`django.contrib.sessions`
+----------------------------------------------------------------------------------
+
+:mod:`django.contrib.sessions` currently uses :mod:`pickle` to serialize
+session data before storing it in the backend. If you're using the :ref:`signed
+cookie session backend<cookie-session-backend>` and :setting:`SECRET_KEY` is
+known by an attacker (there isn't an inherent vulnerability in Django that
+would cause it to leak), the attacker could insert a string into his session
+which, when unpickled, executes arbitrary code on the server. The technique for
+doing so is simple and easily available on the internet. Although the cookie
+session storage signs the cookie-stored data to prevent tampering, a
+:setting:`SECRET_KEY` leak immediately escalates to a remote code execution
+vulnerability.
+
+This attack can be mitigated by serializing session data using JSON rather
+than :mod:`pickle`. To facilitate this, Django 1.5.3 introduces a new setting,
+:setting:`SESSION_SERIALIZER`, to customize the session serialization format.
+For backwards compatibility, this setting defaults to using :mod:`pickle`.
+While JSON serialization does not support all Python objects like :mod:`pickle`
+does, we highly recommend switching to JSON-serialized values. Also,
+as JSON requires string keys, you will likely run into problems if you are
+using non-string keys in ``request.session``. See the
+:ref:`session_serialization` documentation for more details.
diff --git a/lib/django-1.5/docs/releases/1.5.4.txt b/lib/django-1.5/docs/releases/1.5.4.txt
new file mode 100644
index 0000000..00c56bc
--- /dev/null
+++ b/lib/django-1.5/docs/releases/1.5.4.txt
@@ -0,0 +1,21 @@
+==========================
+Django 1.5.3 release notes
+==========================
+
+*September 14, 2013*
+
+This is Django 1.5.4, the fourth release in the Django 1.5 series. It addresses
+one security issue.
+
+Denial-of-service via password hashers
+--------------------------------------
+
+In previous versions of Django no limit was imposed on the plaintext
+length of a password. This allows a denial-of-service attack through
+submission of bogus but extremely large passwords, tying up server
+resources performing the (expensive, and increasingly expensive with
+the length of the password) calculation of the corresponding hash.
+
+As of 1.5.3, Django's authentication framework imposes a 4096-byte
+limit on passwords, and will fail authentication with any submitted
+password of greater length.
diff --git a/lib/django-1.5/docs/releases/1.5.txt b/lib/django-1.5/docs/releases/1.5.txt
index 9c58011..b0bdfbc 100644
--- a/lib/django-1.5/docs/releases/1.5.txt
+++ b/lib/django-1.5/docs/releases/1.5.txt
@@ -146,9 +146,9 @@
save only a select list of model's fields. This can be useful for performance
reasons or when trying to avoid overwriting concurrent changes.
-Deferred instances (those loaded by .only() or .defer()) will automatically
-save just the loaded fields. If any field is set manually after load, that
-field will also get updated on save.
+Deferred instances (those loaded by ``.only()`` or ``.defer()``) will
+automatically save just the loaded fields. If any field is set manually after
+load, that field will also get updated on save.
See the :meth:`Model.save() <django.db.models.Model.save()>` documentation for
more details.
@@ -224,7 +224,9 @@
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
(so-called linear referencing).
-* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
+* The ``wkb`` and ``hex`` properties of
+ :class:`~django.contrib.gis.geos.GEOSGeometry` objects preserve the Z
+ dimension.
* Support for PostGIS 2.0 has been added and support for GDAL < 1.5 has been
dropped.
@@ -294,8 +296,8 @@
* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
the request as ``resolver_match``.
-* By default, all logging messages reaching the `django` logger when
- :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
+* By default, all logging messages reaching the ``django`` logger when
+ :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting).
* When using :class:`~django.template.RequestContext`, it is now possible to
@@ -312,8 +314,9 @@
whenever a user fails to login successfully. See
:data:`~django.contrib.auth.signals.user_login_failed`
-* The loaddata management command now supports an `ignorenonexistent` option to
- ignore data for fields that no longer exist.
+* The loaddata management command now supports an
+ :djadminopt:`--ignorenonexistent` option to ignore data for fields that no
+ longer exist.
* :meth:`~django.test.SimpleTestCase.assertXMLEqual` and
:meth:`~django.test.SimpleTestCase.assertXMLNotEqual` new assertions allow
@@ -439,7 +442,15 @@
This signal is now sent after the content is fully consumed by the WSGI
gateway. This might be backwards incompatible if you rely on the signal being
fired before sending the response content to the client. If you do, you should
-consider using a middleware instead.
+consider using :doc:`middleware </topics/http/middleware>` instead.
+
+.. note::
+
+ Some WSGI servers and middleware do not always call ``close`` on the
+ response object after handling a request, most notably uWSGI prior to 1.2.6
+ and Sentry's error reporting middleware up to 2.0.7. In those cases the
+ ``request_finished`` signal isn't sent at all. This can result in idle
+ connections to database and memcache servers.
OPTIONS, PUT and DELETE requests in the test client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -664,7 +675,7 @@
* Uploaded files are no longer created as executable by default. If you need
them to be executable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
- needs. The new default value is `0666` (octal) and the current umask value
+ needs. The new default value is ``0666`` (octal) and the current umask value
is first masked out.
* The :ref:`F() expressions <query-expressions>` supported bitwise operators by
@@ -694,6 +705,10 @@
longer. If you're using ``django.contrib.redirects``, make sure
:setting:`INSTALLED_APPS` contains ``django.contrib.sites``.
+* :meth:`BoundField.label_tag <django.forms.BoundField.label_tag>` now
+ escapes its ``contents`` argument. To avoid the HTML escaping, use
+ :func:`django.utils.safestring.mark_safe` on the argument before passing it.
+
Features deprecated in 1.5
==========================
diff --git a/lib/django-1.5/docs/releases/index.txt b/lib/django-1.5/docs/releases/index.txt
index d5d82c0..f3f741a 100644
--- a/lib/django-1.5/docs/releases/index.txt
+++ b/lib/django-1.5/docs/releases/index.txt
@@ -6,10 +6,11 @@
what's new in each version, and will also describe any backwards-incompatible
changes made in that version.
-For those upgrading to a new version of Django, you will need to check
-all the backwards-incompatible changes and deprecated features for
-each 'final' release from the one after your current Django version,
-up to and including the new version.
+For those :doc:`upgrading to a new version of Django</howto/upgrade-version>`,
+you will need to check all the backwards-incompatible changes and
+:doc:`deprecated features</internals/deprecation>` for each 'final' release
+from the one after your current Django version, up to and including the new
+version.
Final releases
==============
@@ -21,6 +22,9 @@
.. toctree::
:maxdepth: 1
+ 1.5.3
+ 1.5.2
+ 1.5.1
1.5
1.4 release
@@ -28,6 +32,11 @@
.. toctree::
:maxdepth: 1
+ 1.4.7
+ 1.4.6
+ 1.4.5
+ 1.4.4
+ 1.4.3
1.4.2
1.4.1
1.4
@@ -37,6 +46,11 @@
.. toctree::
:maxdepth: 1
+ 1.3.7
+ 1.3.6
+ 1.3.5
+ 1.3.4
+ 1.3.3
1.3.2
1.3.1
1.3
diff --git a/lib/django-1.5/docs/topics/auth/customizing.txt b/lib/django-1.5/docs/topics/auth/customizing.txt
index f15ce84..73a4d53 100644
--- a/lib/django-1.5/docs/topics/auth/customizing.txt
+++ b/lib/django-1.5/docs/topics/auth/customizing.txt
@@ -37,7 +37,7 @@
database-based scheme, or you can use the default system in tandem with other
systems.
-See the `authentication backend reference
+See the :ref:`authentication backend reference
<authentication-backends-reference>` for information on the authentication
backends included with Django.
@@ -89,7 +89,8 @@
optional permission related :ref:`authorization methods <authorization_methods>`.
The ``get_user`` method takes a ``user_id`` -- which could be a username,
-database ID or whatever -- and returns a ``User`` object.
+database ID or whatever, but has to be the primary key of your ``User`` object
+-- and returns a ``User`` object.
The ``authenticate`` method takes credentials as keyword arguments. Most of
the time, it'll just look like this::
@@ -97,12 +98,14 @@
class MyBackend(object):
def authenticate(self, username=None, password=None):
# Check the username/password and return a User.
+ ...
But it could also authenticate a token, like so::
class MyBackend(object):
def authenticate(self, token=None):
# Check the token and return a User.
+ ...
Either way, ``authenticate`` should check the credentials it gets, and it
should return a ``User`` object that matches those credentials, if the
@@ -177,9 +180,7 @@
fairly simply::
class SettingsBackend(object):
-
- # ...
-
+ ...
def has_perm(self, user_obj, perm, obj=None):
if user_obj.username == settings.ADMIN_LOGIN:
return True
@@ -476,7 +477,7 @@
The easiest way to construct a compliant custom User model is to inherit from
:class:`~django.contrib.auth.models.AbstractBaseUser`.
:class:`~django.contrib.auth.models.AbstractBaseUser` provides the core
-implementation of a `User` model, including hashed passwords and tokenized
+implementation of a ``User`` model, including hashed passwords and tokenized
password resets. You must then provide some key implementation details:
.. currentmodule:: django.contrib.auth
@@ -489,9 +490,9 @@
used as the unique identifier. This will usually be a username of
some kind, but it can also be an email address, or any other unique
identifier. The field *must* be unique (i.e., have ``unique=True``
- set in it's definition).
+ set in its definition).
- In the following example, the field `identifier` is used
+ In the following example, the field ``identifier`` is used
as the identifying field::
class MyUser(AbstractBaseUser):
@@ -501,13 +502,15 @@
.. attribute:: REQUIRED_FIELDS
- A list of the field names that *must* be provided when creating a user
- via the :djadmin:`createsuperuser` management command. The user will be
- prompted to supply a value for each of these fields. It should include
- any field for which :attr:`~django.db.models.Field.blank` is ``False``
- or undefined, but may include additional fields you want prompted for
- when a user is created interactively. However, it will not work for
- :class:`~django.db.models.ForeignKey` fields.
+ A list of the field names that will be prompted for when creating a
+ user via the :djadmin:`createsuperuser` management command. The user
+ will be prompted to supply a value for each of these fields. It must
+ include any field for which :attr:`~django.db.models.Field.blank` is
+ ``False`` or undefined and may include additional fields you want
+ prompted for when a user is created interactively. However, it will not
+ work for :class:`~django.db.models.ForeignKey` fields.
+ ``REQUIRED_FIELDS`` has no effect in other parts of Django, like
+ creating a user in the admin.
For example, here is the partial definition for a ``User`` model that
defines two required fields - a date of birth and height::
@@ -599,11 +602,12 @@
:meth:`~django.contrib.auth.models.AbstractBaseUser.set_unusable_password()` has
been called for this user.
-You should also define a custom manager for your User model. If your User
-model defines `username` and `email` fields the same as Django's default User,
-you can just install Django's
-:class:`~django.contrib.auth.models.UserManager`; however, if your User model
-defines different fields, you will need to define a custom manager that
+You should also define a custom manager for your ``User`` model. If your
+``User`` model defines ``username``, ``email``, ``is_staff``, ``is_active``,
+``is_superuser``, ``last_login``, and ``date_joined`` fields the same as
+Django's default ``User``, you can just install Django's
+:class:`~django.contrib.auth.models.UserManager`; however, if your ``User``
+model defines different fields, you will need to define a custom manager that
extends :class:`~django.contrib.auth.models.BaseUserManager` providing two
additional methods:
@@ -611,26 +615,28 @@
.. method:: models.CustomUserManager.create_user(*username_field*, password=None, \**other_fields)
- The prototype of `create_user()` should accept the username field,
+ The prototype of ``create_user()`` should accept the username field,
plus all required fields as arguments. For example, if your user model
- uses `email` as the username field, and has `date_of_birth` as a required
- fields, then create_user should be defined as::
+ uses ``email`` as the username field, and has ``date_of_birth`` as a
+ required fields, then ``create_user`` should be defined as::
def create_user(self, email, date_of_birth, password=None):
# create user here
+ ...
.. method:: models.CustomUserManager.create_superuser(*username_field*, password, \**other_fields)
- The prototype of `create_superuser()` should accept the username field,
- plus all required fields as arguments. For example, if your user model
- uses `email` as the username field, and has `date_of_birth` as a required
- fields, then create_superuser should be defined as::
+ The prototype of ``create_superuser()`` should accept the username
+ field, plus all required fields as arguments. For example, if your user
+ model uses ``email`` as the username field, and has ``date_of_birth``
+ as a required fields, then ``create_superuser`` should be defined as::
def create_superuser(self, email, date_of_birth, password):
# create superuser here
+ ...
- Unlike `create_user()`, `create_superuser()` *must* require the caller
- to provider a password.
+ Unlike ``create_user()``, ``create_superuser()`` *must* require the
+ caller to provider a password.
:class:`~django.contrib.auth.models.BaseUserManager` provides the following
utility methods:
@@ -699,7 +705,7 @@
* :class:`~django.contrib.auth.forms.PasswordResetForm`
Assumes that the user model has an integer primary key, has a field named
- `email` that can be used to identify the user, and a boolean field
+ ``email`` that can be used to identify the user, and a boolean field
named `is_active` to prevent password resets for inactive users.
* :class:`~django.contrib.auth.forms.SetPasswordForm`
@@ -715,8 +721,8 @@
Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
-Custom users and django.contrib.admin
--------------------------------------
+Custom users and :mod:`django.contrib.admin`
+--------------------------------------------
If you want your custom User model to also work with Admin, your User model must
define some additional attributes and methods. These methods allow the admin to
@@ -726,21 +732,21 @@
.. attribute:: is_staff
- Returns True if the user is allowed to have access to the admin site.
+ Returns ``True`` if the user is allowed to have access to the admin site.
.. attribute:: is_active
- Returns True if the user account is currently active.
+ Returns ``True`` if the user account is currently active.
.. method:: has_perm(perm, obj=None):
- Returns True if the user has the named permission. If `obj` is
+ Returns ``True`` if the user has the named permission. If ``obj`` is
provided, the permission needs to be checked against a specific object
instance.
.. method:: has_module_perms(app_label):
- Returns True if the user has permission to access models in
+ Returns ``True`` if the user has permission to access models in
the given app.
You will also need to register your custom User model with the admin. If
@@ -905,7 +911,7 @@
Here is an example of an admin-compliant custom user app. This user model uses
an email address as the username, and has a required date of birth; it
-provides no permission checking, beyond a simple `admin` flag on the user
+provides no permission checking, beyond a simple ``admin`` flag on the user
account. This model would be compatible with all the built-in auth forms and
views, except for the User creation forms. This example illustrates how most of
the components work together, but is not intended to be copied directly into
@@ -1067,6 +1073,8 @@
('Permissions', {'fields': ('is_admin',)}),
('Important dates', {'fields': ('last_login',)}),
)
+ # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
+ # overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
diff --git a/lib/django-1.5/docs/topics/auth/default.txt b/lib/django-1.5/docs/topics/auth/default.txt
index 4072c51..a104a5c 100644
--- a/lib/django-1.5/docs/topics/auth/default.txt
+++ b/lib/django-1.5/docs/topics/auth/default.txt
@@ -234,10 +234,11 @@
example, you can create the ``can_publish`` permission for a ``BlogPost`` model
in ``myapp``::
+ from myapp.models import BlogPost
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
- content_type = ContentType.objects.get(app_label='myapp', model='BlogPost')
+ content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(codename='can_publish',
name='Can Publish Posts',
content_type=content_type)
@@ -317,7 +318,7 @@
which authentication backend successfully authenticated that user (see the
:ref:`backends documentation <authentication-backends>` for details), and
this information is needed later during the login process. An error will be
- raise if you try to login a user object retrieved from the database
+ raised if you try to login a user object retrieved from the database
directly.
How to log a user out
@@ -564,13 +565,33 @@
patterns.
-.. function:: login(request, [template_name, redirect_field_name, authentication_form])
+.. function:: login(request, [template_name, redirect_field_name, authentication_form, current_app, extra_context])
**URL name:** ``login``
See :doc:`the URL documentation </topics/http/urls>` for details on using
named URL patterns.
+ **Optional arguments:**
+
+ * ``template_name``: The name of a template to display for the view used to
+ log the user in. Defaults to :file:`registration/login.html`.
+
+ * ``redirect_field_name``: The name of a ``GET`` field containing the
+ URL to redirect to after login. Overrides ``next`` if the given
+ ``GET`` parameter is passed.
+
+ * ``authentication_form``: A callable (typically just a form class) to
+ use for authentication. Defaults to
+ :class:`~django.contrib.auth.forms.AuthenticationForm`.
+
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
Here's what ``django.contrib.auth.views.login`` does:
* If called via ``GET``, it displays a login form that POSTs to the
@@ -660,7 +681,7 @@
.. _site framework docs: ../sites/
-.. function:: logout(request, [next_page, template_name, redirect_field_name])
+.. function:: logout(request, [next_page, template_name, redirect_field_name, current_app, extra_context])
Logs a user out.
@@ -678,6 +699,13 @@
URL to redirect to after log out. Overrides ``next_page`` if the given
``GET`` parameter is passed.
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
**Template context:**
* ``title``: The string "Logged out", localized.
@@ -694,7 +722,14 @@
:attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
For more on sites, see :doc:`/ref/contrib/sites`.
-.. function:: logout_then_login(request[, login_url])
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
+.. function:: logout_then_login(request[, login_url, current_app, extra_context])
Logs a user out, then redirects to the login page.
@@ -705,7 +740,14 @@
* ``login_url``: The URL of the login page to redirect to.
Defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
-.. function:: password_change(request[, template_name, post_change_redirect, password_change_form])
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
+.. function:: password_change(request[, template_name, post_change_redirect, password_change_form, current_app, extra_context])
Allows a user to change their password.
@@ -725,11 +767,18 @@
actually changing the user's password. Defaults to
:class:`~django.contrib.auth.forms.PasswordChangeForm`.
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
**Template context:**
* ``form``: The password change form (see ``password_change_form`` above).
-.. function:: password_change_done(request[, template_name])
+.. function:: password_change_done(request[, template_name, current_app, extra_context])
The page shown after a user has changed their password.
@@ -741,7 +790,14 @@
Defaults to :file:`registration/password_change_done.html` if not
supplied.
-.. function:: password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email])
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
+.. function:: password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email, current_app, extra_context])
Allows a user to reset their password by generating a one-time use link
that can be used to reset the password, and sending that link to the
@@ -785,6 +841,13 @@
* ``from_email``: A valid email address. By default Django uses
the :setting:`DEFAULT_FROM_EMAIL`.
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
**Template context:**
* ``form``: The form (see ``password_reset_form`` above) for resetting
@@ -824,7 +887,7 @@
single line plain text string.
-.. function:: password_reset_done(request[, template_name])
+.. function:: password_reset_done(request[, template_name, current_app, extra_context])
The page shown after a user has been emailed a link to reset their
password. This view is called by default if the :func:`password_reset` view
@@ -838,7 +901,14 @@
Defaults to :file:`registration/password_reset_done.html` if not
supplied.
-.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect])
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
+.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect, current_app, extra_context])
Presents a form for entering a new password.
@@ -864,6 +934,13 @@
* ``post_reset_redirect``: URL to redirect after the password reset
done. Defaults to ``None``.
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
**Template context:**
* ``form``: The form (see ``set_password_form`` above) for setting the
@@ -872,7 +949,7 @@
* ``validlink``: Boolean, True if the link (combination of uidb36 and
token) is valid or unused yet.
-.. function:: password_reset_complete(request[,template_name])
+.. function:: password_reset_complete(request[,template_name, current_app, extra_context])
Presents a view which informs the user that the password has been
successfully changed.
@@ -884,6 +961,13 @@
* ``template_name``: The full name of a template to display the view.
Defaults to :file:`registration/password_reset_complete.html`.
+ * ``current_app``: A hint indicating which application contains the current
+ view. See the :ref:`namespaced URL resolution strategy
+ <topics-http-reversing-url-namespaces>` for more information.
+
+ * ``extra_context``: A dictionary of context data that will be added to the
+ default context data passed to the template.
+
Helper functions
----------------
diff --git a/lib/django-1.5/docs/topics/auth/passwords.txt b/lib/django-1.5/docs/topics/auth/passwords.txt
index 45fde9b..164c829 100644
--- a/lib/django-1.5/docs/topics/auth/passwords.txt
+++ b/lib/django-1.5/docs/topics/auth/passwords.txt
@@ -100,14 +100,25 @@
That's it -- now your Django install will use Bcrypt as the default storage
algorithm.
+.. admonition:: Password truncation with BCryptPasswordHasher
+
+ The designers of bcrypt truncate all passwords at 72 characters which means
+ that ``bcrypt(password_with_100_chars) == bcrypt(password_with_100_chars[:72])``.
+ ``BCryptPasswordHasher`` does not have any special handling and
+ thus is also subject to this hidden password length limit. The practical
+ ramification of this truncation is pretty marginal as the average user does
+ not have a password greater than 72 characters in length and even being
+ truncated at 72 the compute powered required to brute force bcrypt in any
+ useful amount of time is still astronomical.
+
.. admonition:: Other bcrypt implementations
There are several other implementations that allow bcrypt to be
used with Django. Django's bcrypt support is NOT directly
compatible with these. To upgrade, you will need to modify the
- hashes in your database to be in the form `bcrypt$(raw bcrypt
- output)`. For example:
- `bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy`.
+ hashes in your database to be in the form ``bcrypt$(raw bcrypt
+ output)``. For example:
+ ``bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy``.
Increasing the work factor
--------------------------
diff --git a/lib/django-1.5/docs/topics/cache.txt b/lib/django-1.5/docs/topics/cache.txt
index 48f682b..1ef6dfe 100644
--- a/lib/django-1.5/docs/topics/cache.txt
+++ b/lib/django-1.5/docs/topics/cache.txt
@@ -39,6 +39,8 @@
caches that you don't directly control but to which you can provide hints (via
HTTP headers) about which parts of your site should be cached, and how.
+.. _setting-up-the-cache:
+
Setting up the cache
====================
@@ -152,6 +154,8 @@
storage -- but we point this out here because memory-based caching is
particularly temporary.
+.. _database-caching:
+
Database caching
----------------
@@ -337,40 +341,37 @@
Cache arguments
---------------
-In addition to the defining the engine and name of the each cache
-backend, each cache backend can be given additional arguments to
-control caching behavior. These arguments are provided as additional
-keys in the :setting:`CACHES` setting. Valid arguments are as follows:
+Each cache backend can be given additional arguments to control caching
+behavior. These arguments are provided as additional keys in the
+:setting:`CACHES` setting. Valid arguments are as follows:
* :setting:`TIMEOUT <CACHES-TIMEOUT>`: The default timeout, in
- seconds, to use for the cache. This argument defaults to 300
+ seconds, to use for the cache. This argument defaults to ``300``
seconds (5 minutes).
* :setting:`OPTIONS <CACHES-OPTIONS>`: Any options that should be
- passed to cache backend. The list options understood by each
- backend vary with each backend.
+ passed to the cache backend. The list of valid options will vary
+ with each backend, and cache backends backed by a third-party library
+ will pass their options directly to the underlying cache library.
Cache backends that implement their own culling strategy (i.e.,
the ``locmem``, ``filesystem`` and ``database`` backends) will
honor the following options:
- * ``MAX_ENTRIES``: the maximum number of entries allowed in
+ * ``MAX_ENTRIES``: The maximum number of entries allowed in
the cache before old values are deleted. This argument
defaults to ``300``.
* ``CULL_FREQUENCY``: The fraction of entries that are culled
when ``MAX_ENTRIES`` is reached. The actual ratio is
- ``1/CULL_FREQUENCY``, so set ``CULL_FREQUENCY``: to ``2`` to
- cull half of the entries when ``MAX_ENTRIES`` is reached.
+ ``1 / CULL_FREQUENCY``, so set ``CULL_FREQUENCY`` to ``2`` to
+ cull half the entries when ``MAX_ENTRIES`` is reached. This argument
+ should be an integer and defaults to ``3``.
A value of ``0`` for ``CULL_FREQUENCY`` means that the
entire cache will be dumped when ``MAX_ENTRIES`` is reached.
- This makes culling *much* faster at the expense of more
- cache misses.
-
- Cache backends backed by a third-party library will pass their
- options directly to the underlying cache library. As a result,
- the list of valid options depends on the library in use.
+ On some backends (``database`` in particular) this makes culling *much*
+ faster at the expense of more cache misses.
* :setting:`KEY_PREFIX <CACHES-KEY_PREFIX>`: A string that will be
automatically included (prepended by default) to all cache keys
@@ -1146,7 +1147,10 @@
and ``Last-Modified`` headers.
* :class:`django.middleware.gzip.GZipMiddleware` compresses responses for all
- modern browsers, saving bandwidth and transfer time.
+ modern browsers, saving bandwidth and transfer time. Be warned, however,
+ that compression techniques like ``GZipMiddleware`` are subject to attacks.
+ See the warning in :class:`~django.middleware.gzip.GZipMiddleware` for
+ details.
Order of MIDDLEWARE_CLASSES
===========================
diff --git a/lib/django-1.5/docs/topics/class-based-views/generic-display.txt b/lib/django-1.5/docs/topics/class-based-views/generic-display.txt
index 8fe6cd0..16d9451 100644
--- a/lib/django-1.5/docs/topics/class-based-views/generic-display.txt
+++ b/lib/django-1.5/docs/topics/class-based-views/generic-display.txt
@@ -92,6 +92,15 @@
def __unicode__(self):
return self.name
+ class Author(models.Model):
+ salutation = models.CharField(max_length=10)
+ name = models.CharField(max_length=200)
+ email = models.EmailField()
+ headshot = models.ImageField(upload_to='author_headshots')
+
+ def __unicode__(self):
+ return self.name
+
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField('Author')
@@ -132,11 +141,11 @@
enabled in :setting:`TEMPLATE_LOADERS`, a template location could be:
/path/to/project/books/templates/books/publisher_list.html
-.. highlightlang:: html+django
-
This template will be rendered against a context containing a variable called
``object_list`` that contains all the publisher objects. A very simple template
-might look like the following::
+might look like the following:
+
+.. code-block:: html+django
{% extends "base.html" %}
@@ -159,8 +168,6 @@
Making "friendly" template contexts
-----------------------------------
-.. highlightlang:: python
-
You might have noticed that our sample publisher list template stores all the
publishers in a variable named ``object_list``. While this works just fine, it
isn't all that "friendly" to template authors: they have to "just know" that
@@ -196,15 +203,12 @@
all the books on each publisher detail page. The
:class:`~django.views.generic.detail.DetailView` generic view provides
the publisher to the context, but how do we get additional information
-in that template.
+in that template?
-However, there is; you can subclass
-:class:`~django.views.generic.detail.DetailView` and provide your own
-implementation of the ``get_context_data`` method. The default
-implementation of this that comes with
-:class:`~django.views.generic.detail.DetailView` simply adds in the
-object being displayed to the template, but you can override it to send
-more::
+The answer is to subclass :class:`~django.views.generic.detail.DetailView`
+and provide your own implementation of the ``get_context_data`` method.
+The default implementation simply adds the object being displayed to the
+template, but you can override it to send more::
from django.views.generic import DetailView
from books.models import Publisher, Book
@@ -222,10 +226,10 @@
.. note::
- Generally, get_context_data will merge the context data of all parent
+ Generally, ``get_context_data`` will merge the context data of all parent
classes with those of the current class. To preserve this behavior in your
own classes where you want to alter the context, you should be sure to call
- get_context_data on the super class. When no two classes try to define the
+ ``get_context_data`` on the super class. When no two classes try to define the
same key, this will give the expected results. However if any class
attempts to override a key after parent classes have set it (after the call
to super), any children of that class will also need to explictly set it
@@ -369,7 +373,7 @@
The last common pattern we'll look at involves doing some extra work before
or after calling the generic view.
-Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
+Imagine we had a ``last_accessed`` field on our ``Author`` model that we were
using to keep track of the last time anybody looked at that author::
# models.py
@@ -378,7 +382,7 @@
salutation = models.CharField(max_length=10)
name = models.CharField(max_length=200)
email = models.EmailField()
- headshot = models.ImageField(upload_to='/tmp')
+ headshot = models.ImageField(upload_to='author_headshots')
last_accessed = models.DateTimeField()
The generic ``DetailView`` class, of course, wouldn't know anything about this
diff --git a/lib/django-1.5/docs/topics/class-based-views/generic-editing.txt b/lib/django-1.5/docs/topics/class-based-views/generic-editing.txt
index 8cd34f8..66ba36f 100644
--- a/lib/django-1.5/docs/topics/class-based-views/generic-editing.txt
+++ b/lib/django-1.5/docs/topics/class-based-views/generic-editing.txt
@@ -237,19 +237,24 @@
return HttpResponse(data, **response_kwargs)
def form_invalid(self, form):
+ response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return self.render_to_json_response(form.errors, status=400)
else:
- return super(AjaxableResponseMixin, self).form_invalid(form)
+ return response
def form_valid(self, form):
+ # We make sure to call the parent's form_valid() method because
+ # it might do some processing (in the case of CreateView, it will
+ # call form.save() for example).
+ response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
- 'pk': form.instance.pk,
+ 'pk': self.object.pk,
}
return self.render_to_json_response(data)
else:
- return super(AjaxableResponseMixin, self).form_valid(form)
+ return response
class AuthorCreate(AjaxableResponseMixin, CreateView):
model = Author
diff --git a/lib/django-1.5/docs/topics/class-based-views/index.txt b/lib/django-1.5/docs/topics/class-based-views/index.txt
index 302f473..b2fa93e 100644
--- a/lib/django-1.5/docs/topics/class-based-views/index.txt
+++ b/lib/django-1.5/docs/topics/class-based-views/index.txt
@@ -14,6 +14,7 @@
.. toctree::
:maxdepth: 1
+ intro
generic-display
generic-editing
mixins
@@ -127,68 +128,3 @@
the ``Last-Modified`` header indicates when the most recent book was published.
Based on this information, the client may or may not download the full object
list.
-
-Decorating class-based views
-============================
-
-.. highlightlang:: python
-
-Since class-based views aren't functions, decorating them works differently
-depending on if you're using ``as_view`` or creating a subclass.
-
-Decorating in URLconf
----------------------
-
-The simplest way of decorating class-based views is to decorate the
-result of the :meth:`~django.views.generic.base.View.as_view` method.
-The easiest place to do this is in the URLconf where you deploy your view::
-
- from django.contrib.auth.decorators import login_required, permission_required
- from django.views.generic import TemplateView
-
- from .views import VoteView
-
- urlpatterns = patterns('',
- (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
- (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
- )
-
-This approach applies the decorator on a per-instance basis. If you
-want every instance of a view to be decorated, you need to take a
-different approach.
-
-.. _decorating-class-based-views:
-
-Decorating the class
---------------------
-
-To decorate every instance of a class-based view, you need to decorate
-the class definition itself. To do this you apply the decorator to the
-:meth:`~django.views.generic.base.View.dispatch` method of the class.
-
-A method on a class isn't quite the same as a standalone function, so
-you can't just apply a function decorator to the method -- you need to
-transform it into a method decorator first. The ``method_decorator``
-decorator transforms a function decorator into a method decorator so
-that it can be used on an instance method. For example::
-
- from django.contrib.auth.decorators import login_required
- from django.utils.decorators import method_decorator
- from django.views.generic import TemplateView
-
- class ProtectedView(TemplateView):
- template_name = 'secret.html'
-
- @method_decorator(login_required)
- def dispatch(self, *args, **kwargs):
- return super(ProtectedView, self).dispatch(*args, **kwargs)
-
-In this example, every instance of ``ProtectedView`` will have
-login protection.
-
-.. note::
-
- ``method_decorator`` passes ``*args`` and ``**kwargs``
- as parameters to the decorated method on the class. If your method
- does not accept a compatible set of parameters it will raise a
- ``TypeError`` exception.
diff --git a/lib/django-1.5/docs/topics/class-based-views/intro.txt b/lib/django-1.5/docs/topics/class-based-views/intro.txt
new file mode 100644
index 0000000..5986ff2
--- /dev/null
+++ b/lib/django-1.5/docs/topics/class-based-views/intro.txt
@@ -0,0 +1,290 @@
+=================================
+Introduction to Class-based views
+=================================
+
+Class-based views provide an alternative way to implement views as Python
+objects instead of functions. They do not replace function-based views, but
+have certain differences and advantages when compared to function-based views:
+
+* Organization of code related to specific HTTP methods (``GET``, ``POST``,
+ etc) can be addressed by separate methods instead of conditional branching.
+
+* Object oriented techniques such as mixins (multiple inheritance) can be
+ used to factor code into reusable components.
+
+The relationship and history of generic views, class-based views, and class-based generic views
+===============================================================================================
+
+In the beginning there was only the view function contract, Django passed your
+function an :class:`~django.http.HttpRequest` and expected back an
+:class:`~django.http.HttpResponse`. This was the extent of what Django provided.
+
+Early on it was recognized that there were common idioms and patterns found in
+view development. Function-based generic views were introduced to abstract
+these patterns and ease view development for the common cases.
+
+The problem with function-based generic views is that while they covered the
+simple cases well, there was no way to extend or customize them beyond some
+simple configuration options, limiting their usefulness in many real-world
+applications.
+
+Class-based generic views were created with the same objective as
+function-based generic views, to make view development easier. However, the way
+the solution is implemented, through the use of mixins, provides a toolkit that
+results in class-based generic views being more extensible and flexible than
+their function-based counterparts.
+
+If you have tried function based generic views in the past and found them
+lacking, you should not think of class-based generic views as simply a
+class-based equivalent, but rather as a fresh approach to solving the original
+problems that generic views were meant to solve.
+
+The toolkit of base classes and mixins that Django uses to build class-based
+generic views are built for maximum flexibility, and as such have many hooks in
+the form of default method implementations and attributes that you are unlikely
+to be concerned with in the simplest use cases. For example, instead of
+limiting you to a class based attribute for ``form_class``, the implementation
+uses a ``get_form`` method, which calls a ``get_form_class`` method, which in
+its default implementation just returns the ``form_class`` attribute of the
+class. This gives you several options for specifying what form to use, from a
+simple attribute, to a fully dynamic, callable hook. These options seem to add
+hollow complexity for simple situations, but without them, more advanced
+designs would be limited.
+
+Using class-based views
+=======================
+
+At its core, a class-based view allows you to respond to different HTTP request
+methods with different class instance methods, instead of with conditionally
+branching code inside a single view function.
+
+So where the code to handle HTTP ``GET`` in a view function would look
+something like::
+
+ from django.http import HttpResponse
+
+ def my_view(request):
+ if request.method == 'GET':
+ # <view logic>
+ return HttpResponse('result')
+
+In a class-based view, this would become::
+
+ from django.http import HttpResponse
+ from django.views.generic.base import View
+
+ class MyView(View):
+ def get(self, request):
+ # <view logic>
+ return HttpResponse('result')
+
+Because Django's URL resolver expects to send the request and associated
+arguments to a callable function, not a class, class-based views have an
+:meth:`~django.views.generic.base.View.as_view` class method which serves as
+the callable entry point to your class. The ``as_view`` entry point creates an
+instance of your class and calls its
+:meth:`~django.views.generic.base.View.dispatch` method. ``dispatch`` looks at
+the request to determine whether it is a ``GET``, ``POST``, etc, and relays the
+request to a matching method if one is defined, or raises
+:class:`~django.http.HttpResponseNotAllowed` if not::
+
+ # urls.py
+ from django.conf.urls import patterns
+ from myapp.views import MyView
+
+ urlpatterns = patterns('',
+ (r'^about/', MyView.as_view()),
+ )
+
+
+It is worth noting that what your method returns is identical to what you
+return from a function-based view, namely some form of
+:class:`~django.http.HttpResponse`. This means that
+:doc:`http shortcuts </topics/http/shortcuts>` or
+:class:`~django.template.response.TemplateResponse` objects are valid to use
+inside a class-based view.
+
+While a minimal class-based view does not require any class attributes to
+perform its job, class attributes are useful in many class-based designs,
+and there are two ways to configure or set class attributes.
+
+The first is the standard Python way of subclassing and overriding attributes
+and methods in the subclass. So that if your parent class had an attribute
+``greeting`` like this::
+
+ from django.http import HttpResponse
+ from django.views.generic.base import View
+
+ class GreetingView(View):
+ greeting = "Good Day"
+
+ def get(self, request):
+ return HttpResponse(self.greeting)
+
+You can override that in a subclass::
+
+ class MorningGreetingView(GreetingView):
+ greeting = "Morning to ya"
+
+Another option is to configure class attributes as keyword arguments to the
+:meth:`~django.views.generic.base.View.as_view` call in the URLconf::
+
+ urlpatterns = patterns('',
+ (r'^about/', GreetingView.as_view(greeting="G'day")),
+ )
+
+.. note::
+
+ While your class is instantiated for each request dispatched to it, class
+ attributes set through the
+ :meth:`~django.views.generic.base.View.as_view` entry point are
+ configured only once at the time your URLs are imported.
+
+Using mixins
+============
+
+Mixins are a form of multiple inheritance where behaviors and attributes of
+multiple parent classes can be combined.
+
+For example, in the generic class-based views there is a mixin called
+:class:`~django.views.generic.base.TemplateResponseMixin` whose primary purpose
+is to define the method
+:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`.
+When combined with the behavior of the :class:`~django.views.generic.base.View`
+base class, the result is a :class:`~django.views.generic.base.TemplateView`
+class that will dispatch requests to the appropriate matching methods (a
+behavior defined in the ``View`` base class), and that has a
+:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
+method that uses a
+:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
+attribute to return a :class:`~django.template.response.TemplateResponse`
+object (a behavior defined in the ``TemplateResponseMixin``).
+
+Mixins are an excellent way of reusing code across multiple classes, but they
+come with some cost. The more your code is scattered among mixins, the harder
+it will be to read a child class and know what exactly it is doing, and the
+harder it will be to know which methods from which mixins to override if you
+are subclassing something that has a deep inheritance tree.
+
+Note also that you can only inherit from one generic view - that is, only one
+parent class may inherit from :class:`~django.views.generic.base.View` and
+the rest (if any) should be mixins. Trying to inherit from more than one class
+that inherits from ``View`` - for example, trying to use a form at the top of a
+list and combining :class:`~django.views.generic.edit.ProcessFormView` and
+:class:`~django.views.generic.list.ListView` - won't work as expected.
+
+Handling forms with class-based views
+=====================================
+
+A basic function-based view that handles forms may look something like this::
+
+ from django.http import HttpResponseRedirect
+ from django.shortcuts import render
+
+ from .forms import MyForm
+
+ def myview(request):
+ if request.method == "POST":
+ form = MyForm(request.POST)
+ if form.is_valid():
+ # <process form cleaned data>
+ return HttpResponseRedirect('/success/')
+ else:
+ form = MyForm(initial={'key': 'value'})
+
+ return render(request, 'form_template.html', {'form': form})
+
+A similar class-based view might look like::
+
+ from django.http import HttpResponseRedirect
+ from django.shortcuts import render
+ from django.views.generic.base import View
+
+ from .forms import MyForm
+
+ class MyFormView(View):
+ form_class = MyForm
+ initial = {'key': 'value'}
+ template_name = 'form_template.html'
+
+ def get(self, request, *args, **kwargs):
+ form = self.form_class(initial=self.initial)
+ return render(request, self.template_name, {'form': form})
+
+ def post(self, request, *args, **kwargs):
+ form = self.form_class(request.POST)
+ if form.is_valid():
+ # <process form cleaned data>
+ return HttpResponseRedirect('/success/')
+
+ return render(request, self.template_name, {'form': form})
+
+This is a very simple case, but you can see that you would then have the option
+of customizing this view by overriding any of the class attributes, e.g.
+``form_class``, via URLconf configuration, or subclassing and overriding one or
+more of the methods (or both!).
+
+Decorating class-based views
+============================
+
+The extension of class-based views isn't limited to using mixins. You
+can use also use decorators. Since class-based views aren't functions,
+decorating them works differently depending on if you're using ``as_view`` or
+creating a subclass.
+
+Decorating in URLconf
+---------------------
+
+The simplest way of decorating class-based views is to decorate the
+result of the :meth:`~django.views.generic.base.View.as_view` method.
+The easiest place to do this is in the URLconf where you deploy your view::
+
+ from django.contrib.auth.decorators import login_required, permission_required
+ from django.views.generic import TemplateView
+
+ from .views import VoteView
+
+ urlpatterns = patterns('',
+ (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
+ (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
+ )
+
+This approach applies the decorator on a per-instance basis. If you
+want every instance of a view to be decorated, you need to take a
+different approach.
+
+.. _decorating-class-based-views:
+
+Decorating the class
+--------------------
+
+To decorate every instance of a class-based view, you need to decorate
+the class definition itself. To do this you apply the decorator to the
+:meth:`~django.views.generic.base.View.dispatch` method of the class.
+
+A method on a class isn't quite the same as a standalone function, so
+you can't just apply a function decorator to the method -- you need to
+transform it into a method decorator first. The ``method_decorator``
+decorator transforms a function decorator into a method decorator so
+that it can be used on an instance method. For example::
+
+ from django.contrib.auth.decorators import login_required
+ from django.utils.decorators import method_decorator
+ from django.views.generic import TemplateView
+
+ class ProtectedView(TemplateView):
+ template_name = 'secret.html'
+
+ @method_decorator(login_required)
+ def dispatch(self, *args, **kwargs):
+ return super(ProtectedView, self).dispatch(*args, **kwargs)
+
+In this example, every instance of ``ProtectedView`` will have
+login protection.
+
+.. note::
+
+ ``method_decorator`` passes ``*args`` and ``**kwargs``
+ as parameters to the decorated method on the class. If your method
+ does not accept a compatible set of parameters it will raise a
+ ``TypeError`` exception.
diff --git a/lib/django-1.5/docs/topics/class-based-views/mixins.txt b/lib/django-1.5/docs/topics/class-based-views/mixins.txt
index 4941ea9..fcde531 100644
--- a/lib/django-1.5/docs/topics/class-based-views/mixins.txt
+++ b/lib/django-1.5/docs/topics/class-based-views/mixins.txt
@@ -13,7 +13,7 @@
want to write a view that renders a template to make the HTTP
response, but you can't use
:class:`~django.views.generic.base.TemplateView`; perhaps you need to
-render a template only on `POST`, with `GET` doing something else
+render a template only on ``POST``, with ``GET`` doing something else
entirely. While you could use
:class:`~django.template.response.TemplateResponse` directly, this
will likely result in duplicate code.
@@ -32,7 +32,6 @@
interface to working with templates in class-based views.
:class:`~django.views.generic.base.TemplateResponseMixin`
-
Every built in view which returns a
:class:`~django.template.response.TemplateResponse` will call the
:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -286,12 +285,17 @@
for the paginated list of books can hang off the publisher found as the single
object. In order to do this, we need to have two different querysets:
-**Publisher queryset for use in get_object**
- We'll set that up directly when we call ``get_object()``.
+**``Publisher`` queryset for use in ``get_object``**
+ We'll set the ``model`` attribute on the view and rely on the default
+ implementation of ``get_object()`` to fetch the correct ``Publisher``
+ object.
-**Book queryset for use by ListView**
- We'll figure that out ourselves in ``get_queryset()`` so we
- can take into account the ``Publisher`` we're looking at.
+**``Book`` queryset for use by ``ListView``**
+ The default implementation of ``get_queryset`` uses the ``model`` attribute
+ to construct the queryset. This conflicts with our use of this attribute
+ for ``get_object`` so we'll override that method and have it return
+ the queryset of ``Book`` objects linked to the ``Publisher`` we're looking
+ at.
.. note::
@@ -300,7 +304,7 @@
:class:`ListView` will
put things in the context data under the value of
``context_object_name`` if it's set, we'll instead explictly
- ensure the Publisher is in the context data. :class:`ListView`
+ ensure the ``Publisher`` is in the context data. :class:`ListView`
will add in the suitable ``page_obj`` and ``paginator`` for us
providing we remember to call ``super()``.
@@ -311,31 +315,36 @@
from books.models import Publisher
class PublisherDetail(SingleObjectMixin, ListView):
+ model = Publisher # for SingleObjectMixin.get_object
paginate_by = 2
template_name = "books/publisher_detail.html"
+ def get(self, request, *args, **kwargs):
+ self.object = self.get_object()
+ return super(PublisherDetail, self).get(request, *args, **kwargs)
+
def get_context_data(self, **kwargs):
- kwargs['publisher'] = self.object
- return super(PublisherDetail, self).get_context_data(**kwargs)
+ context = super(PublisherDetail, self).get_context_data(**kwargs)
+ context['publisher'] = self.object
+ return context
def get_queryset(self):
- self.object = self.get_object(Publisher.objects.all())
return self.object.book_set.all()
-Notice how we set ``self.object`` within ``get_queryset()`` so we
-can use it again later in ``get_context_data()``. If you don't set
-``template_name``, the template will default to the normal
+Notice how we set ``self.object`` within ``get()`` so we
+can use it again later in ``get_context_data()`` and ``get_queryset()``.
+If you don't set ``template_name``, the template will default to the normal
:class:`ListView` choice, which in this case would be
``"books/book_list.html"`` because it's a list of books;
:class:`ListView` knows nothing about
:class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't have
-any clue this view is anything to do with a Publisher.
-
-.. highlightlang:: html+django
+any clue this view is anything to do with a ``Publisher``.
The ``paginate_by`` is deliberately small in the example so you don't
have to create lots of books to see the pagination working! Here's the
-template you'd want to use::
+template you'd want to use:
+
+.. code-block:: html+django
{% extends "base.html" %}
@@ -427,8 +436,6 @@
both of the views implement ``get()``, and things would get much more
confusing.
-.. highlightlang:: python
-
Our new ``AuthorDetail`` looks like this::
# CAUTION: you almost certainly do not want to do this.
@@ -450,21 +457,18 @@
form_class = AuthorInterestForm
def get_success_url(self):
- return reverse(
- 'author-detail',
- kwargs = {'pk': self.object.pk},
- )
+ return reverse('author-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
+ context = super(AuthorDetail, self).get_context_data(**kwargs)
form_class = self.get_form_class()
- form = self.get_form(form_class)
- context = {
- 'form': form
- }
- context.update(kwargs)
- return super(AuthorDetail, self).get_context_data(**context)
+ context['form'] = self.get_form(form_class)
+ return context
def post(self, request, *args, **kwargs):
+ if not request.user.is_authenticated():
+ return HttpResponseForbidden()
+ self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
@@ -473,10 +477,8 @@
return self.form_invalid(form)
def form_valid(self, form):
- if not self.request.user.is_authenticated():
- return HttpResponseForbidden()
- self.object = self.get_object()
- # record the interest using the message in form.cleaned_data
+ # Here, we would record the user's interest using the message
+ # passed in form.cleaned_data['message']
return super(AuthorDetail, self).form_valid(form)
``get_success_url()`` is just providing somewhere to redirect to,
@@ -529,15 +531,12 @@
message = forms.CharField()
class AuthorDisplay(DetailView):
-
- queryset = Author.objects.all()
+ model = Author
def get_context_data(self, **kwargs):
- context = {
- 'form': AuthorInterestForm(),
- }
- context.update(kwargs)
- return super(AuthorDisplay, self).get_context_data(**context)
+ context = super(AuthorDisplay, self).get_context_data(**kwargs)
+ context['form'] = AuthorInterestForm()
+ return context
Then the ``AuthorInterest`` is a simple :class:`FormView`, but we
have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we
@@ -555,24 +554,14 @@
form_class = AuthorInterestForm
model = Author
- def get_context_data(self, **kwargs):
- context = {
- 'object': self.get_object(),
- }
- return super(AuthorInterest, self).get_context_data(**context)
-
- def get_success_url(self):
- return reverse(
- 'author-detail',
- kwargs = {'pk': self.object.pk},
- )
-
- def form_valid(self, form):
- if not self.request.user.is_authenticated():
+ def post(self, request, *args, **kwargs):
+ if not request.user.is_authenticated():
return HttpResponseForbidden()
self.object = self.get_object()
- # record the interest using the message in form.cleaned_data
- return super(AuthorInterest, self).form_valid(form)
+ return super(AuthorInterest, self).post(request, *args, **kwargs)
+
+ def get_success_url(self):
+ return reverse('author-detail', kwargs={'pk': self.object.pk})
Finally we bring this together in a new ``AuthorDetail`` view. We
already know that calling :meth:`~django.views.generic.base.View.as_view()` on
diff --git a/lib/django-1.5/docs/topics/conditional-view-processing.txt b/lib/django-1.5/docs/topics/conditional-view-processing.txt
index caa7376..83b454a 100644
--- a/lib/django-1.5/docs/topics/conditional-view-processing.txt
+++ b/lib/django-1.5/docs/topics/conditional-view-processing.txt
@@ -147,7 +147,7 @@
The ``condition`` decorator is useful for more than only ``GET`` and
``HEAD`` requests (``HEAD`` requests are the same as ``GET`` in this
-situation). It can be used also to be used to provide checking for ``POST``,
+situation). It can also be used to provide checking for ``POST``,
``PUT`` and ``DELETE`` requests. In these situations, the idea isn't to return
a "not modified" response, but to tell the client that the resource they are
trying to change has been altered in the meantime.
diff --git a/lib/django-1.5/docs/topics/db/aggregation.txt b/lib/django-1.5/docs/topics/db/aggregation.txt
index 49134e2..125cd0b 100644
--- a/lib/django-1.5/docs/topics/db/aggregation.txt
+++ b/lib/django-1.5/docs/topics/db/aggregation.txt
@@ -43,7 +43,9 @@
Cheat sheet
===========
-In a hurry? Here's how to do common aggregate queries, assuming the models above::
+In a hurry? Here's how to do common aggregate queries, assuming the models above:
+
+.. code-block:: python
# Total number of books.
>>> Book.objects.count()
@@ -140,8 +142,10 @@
The syntax for these annotations is identical to that used for the
``aggregate()`` clause. Each argument to ``annotate()`` describes an
-aggregate that is to be calculated. For example, to annotate Books with
-the number of authors::
+aggregate that is to be calculated. For example, to annotate books with
+the number of authors:
+
+.. code-block:: python
# Build an annotated queryset
>>> q = Book.objects.annotate(Count('authors'))
@@ -190,8 +194,8 @@
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))
-This tells Django to retrieve the Store model, join (through the
-many-to-many relationship) with the Book model, and aggregate on the
+This tells Django to retrieve the ``Store`` model, join (through the
+many-to-many relationship) with the ``Book`` model, and aggregate on the
price field of the book model to produce a minimum and maximum value.
The same rules apply to the ``aggregate()`` clause. If you wanted to
@@ -215,32 +219,32 @@
of related models and double-underscores are used here too.
For example, we can ask for all publishers, annotated with their respective
-total book stock counters (note how we use `'book'` to specify the
-Publisher->Book reverse foreign key hop)::
+total book stock counters (note how we use ``'book'`` to specify the
+``Publisher`` -> ``Book`` reverse foreign key hop)::
>>> from django.db.models import Count, Min, Sum, Max, Avg
>>> Publisher.objects.annotate(Count('book'))
-(Every Publisher in the resulting QuerySet will have an extra attribute called
-``book__count``.)
+(Every ``Publisher`` in the resulting ``QuerySet`` will have an extra attribute
+called ``book__count``.)
We can also ask for the oldest book of any of those managed by every publisher::
>>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))
(The resulting dictionary will have a key called ``'oldest_pubdate'``. If no
-such alias was specified, it would be the rather long ``'book__pubdate__min'``.)
+such alias were specified, it would be the rather long ``'book__pubdate__min'``.)
This doesn't apply just to foreign keys. It also works with many-to-many
relations. For example, we can ask for every author, annotated with the total
number of pages considering all the books he/she has (co-)authored (note how we
-use `'book'` to specify the Author->Book reverse many-to-many hop)::
+use ``'book'`` to specify the ``Author`` -> ``Book`` reverse many-to-many hop)::
>>> Author.objects.annotate(total_pages=Sum('book__pages'))
-(Every Author in the resulting QuerySet will have an extra attribute called
-``total_pages``. If no such alias was specified, it would be the rather long
-``book__pages__sum``.)
+(Every ``Author`` in the resulting ``QuerySet`` will have an extra attribute
+called ``total_pages``. If no such alias were specified, it would be the rather
+long ``book__pages__sum``.)
Or ask for the average rating of all the books written by author(s) we have on
file::
@@ -248,7 +252,7 @@
>>> Author.objects.aggregate(average_rating=Avg('book__rating'))
(The resulting dictionary will have a key called ``'average__rating'``. If no
-such alias was specified, it would be the rather long ``'book__rating__avg'``.)
+such alias were specified, it would be the rather long ``'book__rating__avg'``.)
Aggregations and other QuerySet clauses
=======================================
@@ -308,7 +312,7 @@
>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))
-Both queries will return a list of Publishers that have at least one good
+Both queries will return a list of publishers that have at least one good
book (i.e., a book with a rating exceeding 3.0). However, the annotation in
the first query will provide the total number of all books published by the
publisher; the second query will only include good books in the annotated
diff --git a/lib/django-1.5/docs/topics/db/managers.txt b/lib/django-1.5/docs/topics/db/managers.txt
index a14616a..a8b0dff 100644
--- a/lib/django-1.5/docs/topics/db/managers.txt
+++ b/lib/django-1.5/docs/topics/db/managers.txt
@@ -70,8 +70,8 @@
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
- GROUP BY 1, 2, 3
- ORDER BY 3 DESC""")
+ GROUP BY p.id, p.question, p.poll_date
+ ORDER BY p.poll_date DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
@@ -85,7 +85,7 @@
objects = PollManager()
class Response(models.Model):
- poll = models.ForeignKey(Poll)
+ poll = models.ForeignKey(OpinionPoll)
person_name = models.CharField(max_length=50)
response = models.TextField()
@@ -188,7 +188,7 @@
If the normal plain manager class (:class:`django.db.models.Manager`) is not
appropriate for your circumstances, you can force Django to use the same class
-as the default manager for your model by setting the `use_for_related_fields`
+as the default manager for your model by setting the ``use_for_related_fields``
attribute on the manager class. This is documented fully below_.
.. _below: manager-types_
@@ -366,7 +366,7 @@
Writing correct Managers for use in automatic Manager instances
---------------------------------------------------------------
-As already suggested by the `django.contrib.gis` example, above, the
+As already suggested by the :mod:`django.contrib.gis` example, above, the
``use_for_related_fields`` feature is primarily for managers that need to
return a custom ``QuerySet`` subclass. In providing this functionality in your
manager, there are a couple of things to remember.
@@ -410,4 +410,3 @@
is created and not subsequently reread. Set the attribute on the manager class
when it is first defined, as in the initial example of this section and
everything will work smoothly.
-
diff --git a/lib/django-1.5/docs/topics/db/models.txt b/lib/django-1.5/docs/topics/db/models.txt
index c4db0d7..58c77bc 100644
--- a/lib/django-1.5/docs/topics/db/models.txt
+++ b/lib/django-1.5/docs/topics/db/models.txt
@@ -180,7 +180,7 @@
('L', 'Large'),
)
name = models.CharField(max_length=60)
- shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
+ shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
::
diff --git a/lib/django-1.5/docs/topics/db/optimization.txt b/lib/django-1.5/docs/topics/db/optimization.txt
index b5cca52..c1459ae 100644
--- a/lib/django-1.5/docs/topics/db/optimization.txt
+++ b/lib/django-1.5/docs/topics/db/optimization.txt
@@ -157,7 +157,7 @@
>>> entry = Entry.objects.get(headline__startswith="News")
-First of all, `headline` is not indexed, which will make the underlying
+First of all, ``headline`` is not indexed, which will make the underlying
database fetch slower.
Second, the lookup doesn't guarantee that only one object will be returned.
diff --git a/lib/django-1.5/docs/topics/db/queries.txt b/lib/django-1.5/docs/topics/db/queries.txt
index f28fb54..e14849b 100644
--- a/lib/django-1.5/docs/topics/db/queries.txt
+++ b/lib/django-1.5/docs/topics/db/queries.txt
@@ -100,7 +100,8 @@
Updating a :class:`~django.db.models.ForeignKey` field works exactly the same
way as saving a normal field -- simply assign an object of the right type to
the field in question. This example updates the ``blog`` attribute of an
-``Entry`` instance ``entry``::
+``Entry`` instance ``entry``, assuming appropriate instances of ``Entry`` and
+``Blog`` are already saved to the database (so we can retrieve them below)::
>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
@@ -297,8 +298,8 @@
:class:`~django.db.models.query.QuerySet` containing a single element.
If you know there is only one object that matches your query, you can use the
-:meth:`~django.db.models.query.QuerySet.get` method on a `Manager` which
-returns the object directly::
+:meth:`~django.db.models.query.QuerySet.get` method on a
+:class:`~django.db.models.Manager` which returns the object directly::
>>> one_entry = Entry.objects.get(pk=1)
@@ -710,9 +711,9 @@
Caching and QuerySets
---------------------
-Each :class:`~django.db.models.query.QuerySet` contains a cache, to minimize
-database access. It's important to understand how it works, in order to write
-the most efficient code.
+Each :class:`~django.db.models.query.QuerySet` contains a cache to minimize
+database access. Understanding how it works will allow you to write the most
+efficient code.
In a newly created :class:`~django.db.models.query.QuerySet`, the cache is
empty. The first time a :class:`~django.db.models.query.QuerySet` is evaluated
@@ -743,6 +744,43 @@
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
+When querysets are not cached
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Querysets do not always cache their results. When evaluating only *part* of
+the queryset, the cache is checked, but if it is not populated then the items
+returned by the subsequent query are not cached. Specifically, this means that
+:ref:`limiting the queryset <limiting-querysets>` using an array slice or an
+index will not populate the cache.
+
+For example, repeatedly getting a certain index in a queryset object will query
+the database each time::
+
+ >>> queryset = Entry.objects.all()
+ >>> print queryset[5] # Queries the database
+ >>> print queryset[5] # Queries the database again
+
+However, if the entire queryset has already been evaluated, the cache will be
+checked instead::
+
+ >>> queryset = Entry.objects.all()
+ >>> [entry for entry in queryset] # Queries the database
+ >>> print queryset[5] # Uses cache
+ >>> print queryset[5] # Uses cache
+
+Here are some examples of other actions that will result in the entire queryset
+being evaluated and therefore populate the cache::
+
+ >>> [entry for entry in queryset]
+ >>> bool(queryset)
+ >>> entry in queryset
+ >>> list(queryset)
+
+.. note::
+
+ Simply printing the queryset will not populate the cache. This is because
+ the call to ``__repr__()`` only returns a slice of the entire queryset.
+
.. _complex-lookups-with-q:
Complex lookups with Q objects
@@ -825,7 +863,7 @@
The `OR lookups examples`_ in the Django unit tests show some possible uses
of ``Q``.
- .. _OR lookups examples: https://github.com/django/django/blob/master/tests/modeltests/or_lookups/tests.py
+ .. _OR lookups examples: https://github.com/django/django/blob/master/tests/or_lookups/tests.py
Comparing objects
=================
diff --git a/lib/django-1.5/docs/topics/files.txt b/lib/django-1.5/docs/topics/files.txt
index 94685f9..96e0557 100644
--- a/lib/django-1.5/docs/topics/files.txt
+++ b/lib/django-1.5/docs/topics/files.txt
@@ -2,7 +2,10 @@
Managing files
==============
-This document describes Django's file access APIs.
+This document describes Django's file access APIs for files such as those
+uploaded by a user. The lower level APIs are general enough that you could use
+them for other purposes. If you want to handle "static files" (JS, CSS, etc),
+see :doc:`/howto/static-files/index`.
By default, Django stores files locally, using the :setting:`MEDIA_ROOT` and
:setting:`MEDIA_URL` settings. The examples below assume that you're using these
@@ -82,16 +85,16 @@
# Create a Python file object using open() and the with statement
>>> with open('/tmp/hello.world', 'w') as f:
- >>> myfile = File(f)
- >>> for line in myfile:
- >>> print line
+ ... myfile = File(f)
+ ... myfile.write('Hello World')
+ ...
>>> myfile.closed
True
>>> f.closed
True
Closing files is especially important when accessing file fields in a loop
-over a large number of objects:: If files are not manually closed after
+over a large number of objects. If files are not manually closed after
accessing them, the risk of running out of file descriptors may arise. This
may lead to the following error::
diff --git a/lib/django-1.5/docs/topics/forms/formsets.txt b/lib/django-1.5/docs/topics/forms/formsets.txt
index e315220..ac8741d 100644
--- a/lib/django-1.5/docs/topics/forms/formsets.txt
+++ b/lib/django-1.5/docs/topics/forms/formsets.txt
@@ -3,9 +3,9 @@
Formsets
========
-.. class:: django.forms.formset.BaseFormSet
+.. class:: django.forms.formsets.BaseFormSet
-A formset is a layer of abstraction to working with multiple forms on the same
+A formset is a layer of abstraction to work with multiple forms on the same
page. It can be best compared to a data grid. Let's say you have the following
form::
@@ -180,7 +180,10 @@
It is used to keep track of how many form instances are being displayed. If
you are adding new forms via JavaScript, you should increment the count fields
-in this form as well.
+in this form as well. On the other hand, if you are using JavaScript to allow
+deletion of existing objects, then you need to ensure the ones being removed
+are properly marked for deletion by including ``form-#-DELETE`` in the ``POST``
+data. It is expected that all forms are present in the ``POST`` data regardless.
The management form is available as an attribute of the formset
itself. When rendering a formset in a template, you can include all
@@ -320,7 +323,7 @@
Default: ``False``
-Lets you create a formset with the ability to delete::
+Lets you create a formset with the ability to select forms for deletion::
>>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
>>> formset = ArticleFormSet(initial=[
@@ -366,6 +369,13 @@
>>> [form.cleaned_data for form in formset.deleted_forms]
[{'DELETE': True, 'pub_date': datetime.date(2008, 5, 10), 'title': u'Article #1'}]
+If you are using a :class:`ModelFormSet<django.forms.models.BaseModelFormSet>`,
+model instances for deleted forms will be deleted when you call
+``formset.save()``. On the other hand, if you are using a plain ``FormSet``,
+it's up to you to handle ``formset.deleted_forms``, perhaps in your formset's
+``save()`` method, as there's no general notion of what it means to delete a
+form.
+
Adding additional fields to a formset
-------------------------------------
@@ -457,8 +467,8 @@
</form>
-Similarly, if the formset has the ability to order (``can_order=True``), it is possible to render it
-with ``{{ form.ORDER }}``.
+Similarly, if the formset has the ability to order (``can_order=True``), it is
+possible to render it with ``{{ form.ORDER }}``.
Using more than one formset in a view
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/lib/django-1.5/docs/topics/forms/media.txt b/lib/django-1.5/docs/topics/forms/media.txt
index 98e70e5..71c6b6a 100644
--- a/lib/django-1.5/docs/topics/forms/media.txt
+++ b/lib/django-1.5/docs/topics/forms/media.txt
@@ -255,7 +255,7 @@
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
- >>> print(w.media)['css']
+ >>> print(w.media['css'])
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
When you use the subscript operator, the value that is returned is a new
diff --git a/lib/django-1.5/docs/topics/forms/modelforms.txt b/lib/django-1.5/docs/topics/forms/modelforms.txt
index 7251dc9..34fb9b9 100644
--- a/lib/django-1.5/docs/topics/forms/modelforms.txt
+++ b/lib/django-1.5/docs/topics/forms/modelforms.txt
@@ -17,7 +17,7 @@
case, it would be redundant to define the field types in your form, because
you've already defined the fields in your model.
-For this reason, Django provides a helper class that let you create a ``Form``
+For this reason, Django provides a helper class that lets you create a ``Form``
class from a Django model.
For example::
@@ -71,7 +71,7 @@
``FileField`` ``FileField``
-``FilePathField`` ``CharField``
+``FilePathField`` ``FilePathField``
``FloatField`` ``FloatField``
@@ -189,29 +189,75 @@
name = forms.CharField(max_length=100)
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
-.. _modelform-is-valid-and-errors:
+.. _validation-on-modelform:
-The ``is_valid()`` method and ``errors``
-----------------------------------------
+Validation on a ``ModelForm``
+-----------------------------
-The first time you call ``is_valid()`` or access the ``errors`` attribute of a
-``ModelForm`` triggers :ref:`form validation <form-and-field-validation>` as
-well as :ref:`model validation <validating-objects>`. This has the side-effect
-of cleaning the model you pass to the ``ModelForm`` constructor. For instance,
-calling ``is_valid()`` on your form will convert any date fields on your model
-to actual date objects. If form validation fails, only some of the updates
-may be applied. For this reason, you'll probably want to avoid reusing the
-model instance passed to the form, especially if validation fails.
+There are two main steps involved in validating a ``ModelForm``:
+1. :ref:`Validating the form <form-and-field-validation>`
+2. :ref:`Validating the model instance <validating-objects>`
+
+Just like normal form validation, model form validation is triggered implicitly
+when calling :meth:`~django.forms.Form.is_valid()` or accessing the
+:attr:`~django.forms.Form.errors` attribute and explicitly when calling
+``full_clean()``, although you will typically not use the latter method in
+practice.
+
+``Model`` validation (:meth:`Model.full_clean()
+<django.db.models.Model.full_clean()>`) is triggered from within the form
+validation step, right after the form's ``clean()`` method is called.
+
+.. warning::
+
+ The cleaning process modifies the model instance passed to the
+ ``ModelForm`` constructor in various ways. For instance, any date fields on
+ the model are converted into actual date objects. Failed validation may
+ leave the underlying model instance in an inconsistent state and therefore
+ it's not recommended to reuse it.
+
+.. _overriding-modelform-clean-method:
+
+Overriding the clean() method
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You can override the ``clean()`` method on a model form to provide additional
+validation in the same way you can on a normal form.
+
+A model form instance bound to a model object will contain an ``instance``
+attribute that gives its methods access to that specific model instance.
+
+.. warning::
+
+ The ``ModelForm.clean()`` method sets a flag that makes the :ref:`model
+ validation <validating-objects>` step validate the uniqueness of model
+ fields that are marked as ``unique``, ``unique_together`` or
+ ``unique_for_date|month|year``.
+
+ If you would like to override the ``clean()`` method and maintain this
+ validation, you must call the parent class's ``clean()`` method.
+
+Interaction with model validation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+As part of the validation process, ``ModelForm`` will call the ``clean()``
+method of each field on your model that has a corresponding field on your form.
+If you have excluded any model fields, validation will not be run on those
+fields. See the :doc:`form validation </ref/forms/validation>` documentation
+for more on how field cleaning and validation work.
+
+The model's ``clean()`` method will be called before any uniqueness checks are
+made. See :ref:`Validating objects <validating-objects>` for more information
+on the model's ``clean()`` hook.
The ``save()`` method
---------------------
-Every form produced by ``ModelForm`` also has a ``save()``
-method. This method creates and saves a database object from the data
-bound to the form. A subclass of ``ModelForm`` can accept an existing
-model instance as the keyword argument ``instance``; if this is
-supplied, ``save()`` will update that instance. If it's not supplied,
+Every ``ModelForm`` also has a ``save()`` method. This method creates and saves
+a database object from the data bound to the form. A subclass of ``ModelForm``
+can accept an existing model instance as the keyword argument ``instance``; if
+this is supplied, ``save()`` will update that instance. If it's not supplied,
``save()`` will create a new instance of the specified model:
.. code-block:: python
@@ -229,7 +275,7 @@
>>> f.save()
Note that if the form :ref:`hasn't been validated
-<modelform-is-valid-and-errors>`, calling ``save()`` will do so by checking
+<validation-on-modelform>`, calling ``save()`` will do so by checking
``form.errors``. A ``ValueError`` will be raised if the data in the form
doesn't validate -- i.e., if ``form.errors`` evaluates to ``True``.
@@ -252,7 +298,9 @@
To work around this problem, every time you save a form using ``commit=False``,
Django adds a ``save_m2m()`` method to your ``ModelForm`` subclass. After
you've manually saved the instance produced by the form, you can invoke
-``save_m2m()`` to save the many-to-many form data. For example::
+``save_m2m()`` to save the many-to-many form data. For example:
+
+.. code-block:: python
# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)
@@ -272,7 +320,9 @@
Calling ``save_m2m()`` is only required if you use ``save(commit=False)``.
When you use a simple ``save()`` on a form, all data -- including
many-to-many data -- is saved without the need for any additional method calls.
-For example::
+For example:
+
+.. code-block:: python
# Create a form instance with POST data.
>>> a = Author()
@@ -468,27 +518,6 @@
... model = Book
... fields = ('title', 'author')
-.. _overriding-modelform-clean-method:
-
-Overriding the clean() method
------------------------------
-
-You can override the ``clean()`` method on a model form to provide additional
-validation in the same way you can on a normal form.
-
-In this regard, model forms have two specific characteristics when compared to
-forms:
-
-By default the ``clean()`` method validates the uniqueness of fields that are
-marked as ``unique``, ``unique_together`` or ``unique_for_date|month|year`` on
-the model. Therefore, if you would like to override the ``clean()`` method and
-maintain the default validation, you must call the parent class's ``clean()``
-method.
-
-Also, a model form instance bound to a model object will contain a
-``self.instance`` attribute that gives model form methods access to that
-specific model instance.
-
Form inheritance
----------------
@@ -527,18 +556,6 @@
Chances are these notes won't affect you unless you're trying to do something
tricky with subclassing.
-Interaction with model validation
----------------------------------
-
-As part of its validation process, ``ModelForm`` will call the ``clean()``
-method of each field on your model that has a corresponding field on your form.
-If you have excluded any model fields, validation will not be run on those
-fields. See the :doc:`form validation </ref/forms/validation>` documentation
-for more on how field cleaning and validation work. Also, your model's
-``clean()`` method will be called before any uniqueness checks are made. See
-:ref:`Validating objects <validating-objects>` for more information on the
-model's ``clean()`` hook.
-
.. _modelforms-factory:
ModelForm factory function
@@ -754,14 +771,16 @@
``formset.save()`` to save the data into the database. (This was described
above, in :ref:`saving-objects-in-the-formset`.)
-Overiding ``clean()`` on a ``model_formset``
+.. _model-formsets-overriding-clean:
+
+Overriding ``clean()`` on a ``ModelFormSet``
--------------------------------------------
Just like with ``ModelForms``, by default the ``clean()`` method of a
-``model_formset`` will validate that none of the items in the formset violate
+``ModelFormSet`` will validate that none of the items in the formset violate
the unique constraints on your model (either ``unique``, ``unique_together`` or
``unique_for_date|month|year``). If you want to override the ``clean()`` method
-on a ``model_formset`` and maintain this validation, you must call the parent
+on a ``ModelFormSet`` and maintain this validation, you must call the parent
class's ``clean`` method::
class MyModelFormSet(BaseModelFormSet):
@@ -859,6 +878,8 @@
Inline formsets
===============
+.. class:: models.BaseInlineFormSet
+
Inline formsets is a small abstraction layer on top of model formsets. These
simplify the case of working with related objects via a foreign key. Suppose
you have these two models::
@@ -888,6 +909,13 @@
:ref:`Manually rendered can_delete and can_order <manually-rendered-can-delete-and-can-order>`.
+Overriding ``clean()`` on an ``InlineFormSet``
+----------------------------------------------
+
+See :ref:`model-formsets-overriding-clean`, but subclass
+:class:`~models.BaseInlineFormSet` rather than
+:class:`~models.BaseModelFormSet`.
+
More than one foreign key to the same model
-------------------------------------------
diff --git a/lib/django-1.5/docs/topics/http/file-uploads.txt b/lib/django-1.5/docs/topics/http/file-uploads.txt
index 80bd5f3..457c2aa 100644
--- a/lib/django-1.5/docs/topics/http/file-uploads.txt
+++ b/lib/django-1.5/docs/topics/http/file-uploads.txt
@@ -369,8 +369,8 @@
``receive_data_chunk`` methods. In this way, one handler can be a
"filter" for other handlers.
- Return ``None`` from ``receive_data_chunk`` to sort-circuit remaining
- upload handlers from getting this chunk.. This is useful if you're
+ Return ``None`` from ``receive_data_chunk`` to short-circuit remaining
+ upload handlers from getting this chunk. This is useful if you're
storing the uploaded data yourself and don't want future handlers to
store a copy of the data.
diff --git a/lib/django-1.5/docs/topics/http/sessions.txt b/lib/django-1.5/docs/topics/http/sessions.txt
index dac146b..039a775 100644
--- a/lib/django-1.5/docs/topics/http/sessions.txt
+++ b/lib/django-1.5/docs/topics/http/sessions.txt
@@ -124,6 +124,18 @@
.. warning::
+ **If the SECRET_KEY is not kept secret and you are using the**
+ :class:`~django.contrib.sessions.serializers.PickleSerializer`, **this can
+ lead to arbitrary remote code execution.**
+
+ An attacker in possession of the :setting:`SECRET_KEY` can not only
+ generate falsified session data, which your site will trust, but also
+ remotely execute arbitrary code, as the data is serialized using pickle.
+
+ If you use cookie-based sessions, pay extra care that your secret key is
+ always kept completely secret, for any system which might be remotely
+ accessible.
+
**The session data is signed but not encrypted**
When using the cookies backend the session data can be read by the client.
@@ -241,7 +253,9 @@
in 5 minutes.
* If ``value`` is a ``datetime`` or ``timedelta`` object, the
- session will expire at that specific date/time.
+ session will expire at that specific date/time. Note that ``datetime``
+ and ``timedelta`` values are only serializable if you are using the
+ :class:`~django.contrib.sessions.serializers.PickleSerializer`.
* If ``value`` is ``0``, the user's session cookie will expire
when the user's Web browser is closed.
@@ -288,6 +302,87 @@
Removes expired sessions from the session store. This class method is
called by :djadmin:`clearsessions`.
+.. _session_serialization:
+
+Session serialization
+---------------------
+
+.. versionadded:: 1.5.3
+
+Before version 1.6, Django defaulted to using :mod:`pickle` to serialize
+session data before storing it in the backend. If you're using the :ref:`signed
+cookie session backend<cookie-session-backend>` and :setting:`SECRET_KEY` is
+known by an attacker (there isn't an inherent vulnerability in Django that
+would cause it to leak), the attacker could insert a string into his session
+which, when unpickled, executes arbitrary code on the server. The technique for
+doing so is simple and easily available on the internet. Although the cookie
+session storage signs the cookie-stored data to prevent tampering, a
+:setting:`SECRET_KEY` leak immediately escalates to a remote code execution
+vulnerability.
+
+This attack can be mitigated by serializing session data using JSON rather
+than :mod:`pickle`. To facilitate this, Django 1.5.3 introduced a new setting,
+:setting:`SESSION_SERIALIZER`, to customize the session serialization format.
+For backwards compatibility, this setting defaults to
+using :class:`django.contrib.sessions.serializers.PickleSerializer` in Django
+1.5.x, but, for security hardening, defaults to
+:class:`django.contrib.sessions.serializers.JSONSerializer` in Django 1.6. If
+you upgrade and switch from pickle to JSON, sessions created before the upgrade
+will be lost. Even with the caveats described in :ref:`custom-serializers`, we
+highly recommend using JSON serialization *especially if you are using the
+cookie backend*.
+
+Bundled Serializers
+^^^^^^^^^^^^^^^^^^^
+
+.. class:: serializers.JSONSerializer
+
+ A wrapper around the JSON serializer from :mod:`django.core.signing`. Can
+ only serialize basic data types.
+
+ In addition, as JSON supports only string keys, note that using non-string
+ keys in ``request.session`` won't work as expected::
+
+ >>> # initial assignment
+ >>> request.session[0] = 'bar'
+ >>> # subsequent requests following serialization & deserialization
+ >>> # of session data
+ >>> request.session[0] # KeyError
+ >>> request.session['0']
+ 'bar'
+
+ See the :ref:`custom-serializers` section for more details on limitations
+ of JSON serialization.
+
+.. class:: serializers.PickleSerializer
+
+ Supports arbitrary Python objects, but, as described above, can lead to a
+ remote code execution vulnerability if :setting:`SECRET_KEY` becomes known
+ by an attacker.
+
+.. _custom-serializers:
+
+Write Your Own Serializer
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Note that unlike :class:`~django.contrib.sessions.serializers.PickleSerializer`,
+the :class:`~django.contrib.sessions.serializers.JSONSerializer` cannot handle
+arbitrary Python data types. As is often the case, there is a trade-off between
+convenience and security. If you wish to store more advanced data types
+including ``datetime`` and ``Decimal`` in JSON backed sessions, you will need
+to write a custom serializer (or convert such values to a JSON serializable
+object before storign them in ``request.session``). While serializing these
+values is fairly straightforward
+(``django.core.serializers.json.DateTimeAwareJSONEncoder`` may
+be helpful), writing a decoder that can reliably get back the same thing that
+you put in is more fragile. For example, you run the risk of returning a
+``datetime`` that was actually a string that just happened to be in the same
+format chosen for ``datetime``\s).
+
+Your serializer class must implement two methods,
+``dumps(self, obj)`` and ``loads(self, data)``, to serialize and deserialize
+the dictionary of session data, respectively.
+
Session object guidelines
-------------------------
@@ -377,14 +472,15 @@
>>> from django.contrib.sessions.backends.db import SessionStore
>>> import datetime
>>> s = SessionStore()
- >>> s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10)
+ >>> # stored as seconds since epoch since datetimes are not serializable in JSON.
+ >>> s['last_login'] = 1376587691
>>> s.save()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login']
- datetime.datetime(2005, 8, 20, 13, 35, 0)
+ 1376587691
In order to prevent session fixation attacks, sessions keys that don't exist
are regenerated::
@@ -474,6 +570,16 @@
by explicitly calling the :meth:`~backends.base.SessionBase.set_expiry` method
of ``request.session`` as described above in `using sessions in views`_.
+.. note::
+
+ Some browsers (Chrome, for example) provide settings that allow users to
+ continue browsing sessions after closing and re-opening the browser. In
+ some cases, this can interfere with the
+ :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` setting and prevent sessions
+ from expiring on browser close. Please be aware of this while testing
+ Django applications which have the
+ :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` setting enabled.
+
Clearing the session store
==========================
@@ -613,8 +719,11 @@
Technical details
=================
-* The session dictionary should accept any pickleable Python object. See
- the :mod:`pickle` module for more information.
+* The session dictionary accepts any :mod:`json` serializable value when using
+ :class:`~django.contrib.sessions.serializers.JSONSerializer` or any
+ pickleable Python object when using
+ :class:`~django.contrib.sessions.serializers.PickleSerializer`. See the
+ :mod:`pickle` module for more information.
* Session data is stored in a database table named ``django_session`` .
diff --git a/lib/django-1.5/docs/topics/http/shortcuts.txt b/lib/django-1.5/docs/topics/http/shortcuts.txt
index 68860f1..6f4c78f 100644
--- a/lib/django-1.5/docs/topics/http/shortcuts.txt
+++ b/lib/django-1.5/docs/topics/http/shortcuts.txt
@@ -169,8 +169,9 @@
* A model: the model's `get_absolute_url()` function will be called.
- * A view name, possibly with arguments: `urlresolvers.reverse()` will
- be used to reverse-resolve the name.
+ * A view name, possibly with arguments: :func:`urlresolvers.reverse
+ <django.core.urlresolvers.reverse>` will be used to reverse-resolve the
+ name.
* A URL, which will be used as-is for the redirect location.
@@ -274,8 +275,8 @@
.. function:: get_list_or_404(klass, *args, **kwargs)
Returns the result of :meth:`~django.db.models.query.QuerySet.filter()` on a
- given model manager, raising :class:`~django.http.Http404` if the resulting
- list is empty.
+ given model manager cast to a list, raising :class:`~django.http.Http404` if
+ the resulting list is empty.
Required arguments
------------------
diff --git a/lib/django-1.5/docs/topics/http/urls.txt b/lib/django-1.5/docs/topics/http/urls.txt
index 67a9b37..cfe1f46 100644
--- a/lib/django-1.5/docs/topics/http/urls.txt
+++ b/lib/django-1.5/docs/topics/http/urls.txt
@@ -67,13 +67,13 @@
Here's a sample URLconf::
- from django.conf.urls import patterns
+ from django.conf.urls import patterns, url
urlpatterns = patterns('',
- (r'^articles/2003/$', 'news.views.special_case_2003'),
- (r'^articles/(\d{4})/$', 'news.views.year_archive'),
- (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
- (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
+ url(r'^articles/2003/$', 'news.views.special_case_2003'),
+ url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
Notes:
@@ -125,10 +125,10 @@
Here's the above example URLconf, rewritten to use named groups::
urlpatterns = patterns('',
- (r'^articles/2003/$', 'news.views.special_case_2003'),
- (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
- (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
- (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
+ url(r'^articles/2003/$', 'news.views.special_case_2003'),
+ url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
+ url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
)
This accomplishes exactly the same thing as the previous example, with one
@@ -184,7 +184,7 @@
of what sort of match the regular expression makes. For example, in this
URLconf line::
- (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
...the ``year`` argument to ``news.views.year_archive()`` will be a string, not
an integer, even though the ``\d{4}`` will only match integer strings.
@@ -194,13 +194,14 @@
# URLconf
urlpatterns = patterns('',
- (r'^blog/$', 'blog.views.page'),
- (r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
+ url(r'^blog/$', 'blog.views.page'),
+ url(r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
)
# View (in blog/views.py)
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
+ ...
In the above example, both URL patterns point to the same view --
``blog.views.page`` -- but the first pattern doesn't capture anything from the
@@ -259,12 +260,12 @@
Here's the example URLconf from the :doc:`Django overview </intro/overview>`::
- from django.conf.urls import patterns
+ from django.conf.urls import patterns, url
urlpatterns = patterns('',
- (r'^articles/(\d{4})/$', 'news.views.year_archive'),
- (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
- (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
+ url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
In this example, each view has a common prefix -- ``'news.views'``.
@@ -274,12 +275,12 @@
With this in mind, the above example can be written more concisely as::
- from django.conf.urls import patterns
+ from django.conf.urls import patterns, url
urlpatterns = patterns('news.views',
- (r'^articles/(\d{4})/$', 'year_archive'),
- (r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
- (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
+ url(r'^articles/(\d{4})/$', 'year_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)
Note that you don't put a trailing dot (``"."``) in the prefix. Django puts
@@ -295,25 +296,25 @@
Old::
- from django.conf.urls import patterns
+ from django.conf.urls import patterns, url
urlpatterns = patterns('',
- (r'^$', 'myapp.views.app_index'),
- (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'myapp.views.month_display'),
- (r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
+ url(r'^$', 'myapp.views.app_index'),
+ url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'myapp.views.month_display'),
+ url(r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
)
New::
- from django.conf.urls import patterns
+ from django.conf.urls import patterns, url
urlpatterns = patterns('myapp.views',
- (r'^$', 'app_index'),
- (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),
+ url(r'^$', 'app_index'),
+ url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),
)
urlpatterns += patterns('weblog.views',
- (r'^tag/(?P<tag>\w+)/$', 'tag'),
+ url(r'^tag/(?P<tag>\w+)/$', 'tag'),
)
.. _including-other-urlconfs:
@@ -327,14 +328,14 @@
For example, here's an excerpt of the URLconf for the `Django Web site`_
itself. It includes a number of other URLconfs::
- from django.conf.urls import patterns, include
+ from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
# ... snip ...
- (r'^comments/', include('django.contrib.comments.urls')),
- (r'^community/', include('django_website.aggregator.urls')),
- (r'^contact/', include('django_website.contact.urls')),
- (r'^r/', include('django.conf.urls.shortcut')),
+ url(r'^comments/', include('django.contrib.comments.urls')),
+ url(r'^community/', include('django_website.aggregator.urls')),
+ url(r'^contact/', include('django_website.contact.urls')),
+ url(r'^r/', include('django.conf.urls.shortcut')),
# ... snip ...
)
@@ -349,7 +350,7 @@
directly the pattern list as returned by :func:`~django.conf.urls.patterns`
instead. For example, consider this URLconf::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import include, patterns, url
extra_patterns = patterns('',
url(r'^reports/(?P<id>\d+)/$', 'credit.views.report'),
@@ -358,8 +359,8 @@
urlpatterns = patterns('',
url(r'^$', 'apps.main.views.homepage'),
- (r'^help/', include('apps.help.urls')),
- (r'^credit/', include(extra_patterns)),
+ url(r'^help/', include('apps.help.urls')),
+ url(r'^credit/', include(extra_patterns)),
)
In this example, the ``/credit/reports/`` URL will be handled by the
@@ -375,13 +376,13 @@
# In settings/urls/main.py
urlpatterns = patterns('',
- (r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
+ url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# In foo/urls/blog.py
urlpatterns = patterns('foo.views',
- (r'^$', 'blog.index'),
- (r'^archive/$', 'blog.archive'),
+ url(r'^$', 'blog.index'),
+ url(r'^archive/$', 'blog.archive'),
)
In the above example, the captured ``"username"`` variable is passed to the
@@ -395,17 +396,18 @@
URLconfs have a hook that lets you pass extra arguments to your view functions,
as a Python dictionary.
-Any URLconf tuple can have an optional third element, which should be a
-dictionary of extra keyword arguments to pass to the view function.
+The :func:`django.conf.urls.url` function can take an optional third argument
+which should be a dictionary of extra keyword arguments to pass to the view
+function.
For example::
urlpatterns = patterns('blog.views',
- (r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
+ url(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)
In this example, for a request to ``/blog/2005/``, Django will call
-``blog.views.year_archive(year='2005', foo='bar')``.
+``blog.views.year_archive(request, year='2005', foo='bar')``.
This technique is used in the
:doc:`syndication framework </ref/contrib/syndication>` to pass metadata and
@@ -431,26 +433,26 @@
# main.py
urlpatterns = patterns('',
- (r'^blog/', include('inner'), {'blogid': 3}),
+ url(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
urlpatterns = patterns('',
- (r'^archive/$', 'mysite.views.archive'),
- (r'^about/$', 'mysite.views.about'),
+ url(r'^archive/$', 'mysite.views.archive'),
+ url(r'^about/$', 'mysite.views.about'),
)
Set two::
# main.py
urlpatterns = patterns('',
- (r'^blog/', include('inner')),
+ url(r'^blog/', include('inner')),
)
# inner.py
urlpatterns = patterns('',
- (r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
- (r'^about/$', 'mysite.views.about', {'blogid': 3}),
+ url(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
+ url(r'^about/$', 'mysite.views.about', {'blogid': 3}),
)
Note that extra options will *always* be passed to *every* line in the included
@@ -468,9 +470,9 @@
For example, given this URLconf in "string" notation::
urlpatterns = patterns('',
- (r'^archive/$', 'mysite.views.archive'),
- (r'^about/$', 'mysite.views.about'),
- (r'^contact/$', 'mysite.views.contact'),
+ url(r'^archive/$', 'mysite.views.archive'),
+ url(r'^about/$', 'mysite.views.about'),
+ url(r'^contact/$', 'mysite.views.contact'),
)
You can accomplish the same thing by passing objects rather than strings. Just
@@ -479,9 +481,9 @@
from mysite.views import archive, about, contact
urlpatterns = patterns('',
- (r'^archive/$', archive),
- (r'^about/$', about),
- (r'^contact/$', contact),
+ url(r'^archive/$', archive),
+ url(r'^about/$', about),
+ url(r'^contact/$', contact),
)
The following example is functionally identical. It's just a bit more compact
@@ -491,9 +493,9 @@
from mysite import views
urlpatterns = patterns('',
- (r'^archive/$', views.archive),
- (r'^about/$', views.about),
- (r'^contact/$', views.contact),
+ url(r'^archive/$', views.archive),
+ url(r'^about/$', views.about),
+ url(r'^contact/$', views.contact),
)
The style you use is up to you.
@@ -507,7 +509,7 @@
from mysite.views import ClassBasedView
urlpatterns = patterns('',
- (r'^myview/$', ClassBasedView.as_view()),
+ url(r'^myview/$', ClassBasedView.as_view()),
)
Reverse resolution of URLs
@@ -616,8 +618,8 @@
view::
urlpatterns = patterns('',
- (r'^archive/(\d{4})/$', archive),
- (r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
+ url(r'^archive/(\d{4})/$', archive),
+ url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
)
This is completely valid, but it leads to problems when you try to do reverse
@@ -635,7 +637,7 @@
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive, name="full-archive"),
- url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"),
+ url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, name="arch-summary"),
)
With these names in place (``full-archive`` and ``arch-summary``), you can
@@ -647,7 +649,8 @@
{% url 'full-archive' 2007 %}
Even though both URL patterns refer to the ``archive`` view here, using the
-``name`` parameter to ``url()`` allows you to tell them apart in templates.
+``name`` parameter to :func:`django.conf.urls.url` allows you to tell them
+apart in templates.
The string used for the URL name can contain any characters you like. You are
not restricted to valid Python names.
@@ -790,7 +793,7 @@
:func:`django.conf.urls.include()` when you construct your URL patterns. For
example,::
- (r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
+ url(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
This will include the URLs defined in ``apps.help.urls`` into the
:term:`application namespace` ``'bar'``, with the :term:`instance namespace`
@@ -810,7 +813,7 @@
url(r'^advanced/$', 'apps.help.views.views.advanced'),
)
- (r'^help/', include(help_patterns, 'bar', 'foo')),
+ url(r'^help/', include(help_patterns, 'bar', 'foo')),
This will include the nominated URL patterns into the given application and
instance namespace.
diff --git a/lib/django-1.5/docs/topics/i18n/translation.txt b/lib/django-1.5/docs/topics/i18n/translation.txt
index 3923a58..9e6052e 100644
--- a/lib/django-1.5/docs/topics/i18n/translation.txt
+++ b/lib/django-1.5/docs/topics/i18n/translation.txt
@@ -637,6 +637,25 @@
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
+String literals passed to tags and filters
+------------------------------------------
+
+You can translate string literals passed as arguments to tags and filters
+by using the familiar ``_()`` syntax::
+
+ {% some_tag _("Page not found") value|yesno:_("yes,no") %}
+
+In this case, both the tag and the filter will see the translated string,
+so they don't need to be aware of translations.
+
+.. note::
+ In this example, the translation infrastructure will be passed the string
+ ``"yes,no"``, not the individual strings ``"yes"`` and ``"no"``. The
+ translated string will need to contain the comma so that the filter
+ parsing code knows how to split up the arguments. For example, a German
+ translator might translate the string ``"yes,no"`` as ``"ja,nein"``
+ (keeping the comma intact).
+
.. _translator-comments-in-templates:
Comments for translators in templates
@@ -663,7 +682,7 @@
<button type="submit">{% trans "Go" %}</button>
{# Translators: This is a text of the base template #}
- {% blocktrans %}Ambiguous translatable block of text{% endtransblock %}
+ {% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}
.. note:: Just for completeness, these are the corresponding fragments of the
resulting ``.po`` file:
@@ -694,6 +713,31 @@
msgid "Ambiguous translatable block of text"
msgstr ""
+.. templatetag:: language
+
+Switching language in templates
+-------------------------------
+
+If you want to select a language within a template, you can use the
+``language`` template tag:
+
+.. code-block:: html+django
+
+ {% load i18n %}
+
+ {% get_current_language as LANGUAGE_CODE %}
+ <!-- Current language: {{ LANGUAGE_CODE }} -->
+ <p>{% trans "Welcome to our page" %}</p>
+
+ {% language 'en' %}
+ {% get_current_language as LANGUAGE_CODE %}
+ <!-- Current language: {{ LANGUAGE_CODE }} -->
+ <p>{% trans "Welcome to our page" %}</p>
+ {% endlanguage %}
+
+While the first occurrence of "Welcome to our page" uses the current language,
+the second will always be in English.
+
.. _template-translation-vars:
Other tags
@@ -721,23 +765,6 @@
These tags also require a ``{% load i18n %}``.
-Translation hooks are also available within any template block tag that accepts
-constant strings. In those cases, just use ``_()`` syntax to specify a
-translation string::
-
- {% some_special_tag _("Page not found") value|yesno:_("yes,no") %}
-
-In this case, both the tag and the filter will see the already-translated
-string, so they don't need to be aware of translations.
-
-.. note::
- In this example, the translation infrastructure will be passed the string
- ``"yes,no"``, not the individual strings ``"yes"`` and ``"no"``. The
- translated string will need to contain the comma so that the filter
- parsing code knows how to split up the arguments. For example, a German
- translator might translate the string ``"yes,no"`` as ``"ja,nein"``
- (keeping the comma intact).
-
You can also retrieve information about any of the available languages using
provided template tags and filters. To get information about a single language,
use the ``{% get_language_info %}`` tag::
@@ -1002,11 +1029,11 @@
from django.conf.urls import patterns, include, url
from django.conf.urls.i18n import i18n_patterns
- urlpatterns = patterns(''
+ urlpatterns = patterns('',
url(r'^sitemap\.xml$', 'sitemap.view', name='sitemap_xml'),
)
- news_patterns = patterns(''
+ news_patterns = patterns('',
url(r'^$', 'news.views.index', name='index'),
url(r'^category/(?P<slug>[\w-]+)/$', 'news.views.category', name='category'),
url(r'^(?P<slug>[\w-]+)/$', 'news.views.details', name='detail'),
@@ -1098,13 +1125,11 @@
.. _reversing_in_templates:
-.. templatetag:: language
-
Reversing in templates
----------------------
If localized URLs get reversed in templates they always use the current
-language. To link to a URL in another language use the ``language``
+language. To link to a URL in another language use the :ttag:`language`
template tag. It enables the given language in the enclosed template section:
.. code-block:: html+django
@@ -1176,13 +1201,16 @@
The script should be run from one of two places:
-* The root directory of your Django project.
-* The root directory of your Django app.
+* The root directory of your Django project (the one that contains
+ ``manage.py``).
+* The root directory of one of your Django apps.
The script runs over your project source tree or your application source tree
-and pulls out all strings marked for translation. It creates (or updates) a
-message file in the directory ``locale/LANG/LC_MESSAGES``. In the ``de``
-example, the file will be ``locale/de/LC_MESSAGES/django.po``.
+and pulls out all strings marked for translation (see
+:ref:`how-django-discovers-translations` and be sure :setting:`LOCALE_PATHS`
+is configured correctly). It creates (or updates) a message file in the
+directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the file will be
+``locale/de/LC_MESSAGES/django.po``.
By default :djadmin:`django-admin.py makemessages <makemessages>` examines every
file that has the ``.html`` or ``.txt`` file extension. In case you want to
@@ -1409,7 +1437,9 @@
<select name="language">
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
- <option value="{{ language.code }}">{{ language.name_local }} ({{ language.code }})</option>
+ <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
+ {{ language.name_local }} ({{ language.code }})
+ </option>
{% endfor %}
</select>
<input type="submit" value="Go" />
@@ -1487,14 +1517,17 @@
To set an installation-wide language preference, set :setting:`LANGUAGE_CODE`.
Django uses this language as the default translation -- the final attempt if no
-other translator finds a translation.
+better matching translation is found through one of the methods employed by the
+locale middleware (see below).
-If all you want to do is run Django with your native language, and a language
-file is available for it, all you need to do is set :setting:`LANGUAGE_CODE`.
+If all you want is to run Django with your native language all you need to do
+is set :setting:`LANGUAGE_CODE` and make sure the corresponding :term:`message
+files <message file>` and their compiled versions (``.mo``) exist.
If you want to let each individual user specify which language he or she
-prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language
-selection based on data from the request. It customizes content for each user.
+prefers, then you also need to use use the ``LocaleMiddleware``.
+``LocaleMiddleware`` enables language selection based on data from the request.
+It customizes content for each user.
To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'``
to your :setting:`MIDDLEWARE_CLASSES` setting. Because middleware order
diff --git a/lib/django-1.5/docs/topics/logging.txt b/lib/django-1.5/docs/topics/logging.txt
index e2ce82f..90280dd 100644
--- a/lib/django-1.5/docs/topics/logging.txt
+++ b/lib/django-1.5/docs/topics/logging.txt
@@ -223,8 +223,9 @@
1.5 forward, the project's logging configuration is merged with Django's
defaults, hence you can decide if you want to add to, or replace the existing
configuration. To completely override the default configuration, set the
-``disable_existing_loggers`` key to True in the :setting:`LOGGING`
-dictConfig. Alternatively you can redefine some or all of the loggers.
+``disable_existing_loggers`` key to ``True`` (which is the default) in the
+:setting:`LOGGING` dictConfig. Alternatively you can redefine some or all of
+the loggers by setting ``disable_existing_loggers`` to ``False``.
Logging is configured as soon as settings have been loaded
(either manually using :func:`~django.conf.settings.configure` or when at least
@@ -308,7 +309,7 @@
* ``simple``, that just outputs the log level name (e.g.,
``DEBUG``) and the log message.
- The `format` string is a normal Python formatting string
+ The ``format`` string is a normal Python formatting string
describing the details that are to be output on each logging
line. The full list of detail that can be output can be
found in the `formatter documentation`_.
@@ -330,7 +331,7 @@
higher) message to ``/dev/null``.
* ``console``, a StreamHandler, which will print any ``DEBUG``
- (or higher) message to stderr. This handler uses the `simple` output
+ (or higher) message to stderr. This handler uses the ``simple`` output
format.
* ``mail_admins``, an AdminEmailHandler, which will email any
@@ -531,7 +532,7 @@
This filter is used as follows in the default :setting:`LOGGING`
configuration to ensure that the :class:`AdminEmailHandler` only sends error
- emails to admins when :setting:`DEBUG` is `False`::
+ emails to admins when :setting:`DEBUG` is ``False``::
'filters': {
'require_debug_false': {
@@ -551,7 +552,7 @@
.. versionadded:: 1.5
This filter is similar to :class:`RequireDebugFalse`, except that records are
- passed only when :setting:`DEBUG` is `True`.
+ passed only when :setting:`DEBUG` is ``True``.
.. _default-logging-configuration:
@@ -563,8 +564,8 @@
long as the :setting:`DEBUG` setting is set to ``False``.
All messages reaching the ``django`` catch-all logger when :setting:`DEBUG` is
-`True` are sent ot the console. They are simply discarded (sent to
-``NullHandler``) when :setting:`DEBUG` is `False`.
+``True`` are sent to the console. They are simply discarded (sent to
+``NullHandler``) when :setting:`DEBUG` is ``False``.
.. versionchanged:: 1.5
diff --git a/lib/django-1.5/docs/topics/python3.txt b/lib/django-1.5/docs/topics/python3.txt
index b44c180..633ffe0 100644
--- a/lib/django-1.5/docs/topics/python3.txt
+++ b/lib/django-1.5/docs/topics/python3.txt
@@ -187,7 +187,7 @@
six_ provides compatibility functions to work around this change:
:func:`~six.iterkeys`, :func:`~six.iteritems`, and :func:`~six.itervalues`.
-Django's bundled version adds :func:`~django.utils.six.iterlists` for
+It also contains an undocumented ``iterlists`` function that works well for
``django.utils.datastructures.MultiValueDict`` and its subclasses.
:class:`~django.http.HttpRequest` and :class:`~django.http.HttpResponse` objects
@@ -238,7 +238,7 @@
str('my string')
In Python 3, there aren't any automatic conversions between ``str`` and
-``bytes``, and the :mod:`codecs` module became more strict. :meth:`str.decode`
+``bytes``, and the :mod:`codecs` module became more strict. :meth:`str.encode`
always returns ``bytes``, and ``bytes.decode`` always returns ``str``. As a
consequence, the following pattern is sometimes necessary::
@@ -391,12 +391,7 @@
Customizations of six
---------------------
-The version of six bundled with Django includes one extra function:
-
-.. function:: iterlists(MultiValueDict)
-
- Returns an iterator over the lists of values of a ``MultiValueDict``. This
- replaces ``iterlists()`` on Python 2 and ``lists()`` on Python 3.
+The version of six bundled with Django includes a few extras.
.. function:: assertRaisesRegex(testcase, *args, **kwargs)
diff --git a/lib/django-1.5/docs/topics/serialization.txt b/lib/django-1.5/docs/topics/serialization.txt
index 2af0584..f53a620 100644
--- a/lib/django-1.5/docs/topics/serialization.txt
+++ b/lib/django-1.5/docs/topics/serialization.txt
@@ -90,11 +90,11 @@
data = serializers.serialize('xml', Restaurant.objects.all())
-the fields on the serialized output will only contain the `serves_hot_dogs`
-attribute. The `name` attribute of the base class will be ignored.
+the fields on the serialized output will only contain the ``serves_hot_dogs``
+attribute. The ``name`` attribute of the base class will be ignored.
-In order to fully serialize your Restaurant instances, you will need to
-serialize the Place models as well::
+In order to fully serialize your ``Restaurant`` instances, you will need to
+serialize the ``Place`` models as well::
all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
data = serializers.serialize('xml', all_objects)
@@ -343,7 +343,7 @@
type that defines the method.
If you are using :djadmin:`dumpdata` to generate serialized data, you
-use the `--natural` command line flag to generate natural keys.
+use the :djadminopt:`--natural` command line flag to generate natural keys.
.. note::
@@ -362,7 +362,7 @@
Since natural keys rely on database lookups to resolve references, it
is important that the data exists before it is referenced. You can't make
-a `forward reference` with natural keys -- the data you're referencing
+a "forward reference" with natural keys -- the data you're referencing
must exist before you include a natural key reference to that data.
To accommodate this limitation, calls to :djadmin:`dumpdata` that use
diff --git a/lib/django-1.5/docs/topics/settings.txt b/lib/django-1.5/docs/topics/settings.txt
index fa26297..1cfcd26 100644
--- a/lib/django-1.5/docs/topics/settings.txt
+++ b/lib/django-1.5/docs/topics/settings.txt
@@ -17,6 +17,11 @@
DEFAULT_FROM_EMAIL = 'webmaster@example.com'
TEMPLATE_DIRS = ('/home/templates/mike', '/home/templates/john')
+.. note::
+
+ If you set :setting:`DEBUG` to ``False``, you also need to properly set
+ the :setting:`ALLOWED_HOSTS` setting.
+
Because a settings file is a Python module, the following apply:
* It doesn't allow for Python syntax errors.
diff --git a/lib/django-1.5/docs/topics/signing.txt b/lib/django-1.5/docs/topics/signing.txt
index 671f247..3da70da 100644
--- a/lib/django-1.5/docs/topics/signing.txt
+++ b/lib/django-1.5/docs/topics/signing.txt
@@ -39,8 +39,6 @@
Using the low-level API
=======================
-.. class:: Signer
-
Django's signing methods live in the ``django.core.signing`` module.
To sign a value, first instantiate a ``Signer`` instance::
@@ -76,6 +74,11 @@
>>> value
'My string:EkfQJafvGyiofrdGnuthdxImIJw'
+.. class:: Signer(key=None, sep=':', salt=None)
+
+ Returns a signer which uses ``key`` to generate signatures and ``sep``
+ to separate values.
+
Using the salt argument
-----------------------
@@ -107,8 +110,6 @@
Verifying timestamped values
----------------------------
-.. class:: TimestampSigner
-
``TimestampSigner`` is a subclass of :class:`~Signer` that appends a signed
timestamp to the value. This allows you to confirm that a signed value was
created within a specified period of time::
@@ -126,6 +127,17 @@
>>> signer.unsign(value, max_age=20)
u'hello'
+.. class:: TimestampSigner(key=None, sep=':', salt=None)
+
+ .. method:: sign(value)
+
+ Sign ``value`` and append current timestamp to it.
+
+ .. method:: unsign(value, max_age=None)
+
+ Checks if ``value`` was signed less than ``max_age`` seconds ago,
+ otherwise raises ``SignatureExpired``.
+
Protecting complex data structures
----------------------------------
@@ -144,8 +156,10 @@
.. function:: dumps(obj, key=None, salt='django.core.signing', compress=False)
- Returns URL-safe, sha1 signed base64 compressed JSON string.
+ Returns URL-safe, sha1 signed base64 compressed JSON string. Serialized
+ object is signed using :class:`~TimestampSigner`.
.. function:: loads(string, key=None, salt='django.core.signing', max_age=None)
- Reverse of dumps(), raises ``BadSignature`` if signature fails.
+ Reverse of ``dumps()``, raises ``BadSignature`` if signature fails.
+ Checks ``max_age`` (in seconds) if given.
diff --git a/lib/django-1.5/docs/topics/templates.txt b/lib/django-1.5/docs/topics/templates.txt
index 58a3ee9..c75b83f 100644
--- a/lib/django-1.5/docs/topics/templates.txt
+++ b/lib/django-1.5/docs/topics/templates.txt
@@ -45,7 +45,9 @@
template is evaluated, and **tags**, which control the logic of the template.
Below is a minimal template that illustrates a few basics. Each element will be
-explained later in this document.::
+explained later in this document.
+
+.. code-block:: html+django
{% extends "base_generic.html" %}
diff --git a/lib/django-1.5/docs/topics/testing/advanced.txt b/lib/django-1.5/docs/topics/testing/advanced.txt
index 0674b2e..c07b718 100644
--- a/lib/django-1.5/docs/topics/testing/advanced.txt
+++ b/lib/django-1.5/docs/topics/testing/advanced.txt
@@ -37,18 +37,25 @@
The following is a simple unit test using the request factory::
- from django.utils import unittest
+ from django.contrib.auth.models import User
+ from django.test import TestCase
from django.test.client import RequestFactory
- class SimpleTest(unittest.TestCase):
+ class SimpleTest(TestCase):
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
+ self.user = User.objects.create_user(
+ first_name='jacob', email='jacob@…', password='top_secret')
def test_details(self):
# Create an instance of a GET request.
request = self.factory.get('/customer/details')
+ # Recall that middleware are not suported. You can simulate a
+ # logged-in user by setting request.user manually.
+ request.user = self.user
+
# Test my_view() as if it were deployed at /customer/details
response = my_view(request)
self.assertEqual(response.status_code, 200)
@@ -113,7 +120,8 @@
Controlling creation order for test databases
---------------------------------------------
-By default, Django will always create the ``default`` database first.
+By default, Django will assume all databases depend on the ``default``
+database and therefore always create the ``default`` database first.
However, no guarantees are made on the creation order of any other
databases in your test setup.
@@ -129,6 +137,7 @@
},
'diamonds': {
# ... db settings
+ 'TEST_DEPENDENCIES': []
},
'clubs': {
# ... db settings
@@ -163,10 +172,12 @@
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
-This convenience method sets up the test database, and puts other
-Django features into modes that allow for repeatable testing.
+:func:`~django.test.utils.setup_test_environment` puts several Django features
+into modes that allow for repeatable testing, but does not create the test
+databases; :func:`django.test.simple.DjangoTestSuiteRunner.setup_databases`
+takes care of that.
-The call to :meth:`~django.test.utils.setup_test_environment` is made
+The call to :func:`~django.test.utils.setup_test_environment` is made
automatically as part of the setup of ``./manage.py test``. You only
need to manually invoke this method if you're not using running your
tests via Django's test runner.
@@ -286,7 +297,9 @@
.. method:: DjangoTestSuiteRunner.setup_test_environment(**kwargs)
- Sets up the test environment ready for testing.
+ Sets up the test environment by calling
+ :func:`~django.test.utils.setup_test_environment` and setting
+ :setting:`DEBUG` to ``False``.
.. method:: DjangoTestSuiteRunner.build_suite(test_labels, extra_tests=None, **kwargs)
@@ -344,6 +357,9 @@
Testing utilities
-----------------
+django.test.utils
+~~~~~~~~~~~~~~~~~
+
.. module:: django.test.utils
:synopsis: Helpers to write custom test runners.
@@ -362,10 +378,13 @@
magic hooks into the template system and restoring normal email
services.
+django.db.connection.creation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
.. currentmodule:: django.db.connection.creation
-The creation module of the database backend (``connection.creation``)
-also provides some utilities that can be useful during testing.
+The creation module of the database backend also provides some utilities that
+can be useful during testing.
.. function:: create_test_db([verbosity=1, autoclobber=False])
diff --git a/lib/django-1.5/docs/topics/testing/overview.txt b/lib/django-1.5/docs/topics/testing/overview.txt
index 7528ae1..ea475e0 100644
--- a/lib/django-1.5/docs/topics/testing/overview.txt
+++ b/lib/django-1.5/docs/topics/testing/overview.txt
@@ -56,20 +56,24 @@
directory that holds ``models.py``. Again, the test runner looks for any
subclass of :class:`unittest.TestCase` in this module.
-Here is an example :class:`unittest.TestCase` subclass::
+Here is an example which subclasses from :class:`django.test.TestCase`,
+which is a subclass of :class:`unittest.TestCase` that runs each test inside a
+transaction to provide isolation::
- from django.utils import unittest
+ from django.test import TestCase
from myapp.models import Animal
- class AnimalTestCase(unittest.TestCase):
+ class AnimalTestCase(TestCase):
def setUp(self):
- self.lion = Animal(name="lion", sound="roar")
- self.cat = Animal(name="cat", sound="meow")
+ Animal.objects.create(name="lion", sound="roar")
+ Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
- self.assertEqual(self.lion.speak(), 'The lion says "roar"')
- self.assertEqual(self.cat.speak(), 'The cat says "meow"')
+ lion = Animal.objects.get(name="lion")
+ cat = Animal.objects.get(name="cat")
+ self.assertEqual(lion.speak(), 'The lion says "roar"')
+ self.assertEqual(cat.speak(), 'The cat says "meow"')
When you :ref:`run your tests <running-tests>`, the default behavior of the test
utility is to find all the test cases (that is, subclasses of
@@ -93,11 +97,11 @@
be sure to create your test classes as subclasses of
:class:`django.test.TestCase` rather than :class:`unittest.TestCase`.
- In the example above, we instantiate some models but do not save them to
- the database. Using :class:`unittest.TestCase` avoids the cost of running
- each test in a transaction and flushing the database, but for most
- applications the scope of tests you will be able to write this way will
- be fairly limited, so it's easiest to use :class:`django.test.TestCase`.
+ Using :class:`unittest.TestCase` avoids the cost of running each test in a
+ transaction and flushing the database, but if your tests interact with
+ the database their behavior will vary based on the order that the test
+ runner executes them. This can lead to unit tests that pass when run in
+ isolation but fail when run in a suite.
.. _running-tests:
@@ -358,7 +362,8 @@
everything from low-level HTTP (result headers and status codes) to
page content.
-* Test that the correct view is executed for a given URL.
+* See the chain of redirects (if any) and check the URL and status code at
+ each step.
* Test that a given request is rendered by a given Django template, with
a template context that contains certain values.
@@ -367,8 +372,8 @@
other "in-browser" frameworks. Django's test client has a different focus. In
short:
-* Use Django's test client to establish that the correct view is being
- called and that the view is collecting the correct context data.
+* Use Django's test client to establish that the correct template is being
+ rendered and that the template is passed the correct context data.
* Use in-browser frameworks like Selenium_ to test *rendered* HTML and the
*behavior* of Web pages, namely JavaScript functionality. Django also
@@ -906,14 +911,23 @@
* A ``TestCase``, on the other hand, does not truncate tables after a test.
Instead, it encloses the test code in a database transaction that is rolled
- back at the end of the test. It also prevents the code under test from
- issuing any commit or rollback operations on the database, to ensure that the
- rollback at the end of the test restores the database to its initial state.
+ back at the end of the test. Both explicit commits like
+ ``transaction.commit()`` and implicit ones that may be caused by
+ ``Model.save()`` are replaced with a ``nop`` operation. This guarantees that
+ the rollback at the end of the test restores the database to its initial
+ state.
When running on a database that does not support rollback (e.g. MySQL with the
MyISAM storage engine), ``TestCase`` falls back to initializing the database
by truncating tables and reloading initial data.
+.. warning::
+
+ While ``commit`` and ``rollback`` operations still *appear* to work when
+ used in ``TestCase``, no actual commit or rollback will be performed by the
+ database. This can cause your tests to pass or fail unexpectedly. Always
+ use ``TransactionalTestCase`` when testing transactional behavior.
+
.. note::
.. versionchanged:: 1.5
@@ -965,8 +979,8 @@
Web sites.
Converting a normal :class:`unittest.TestCase` to a Django :class:`TestCase` is
-easy: Just change the base class of your test from `'unittest.TestCase'` to
-`'django.test.TestCase'`. All of the standard Python unit test functionality
+easy: Just change the base class of your test from ``'unittest.TestCase'`` to
+``'django.test.TestCase'``. All of the standard Python unit test functionality
will continue to be available, but it will be augmented with some useful
additions, including:
@@ -1002,7 +1016,7 @@
client, to execute a series of functional tests inside a browser and simulate a
real user's actions.
-By default the live server's address is `'localhost:8081'` and the full URL
+By default the live server's address is ``'localhost:8081'`` and the full URL
can be accessed during the tests with ``self.live_server_url``. If you'd like
to change the default address (in the case, for example, where the 8081 port is
already taken) then you may pass a different one to the :djadmin:`test` command
@@ -1094,7 +1108,7 @@
.. note::
``LiveServerTestCase`` makes use of the :doc:`staticfiles contrib app
- </howto/static-files>` so you'll need to have your project configured
+ </howto/static-files/index>` so you'll need to have your project configured
accordingly (in particular by setting :setting:`STATIC_URL`).
.. note::
@@ -1109,7 +1123,7 @@
(for example, just after clicking a link or submitting a form), you might
need to check that a response is received by Selenium and that the next
page is loaded before proceeding with further test execution.
- Do this, for example, by making Selenium wait until the `<body>` HTML tag
+ Do this, for example, by making Selenium wait until the ``<body>`` HTML tag
is found in the response (requires Selenium > 2.13):
.. code-block:: python
@@ -1126,7 +1140,7 @@
The tricky thing here is that there's really no such thing as a "page load,"
especially in modern Web apps that generate HTML dynamically after the
server generates the initial document. So, simply checking for the presence
- of `<body>` in the response might not necessarily be appropriate for all
+ of ``<body>`` in the response might not necessarily be appropriate for all
use cases. Please refer to the `Selenium FAQ`_ and
`Selenium documentation`_ for more information.
diff --git a/lib/django-1.5/extras/Makefile b/lib/django-1.5/extras/Makefile
new file mode 100644
index 0000000..ff14f40
--- /dev/null
+++ b/lib/django-1.5/extras/Makefile
@@ -0,0 +1,9 @@
+all: sdist bdist_wheel
+
+sdist:
+ python setup.py sdist
+
+bdist_wheel:
+ python -c "import setuptools;__file__='setup.py';exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))" bdist_wheel
+
+.PHONY : sdist bdist_wheel
diff --git a/lib/django-1.5/scripts/manage_translations.py b/lib/django-1.5/scripts/manage_translations.py
index b90b26e..3e58040 100644
--- a/lib/django-1.5/scripts/manage_translations.py
+++ b/lib/django-1.5/scripts/manage_translations.py
@@ -47,9 +47,9 @@
def _tx_resource_for_name(name):
""" Return the Transifex resource name """
if name == 'core':
- return "django.core"
+ return "django-core.core"
else:
- return "django.contrib-%s" % name
+ return "django-core.contrib-%s" % name
def _check_diff(cat_name, base_path):
"""
diff --git a/lib/django-1.5/setup.cfg b/lib/django-1.5/setup.cfg
index e189ffb..4c25b84 100644
--- a/lib/django-1.5/setup.cfg
+++ b/lib/django-1.5/setup.cfg
@@ -2,3 +2,8 @@
doc_files = docs extras AUTHORS INSTALL LICENSE README.rst
install-script = scripts/rpm-install.sh
+[metadata]
+license-file = LICENSE
+
+[wheel]
+universal = 1
diff --git a/lib/django-1.5/setup.py b/lib/django-1.5/setup.py
index fcd6193..38db93d 100644
--- a/lib/django-1.5/setup.py
+++ b/lib/django-1.5/setup.py
@@ -1,10 +1,9 @@
-from distutils.core import setup
-from distutils.command.install_data import install_data
-from distutils.command.install import INSTALL_SCHEMES
-from distutils.sysconfig import get_python_lib
import os
import sys
+from distutils.core import setup
+from distutils.sysconfig import get_python_lib
+
# Warn if we are installing over top of an existing installation. This can
# cause issues where files that were deleted from a more recent Django are
# still present in site-packages. See #18115.
@@ -20,28 +19,11 @@
overlay_warning = True
break
-class osx_install_data(install_data):
- # On MacOS, the platform-specific lib dir is /System/Library/Framework/Python/.../
- # which is wrong. Python 2.5 supplied with MacOS 10.5 has an Apple-specific fix
- # for this in distutils.command.install_data#306. It fixes install_lib but not
- # install_data, which is why we roll our own install_data class.
-
- def finalize_options(self):
- # By the time finalize_options is called, install.install_lib is set to the
- # fixed directory, so we set the installdir to install_lib. The
- # install_data class uses ('install_data', 'install_dir') instead.
- self.set_undefined_options('install', ('install_lib', 'install_dir'))
- install_data.finalize_options(self)
-
-if sys.platform == "darwin":
- cmdclasses = {'install_data': osx_install_data}
-else:
- cmdclasses = {'install_data': install_data}
def fullsplit(path, result=None):
"""
- Split a pathname into components (the opposite of os.path.join) in a
- platform-neutral way.
+ Split a pathname into components (the opposite of os.path.join)
+ in a platform-neutral way.
"""
if result is None:
result = []
@@ -52,15 +34,23 @@
return result
return fullsplit(head, [tail] + result)
-# Tell distutils not to put the data_files in platform-specific installation
-# locations. See here for an explanation:
-# http://groups.google.com/group/comp.lang.python/browse_thread/thread/35ec7b2fed36eaec/2105ee4d9e8042cb
-for scheme in INSTALL_SCHEMES.values():
- scheme['data'] = scheme['purelib']
+
+EXCLUDE_FROM_PACKAGES = ['django.conf.project_template',
+ 'django.conf.app_template',
+ 'django.bin']
+
+
+def is_package(package_name):
+ for pkg in EXCLUDE_FROM_PACKAGES:
+ if package_name.startswith(pkg):
+ return False
+ return True
+
# Compile the list of packages available, because distutils doesn't have
# an easy way to do this.
-packages, data_files = [], []
+packages, package_data = [], {}
+
root_dir = os.path.dirname(__file__)
if root_dir != '':
os.chdir(root_dir)
@@ -69,34 +59,38 @@
for dirpath, dirnames, filenames in os.walk(django_dir):
# Ignore PEP 3147 cache dirs and those whose names start with '.'
dirnames[:] = [d for d in dirnames if not d.startswith('.') and d != '__pycache__']
- if '__init__.py' in filenames:
- packages.append('.'.join(fullsplit(dirpath)))
+ parts = fullsplit(dirpath)
+ package_name = '.'.join(parts)
+ if '__init__.py' in filenames and is_package(package_name):
+ packages.append(package_name)
elif filenames:
- data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
+ relative_path = []
+ while '.'.join(parts) not in packages:
+ relative_path.append(parts.pop())
+ relative_path.reverse()
+ path = os.path.join(*relative_path)
+ package_files = package_data.setdefault('.'.join(parts), [])
+ package_files.extend([os.path.join(path, f) for f in filenames])
-# Small hack for working with bdist_wininst.
-# See http://mail.python.org/pipermail/distutils-sig/2004-August/004134.html
-if len(sys.argv) > 1 and sys.argv[1] == 'bdist_wininst':
- for file_info in data_files:
- file_info[0] = '\\PURELIB\\%s' % file_info[0]
# Dynamically calculate the version based on django.VERSION.
version = __import__('django').get_version()
+
setup(
- name = "Django",
- version = version,
- url = 'http://www.djangoproject.com/',
- author = 'Django Software Foundation',
- author_email = 'foundation@djangoproject.com',
- description = 'A high-level Python Web framework that encourages rapid development and clean, pragmatic design.',
- download_url = 'https://www.djangoproject.com/m/releases/1.5/Django-1.5.tar.gz',
- license = "BSD",
- packages = packages,
- cmdclass = cmdclasses,
- data_files = data_files,
- scripts = ['django/bin/django-admin.py'],
- classifiers = [
+ name='Django',
+ version=version,
+ url='http://www.djangoproject.com/',
+ author='Django Software Foundation',
+ author_email='foundation@djangoproject.com',
+ description=('A high-level Python Web framework that encourages '
+ 'rapid development and clean, pragmatic design.'),
+ download_url='https://www.djangoproject.com/m/releases/1.5/Django-1.5.4.tar.gz',
+ license='BSD',
+ packages=packages,
+ package_data=package_data,
+ scripts=['django/bin/django-admin.py'],
+ classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Django',
@@ -104,8 +98,10 @@
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Topic :: Internet :: WWW/HTTP',
@@ -113,7 +109,7 @@
'Topic :: Internet :: WWW/HTTP :: WSGI',
'Topic :: Software Development :: Libraries :: Application Frameworks',
'Topic :: Software Development :: Libraries :: Python Modules',
- ],
+ ],
)
if overlay_warning:
@@ -134,4 +130,4 @@
directory and re-install Django.
-""" % { "existing_path": existing_path })
+""" % {"existing_path": existing_path})
diff --git a/lib/django-1.5/tests/.coveragerc b/lib/django-1.5/tests/.coveragerc
new file mode 100644
index 0000000..b979e94
--- /dev/null
+++ b/lib/django-1.5/tests/.coveragerc
@@ -0,0 +1,5 @@
+[run]
+omit = runtests,test_sqlite,regressiontests*,modeltests*,*/django/contrib/*/tests*,*/django/utils/unittest*,*/django/utils/simplejson*,*/django/utils/importlib.py,*/django/test/_doctest.py,*/django/core/servers/fastcgi.py,*/django/utils/autoreload.py,*/django/utils/dictconfig.py
+
+[html]
+directory = coverage_html
diff --git a/lib/django-1.5/tests/modeltests/basic/models.py b/lib/django-1.5/tests/modeltests/basic/models.py
index 660bedd..1bffcb9 100644
--- a/lib/django-1.5/tests/modeltests/basic/models.py
+++ b/lib/django-1.5/tests/modeltests/basic/models.py
@@ -18,3 +18,11 @@
def __str__(self):
return self.headline
+
+@python_2_unicode_compatible
+class SelfRef(models.Model):
+ selfref = models.ForeignKey('self', null=True, blank=True,
+ related_name='+')
+
+ def __str__(self):
+ return SelfRef.objects.get(selfref=self).pk
diff --git a/lib/django-1.5/tests/modeltests/basic/tests.py b/lib/django-1.5/tests/modeltests/basic/tests.py
index 1dd1176..20dceed 100644
--- a/lib/django-1.5/tests/modeltests/basic/tests.py
+++ b/lib/django-1.5/tests/modeltests/basic/tests.py
@@ -2,13 +2,13 @@
from datetime import datetime
-from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
+from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db.models.fields import Field, FieldDoesNotExist
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import six
from django.utils.translation import ugettext_lazy
-from .models import Article
+from .models import Article, SelfRef
class ModelTest(TestCase):
@@ -84,23 +84,14 @@
# parameters don't match any object.
six.assertRaisesRegex(self,
ObjectDoesNotExist,
- "Article matching query does not exist. Lookup parameters were "
- "{'id__exact': 2000}",
+ "Article matching query does not exist.",
Article.objects.get,
id__exact=2000,
)
# To avoid dict-ordering related errors check only one lookup
# in single assert.
- six.assertRaisesRegex(self,
+ self.assertRaises(
ObjectDoesNotExist,
- ".*'pub_date__year': 2005.*",
- Article.objects.get,
- pub_date__year=2005,
- pub_date__month=8,
- )
- six.assertRaisesRegex(self,
- ObjectDoesNotExist,
- ".*'pub_date__month': 8.*",
Article.objects.get,
pub_date__year=2005,
pub_date__month=8,
@@ -108,8 +99,7 @@
six.assertRaisesRegex(self,
ObjectDoesNotExist,
- "Article matching query does not exist. Lookup parameters were "
- "{'pub_date__week_day': 6}",
+ "Article matching query does not exist.",
Article.objects.get,
pub_date__week_day=6,
)
@@ -640,7 +630,7 @@
article = Article.objects.get()
self.assertEqual(article.headline, notlazy)
- def test_invalid_qs_list(self):
- qs = Article.objects.order_by('invalid_column')
- self.assertRaises(FieldError, list, qs)
- self.assertRaises(FieldError, list, qs)
\ No newline at end of file
+ def test_ticket_20278(self):
+ sr = SelfRef.objects.create()
+ with self.assertRaises(ObjectDoesNotExist):
+ SelfRef.objects.get(selfref=sr)
diff --git a/lib/django-1.5/tests/modeltests/many_to_many/models.py b/lib/django-1.5/tests/modeltests/many_to_many/models.py
index a196c85..31793b3 100644
--- a/lib/django-1.5/tests/modeltests/many_to_many/models.py
+++ b/lib/django-1.5/tests/modeltests/many_to_many/models.py
@@ -6,6 +6,7 @@
In this example, an ``Article`` can be published in multiple ``Publication``
objects, and a ``Publication`` has multiple ``Article`` objects.
"""
+from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@@ -24,7 +25,9 @@
@python_2_unicode_compatible
class Article(models.Model):
headline = models.CharField(max_length=100)
- publications = models.ManyToManyField(Publication)
+ # Assign a unicode string as name to make sure the intermediary model is
+ # correctly created. Refs #20207
+ publications = models.ManyToManyField(Publication, name='publications')
def __str__(self):
return self.headline
diff --git a/lib/django-1.5/tests/modeltests/prefetch_related/models.py b/lib/django-1.5/tests/modeltests/prefetch_related/models.py
index e58997d..779d649 100644
--- a/lib/django-1.5/tests/modeltests/prefetch_related/models.py
+++ b/lib/django-1.5/tests/modeltests/prefetch_related/models.py
@@ -195,3 +195,23 @@
class Meta:
ordering = ['id']
+
+
+### Ticket 19607
+
+@python_2_unicode_compatible
+class LessonEntry(models.Model):
+ name1 = models.CharField(max_length=200)
+ name2 = models.CharField(max_length=200)
+
+ def __str__(self):
+ return "%s %s" % (self.name1, self.name2)
+
+
+@python_2_unicode_compatible
+class WordEntry(models.Model):
+ lesson_entry = models.ForeignKey(LessonEntry)
+ name = models.CharField(max_length=200)
+
+ def __str__(self):
+ return "%s (%s)" % (self.name, self.id)
diff --git a/lib/django-1.5/tests/modeltests/prefetch_related/tests.py b/lib/django-1.5/tests/modeltests/prefetch_related/tests.py
index e81560f..3921153 100644
--- a/lib/django-1.5/tests/modeltests/prefetch_related/tests.py
+++ b/lib/django-1.5/tests/modeltests/prefetch_related/tests.py
@@ -8,7 +8,8 @@
from .models import (Author, Book, Reader, Qualification, Teacher, Department,
TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
- BookWithYear, BookReview, Person, House, Room, Employee, Comment)
+ BookWithYear, BookReview, Person, House, Room, Employee, Comment,
+ LessonEntry, WordEntry)
class PrefetchRelatedTests(TestCase):
@@ -618,3 +619,25 @@
ages = ", ".join(str(a.authorwithage.age) for a in A.prefetch_related('authorwithage'))
self.assertEqual(ages, "50, 49")
+
+
+class Ticket19607Tests(TestCase):
+
+ def setUp(self):
+
+ for id, name1, name2 in [
+ (1, 'einfach', 'simple'),
+ (2, 'schwierig', 'difficult'),
+ ]:
+ LessonEntry.objects.create(id=id, name1=name1, name2=name2)
+
+ for id, lesson_entry_id, name in [
+ (1, 1, 'einfach'),
+ (2, 1, 'simple'),
+ (3, 2, 'schwierig'),
+ (4, 2, 'difficult'),
+ ]:
+ WordEntry.objects.create(id=id, lesson_entry_id=lesson_entry_id, name=name)
+
+ def test_bug(self):
+ list(WordEntry.objects.prefetch_related('lesson_entry', 'lesson_entry__wordentry_set'))
diff --git a/lib/django-1.5/tests/regressiontests/admin_scripts/tests.py b/lib/django-1.5/tests/regressiontests/admin_scripts/tests.py
index 6ac88ad..91bcbee 100644
--- a/lib/django-1.5/tests/regressiontests/admin_scripts/tests.py
+++ b/lib/django-1.5/tests/regressiontests/admin_scripts/tests.py
@@ -14,7 +14,8 @@
import sys
import codecs
-from django import conf, bin, get_version
+import django
+from django import conf, get_version
from django.conf import settings
from django.core.management import BaseCommand
from django.db import connection
@@ -139,8 +140,8 @@
return out, err
def run_django_admin(self, args, settings_file=None):
- bin_dir = os.path.abspath(os.path.dirname(upath(bin.__file__)))
- return self.run_test(os.path.join(bin_dir, 'django-admin.py'), args, settings_file)
+ script_dir = os.path.abspath(os.path.join(os.path.dirname(upath(django.__file__)), 'bin'))
+ return self.run_test(os.path.join(script_dir, 'django-admin.py'), args, settings_file)
def run_manage(self, args, settings_file=None):
def safe_remove(path):
diff --git a/lib/django-1.5/tests/regressiontests/admin_widgets/tests.py b/lib/django-1.5/tests/regressiontests/admin_widgets/tests.py
index fa599c6..b3fff1f 100644
--- a/lib/django-1.5/tests/regressiontests/admin_widgets/tests.py
+++ b/lib/django-1.5/tests/regressiontests/admin_widgets/tests.py
@@ -299,18 +299,24 @@
w = widgets.AdminURLFieldWidget()
self.assertHTMLEqual(
conditional_escape(w.render('test', 'http://example-äüö.com')),
- '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
+ '<p class="url">Currently: <a href="http://xn--example--7za4pnc.com">http://example-äüö.com</a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com" /></p>'
)
def test_render_quoting(self):
+ # WARNING: Don't use assertHTMLEqual in that testcase!
+ # assertHTMLEqual will get rid of some escapes which are tested here!
w = widgets.AdminURLFieldWidget()
- self.assertHTMLEqual(
- conditional_escape(w.render('test', 'http://example.com/<sometag>some text</sometag>')),
- '<p class="url">Currently:<a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
+ self.assertEqual(
+ w.render('test', 'http://example.com/<sometag>some text</sometag>'),
+ '<p class="url">Currently: <a href="http://example.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example.com/<sometag>some text</sometag></a><br />Change: <input class="vURLField" name="test" type="text" value="http://example.com/<sometag>some text</sometag>" /></p>'
)
- self.assertHTMLEqual(
- conditional_escape(w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>')),
- '<p class="url">Currently:<a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/<sometag>some text</sometag></a><br />Change:<input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
+ self.assertEqual(
+ w.render('test', 'http://example-äüö.com/<sometag>some text</sometag>'),
+ '<p class="url">Currently: <a href="http://xn--example--7za4pnc.com/%3Csometag%3Esome%20text%3C/sometag%3E">http://example-äüö.com/<sometag>some text</sometag></a><br />Change: <input class="vURLField" name="test" type="text" value="http://example-äüö.com/<sometag>some text</sometag>" /></p>'
+ )
+ self.assertEqual(
+ w.render('test', 'http://www.example.com/%C3%A4"><script>alert("XSS!")</script>"'),
+ '<p class="url">Currently: <a href="http://www.example.com/%C3%A4%22%3E%3Cscript%3Ealert(%22XSS!%22)%3C/script%3E%22">http://www.example.com/%C3%A4"><script>alert("XSS!")</script>"</a><br />Change: <input class="vURLField" name="test" type="text" value="http://www.example.com/%C3%A4"><script>alert("XSS!")</script>"" /></p>'
)
diff --git a/lib/django-1.5/tests/regressiontests/aggregation_regress/tests.py b/lib/django-1.5/tests/regressiontests/aggregation_regress/tests.py
index 9b3cd41..71c90ec 100644
--- a/lib/django-1.5/tests/regressiontests/aggregation_regress/tests.py
+++ b/lib/django-1.5/tests/regressiontests/aggregation_regress/tests.py
@@ -588,10 +588,15 @@
)
publishers = publishers.annotate(n_books=Count("book"))
+ sorted_publishers = sorted(publishers, key=lambda x: x.name)
self.assertEqual(
- publishers[0].n_books,
+ sorted_publishers[0].n_books,
2
)
+ self.assertEqual(
+ sorted_publishers[1].n_books,
+ 1
+ )
self.assertEqual(
sorted(p.name for p in publishers),
diff --git a/lib/django-1.5/tests/regressiontests/defaultfilters/tests.py b/lib/django-1.5/tests/regressiontests/defaultfilters/tests.py
index 52268da..ae48b67 100644
--- a/lib/django-1.5/tests/regressiontests/defaultfilters/tests.py
+++ b/lib/django-1.5/tests/regressiontests/defaultfilters/tests.py
@@ -246,9 +246,10 @@
'<a href="https://google.com" rel="nofollow">https://google.com</a>')
# Check urlize doesn't overquote already quoted urls - see #9655
- self.assertEqual(urlize('http://hi.baidu.com/%D6%D8%D0%C2%BF'),
- '<a href="http://hi.baidu.com/%D6%D8%D0%C2%BF" rel="nofollow">'
- 'http://hi.baidu.com/%D6%D8%D0%C2%BF</a>')
+ # The teststring is the urlquoted version of 'http://hi.baidu.com/重新开始'
+ self.assertEqual(urlize('http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B'),
+ '<a href="http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B" rel="nofollow">'
+ 'http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B</a>')
self.assertEqual(urlize('www.mystore.com/30%OffCoupons!'),
'<a href="http://www.mystore.com/30%25OffCoupons!" rel="nofollow">'
'www.mystore.com/30%OffCoupons!</a>')
diff --git a/lib/django-1.5/tests/regressiontests/defer_regress/tests.py b/lib/django-1.5/tests/regressiontests/defer_regress/tests.py
index d4d7220..0bd0eed 100644
--- a/lib/django-1.5/tests/regressiontests/defer_regress/tests.py
+++ b/lib/django-1.5/tests/regressiontests/defer_regress/tests.py
@@ -7,6 +7,7 @@
from django.db.models import Count
from django.db.models.loading import cache
from django.test import TestCase
+from django.test.utils import override_settings
from .models import (ResolveThis, Item, RelatedItem, Child, Leaf, Proxy,
SimpleItem, Feature, ItemAndSimpleItem, OneToOneItem, SpecialFeature)
@@ -80,24 +81,6 @@
self.assertEqual(results[0].child.name, "c1")
self.assertEqual(results[0].second_child.name, "c2")
- # Test for #12163 - Pickling error saving session with unsaved model
- # instances.
- SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead'
-
- item = Item()
- item._deferred = False
- s = SessionStore(SESSION_KEY)
- s.clear()
- s["item"] = item
- s.save()
-
- s = SessionStore(SESSION_KEY)
- s.modified = True
- s.save()
-
- i2 = s["item"]
- self.assertFalse(i2._deferred)
-
# Regression for #11936 - loading.get_models should not return deferred
# models by default.
klasses = sorted(
@@ -159,6 +142,27 @@
]
)
+ @override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer')
+ def test_ticket_12163(self):
+ # Test for #12163 - Pickling error saving session with unsaved model
+ # instances.
+ SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead'
+
+ item = Item()
+ item._deferred = False
+ s = SessionStore(SESSION_KEY)
+ s.clear()
+ s["item"] = item
+ s.save()
+
+ s = SessionStore(SESSION_KEY)
+ s.modified = True
+ s.save()
+
+ i2 = s["item"]
+ self.assertFalse(i2._deferred)
+
+ def test_ticket_16409(self):
# Regression for #16409 - make sure defer() and only() work with annotate()
self.assertIsInstance(list(SimpleItem.objects.annotate(Count('feature')).defer('name')), list)
self.assertIsInstance(list(SimpleItem.objects.annotate(Count('feature')).only('name')), list)
diff --git a/lib/django-1.5/tests/regressiontests/forms/tests/forms.py b/lib/django-1.5/tests/regressiontests/forms/tests/forms.py
index ade0684..0049ce8 100644
--- a/lib/django-1.5/tests/regressiontests/forms/tests/forms.py
+++ b/lib/django-1.5/tests/regressiontests/forms/tests/forms.py
@@ -1612,23 +1612,6 @@
</form>""")
self.assertEqual(Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)})), '')
- # The label_tag() method takes an optional attrs argument: a dictionary of HTML
- # attributes to add to the <label> tag.
- f = UserRegistration(auto_id='id_%s')
- form_output = []
-
- for bf in f:
- form_output.append(bf.label_tag(attrs={'class': 'pretty'}))
-
- expected_form_output = [
- '<label for="id_username" class="pretty">Username</label>',
- '<label for="id_password1" class="pretty">Password1</label>',
- '<label for="id_password2" class="pretty">Password2</label>',
- ]
- self.assertEqual(len(form_output), len(expected_form_output))
- for i in range(len(form_output)):
- self.assertHTMLEqual(form_output[i], expected_form_output[i])
-
# To display the errors that aren't associated with a particular field -- e.g.,
# the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
# template. If used on its own, it is displayed as a <ul> (or an empty string, if
@@ -1797,3 +1780,38 @@
form = NameForm(data={'name' : ['fname', 'lname']})
self.assertTrue(form.is_valid())
self.assertEqual(form.cleaned_data, {'name' : 'fname lname'})
+
+ def test_boundfield_label_tag(self):
+ class SomeForm(Form):
+ field = CharField()
+ boundfield = SomeForm()['field']
+
+ testcases = [ # (args, kwargs, expected)
+ # without anything: just print the <label>
+ ((), {}, '<label for="id_field">Field</label>'),
+
+ # passing just one argument: overrides the field's label
+ (('custom',), {}, '<label for="id_field">custom</label>'),
+
+ # the overriden label is escaped
+ (('custom&',), {}, '<label for="id_field">custom&</label>'),
+ ((mark_safe('custom&'),), {}, '<label for="id_field">custom&</label>'),
+
+ # Passing attrs to add extra attributes on the <label>
+ ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field</label>')
+ ]
+
+ for args, kwargs, expected in testcases:
+ self.assertHTMLEqual(boundfield.label_tag(*args, **kwargs), expected)
+
+ def test_boundfield_label_tag_no_id(self):
+ """
+ If a widget has no id, label_tag just returns the text with no
+ surrounding <label>.
+ """
+ class SomeForm(Form):
+ field = CharField()
+ boundfield = SomeForm(auto_id='')['field']
+
+ self.assertHTMLEqual(boundfield.label_tag(), 'Field')
+ self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&')
diff --git a/lib/django-1.5/tests/regressiontests/i18n/commands/extraction.py b/lib/django-1.5/tests/regressiontests/i18n/commands/extraction.py
index aa5efe1..47d58c4 100644
--- a/lib/django-1.5/tests/regressiontests/i18n/commands/extraction.py
+++ b/lib/django-1.5/tests/regressiontests/i18n/commands/extraction.py
@@ -27,6 +27,10 @@
return
shutil.rmtree(dname)
+ def rmfile(self, filepath):
+ if os.path.exists(filepath):
+ os.remove(filepath)
+
def tearDown(self):
os.chdir(self.test_dir)
try:
@@ -120,12 +124,21 @@
# Check that the temporary file was cleaned up
self.assertFalse(os.path.exists('./templates/template_with_error.html.py'))
+ def test_unicode_decode_error(self):
+ os.chdir(self.test_dir)
+ shutil.copyfile('./not_utf8.sample', './not_utf8.txt')
+ self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'not_utf8.txt'))
+ stdout = StringIO()
+ management.call_command('makemessages', locale=LOCALE, stdout=stdout)
+ self.assertIn("UnicodeDecodeError: skipped file not_utf8.txt in .",
+ force_text(stdout.getvalue()))
+
def test_extraction_warning(self):
os.chdir(self.test_dir)
shutil.copyfile('./code.sample', './code_sample.py')
+ self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'code_sample.py'))
stdout = StringIO()
management.call_command('makemessages', locale=LOCALE, stdout=stdout)
- os.remove('./code_sample.py')
self.assertIn("code_sample.py:4", force_text(stdout.getvalue()))
def test_template_message_context_extractor(self):
diff --git a/lib/django-1.5/tests/regressiontests/i18n/commands/not_utf8.sample b/lib/django-1.5/tests/regressiontests/i18n/commands/not_utf8.sample
new file mode 100644
index 0000000..6449f52
--- /dev/null
+++ b/lib/django-1.5/tests/regressiontests/i18n/commands/not_utf8.sample
@@ -0,0 +1 @@
+Copyright (c) 2009 Øyvind Sean Kinsey, oyvind@kinsey.no
\ No newline at end of file
diff --git a/lib/django-1.5/tests/regressiontests/logging_tests/tests.py b/lib/django-1.5/tests/regressiontests/logging_tests/tests.py
index a498510..080bd03 100644
--- a/lib/django-1.5/tests/regressiontests/logging_tests/tests.py
+++ b/lib/django-1.5/tests/regressiontests/logging_tests/tests.py
@@ -142,6 +142,14 @@
and captured to the logging system
"""
def setUp(self):
+ # If tests are invoke with "-Wall" (or any -W flag actually) then
+ # warning logging gets disabled (see django/conf/__init__.py). However,
+ # these tests expect warnings to be logged, so manually force warnings
+ # to the logs. Use getattr() here because the logging capture state is
+ # undocumented and (I assume) brittle.
+ self._old_capture_state = bool(getattr(logging, '_warnings_showwarning', False))
+ logging.captureWarnings(True)
+
# this convoluted setup is to avoid printing this deprecation to
# stderr during test running - as the test runner forces deprecations
# to be displayed at the global py.warnings level
@@ -157,6 +165,9 @@
for i, handler in enumerate(self.logger.handlers):
self.logger.handlers[i].stream = self.old_streams[i]
+ # Reset warnings state.
+ logging.captureWarnings(self._old_capture_state)
+
@override_settings(DEBUG=True)
def test_warnings_capture(self):
warnings.warn('Foo Deprecated', DeprecationWarning)
diff --git a/lib/django-1.5/tests/regressiontests/queries/tests.py b/lib/django-1.5/tests/regressiontests/queries/tests.py
index a2f91e9..33e5337 100644
--- a/lib/django-1.5/tests/regressiontests/queries/tests.py
+++ b/lib/django-1.5/tests/regressiontests/queries/tests.py
@@ -1533,7 +1533,6 @@
# Nested queries are possible (although should be used with care, since
# they have performance problems on backends like MySQL.
-
self.assertQuerysetEqual(
Annotation.objects.filter(notes__in=Note.objects.filter(note="n1")),
['<Annotation: a1>']
@@ -2142,3 +2141,11 @@
# so we can use INNER JOIN for it. However, we can NOT use INNER JOIN
# for the b->c join, as a->b is nullable.
self.assertEqual(str(qset.query).count('INNER JOIN'), 1)
+
+class EmptyStringPromotionTests(TestCase):
+ def test_empty_string_promotion(self):
+ qs = RelatedObject.objects.filter(single__name='')
+ if connection.features.interprets_empty_strings_as_nulls:
+ self.assertIn('LEFT OUTER JOIN', str(qs.query))
+ else:
+ self.assertNotIn('LEFT OUTER JOIN', str(qs.query))
diff --git a/lib/django-1.5/tests/regressiontests/queryset_pickle/models.py b/lib/django-1.5/tests/regressiontests/queryset_pickle/models.py
index d3500c9..871fdd8 100644
--- a/lib/django-1.5/tests/regressiontests/queryset_pickle/models.py
+++ b/lib/django-1.5/tests/regressiontests/queryset_pickle/models.py
@@ -36,3 +36,16 @@
number2 = models.IntegerField(blank=True, default=Numbers.get_static_number)
number3 = models.IntegerField(blank=True, default=Numbers.get_class_number)
number4 = models.IntegerField(blank=True, default=nn.get_member_number)
+
+class Person(models.Model):
+ name = models.CharField(max_length=200)
+
+class SocialProfile(models.Model):
+ person = models.ForeignKey(Person)
+ friends = models.ManyToManyField('self')
+
+class Post(models.Model):
+ post_date = models.DateTimeField(default=datetime.datetime.now)
+
+class Material(models.Model):
+ post = models.ForeignKey(Post, related_name='materials')
diff --git a/lib/django-1.5/tests/regressiontests/queryset_pickle/tests.py b/lib/django-1.5/tests/regressiontests/queryset_pickle/tests.py
index ab32e8f..b1be30d 100644
--- a/lib/django-1.5/tests/regressiontests/queryset_pickle/tests.py
+++ b/lib/django-1.5/tests/regressiontests/queryset_pickle/tests.py
@@ -5,7 +5,8 @@
from django.test import TestCase
-from .models import Group, Event, Happening
+from .models import Group, Event, Happening, Person, Post
+from django.contrib.auth.models import AnonymousUser
class PickleabilityTestCase(TestCase):
@@ -46,3 +47,41 @@
# can't just use assertEqual(original, unpickled)
self.assertEqual(original.__class__, unpickled.__class__)
self.assertEqual(original.args, unpickled.args)
+
+ def test_pickle_m2m_prefetch_related(self):
+ bob = Person(name="Bob")
+ bob.save()
+ people = Person.objects.prefetch_related('socialprofile_set')
+ dumped = pickle.dumps(people)
+ people = pickle.loads(dumped)
+ self.assertQuerysetEqual(
+ people, [bob], lambda x: x)
+
+ def test_pickle_field_default_prefetch_related(self):
+ p1 = Post.objects.create()
+ posts = Post.objects.prefetch_related('materials')
+ dumped = pickle.dumps(posts)
+ posts = pickle.loads(dumped)
+ self.assertQuerysetEqual(
+ posts, [p1], lambda x: x)
+
+ def test_pickle_emptyqs(self):
+ u = AnonymousUser()
+ # Use AnonymousUser, as AnonymousUser.groups has qs.model = None
+ empty = u.groups.all()
+ dumped = pickle.dumps(empty)
+ empty = pickle.loads(dumped)
+ self.assertQuerysetEqual(
+ empty, [])
+
+ def test_pickle_prefetch_related_idempotence(self):
+ p = Post.objects.create()
+ posts = Post.objects.prefetch_related('materials')
+
+ # First pickling
+ posts = pickle.loads(pickle.dumps(posts))
+ self.assertQuerysetEqual(posts, [p], lambda x: x)
+
+ # Second pickling
+ posts = pickle.loads(pickle.dumps(posts))
+ self.assertQuerysetEqual(posts, [p], lambda x: x)
diff --git a/lib/django-1.5/tests/regressiontests/requests/tests.py b/lib/django-1.5/tests/regressiontests/requests/tests.py
index 9e17883..e4d0db0 100644
--- a/lib/django-1.5/tests/regressiontests/requests/tests.py
+++ b/lib/django-1.5/tests/regressiontests/requests/tests.py
@@ -50,6 +50,44 @@
self.assertEqual(request.META['REQUEST_METHOD'], 'bogus')
self.assertEqual(request.META['SCRIPT_NAME'], '')
+ def test_wsgirequest_with_script_name(self):
+ """
+ Ensure that the request's path is correctly assembled, regardless of
+ whether or not the SCRIPT_NAME has a trailing slash.
+ Refs #20169.
+ """
+ # With trailing slash
+ request = WSGIRequest({'PATH_INFO': '/somepath/', 'SCRIPT_NAME': '/PREFIX/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
+ self.assertEqual(request.path, '/PREFIX/somepath/')
+ # Without trailing slash
+ request = WSGIRequest({'PATH_INFO': '/somepath/', 'SCRIPT_NAME': '/PREFIX', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
+ self.assertEqual(request.path, '/PREFIX/somepath/')
+
+ def test_wsgirequest_with_force_script_name(self):
+ """
+ Ensure that the FORCE_SCRIPT_NAME setting takes precedence over the
+ request's SCRIPT_NAME environment parameter.
+ Refs #20169.
+ """
+ with override_settings(FORCE_SCRIPT_NAME='/FORCED_PREFIX/'):
+ request = WSGIRequest({'PATH_INFO': '/somepath/', 'SCRIPT_NAME': '/PREFIX/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
+ self.assertEqual(request.path, '/FORCED_PREFIX/somepath/')
+
+ def test_wsgirequest_path_with_force_script_name_trailing_slash(self):
+ """
+ Ensure that the request's path is correctly assembled, regardless of
+ whether or not the FORCE_SCRIPT_NAME setting has a trailing slash.
+ Refs #20169.
+ """
+ # With trailing slash
+ with override_settings(FORCE_SCRIPT_NAME='/FORCED_PREFIX/'):
+ request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
+ self.assertEqual(request.path, '/FORCED_PREFIX/somepath/')
+ # Without trailing slash
+ with override_settings(FORCE_SCRIPT_NAME='/FORCED_PREFIX'):
+ request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
+ self.assertEqual(request.path, '/FORCED_PREFIX/somepath/')
+
def test_wsgirequest_repr(self):
request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
request.GET = {'get-key': 'get-value'}
diff --git a/lib/django-1.5/tests/regressiontests/templates/tests.py b/lib/django-1.5/tests/regressiontests/templates/tests.py
index 2556714..fcf6d97 100644
--- a/lib/django-1.5/tests/regressiontests/templates/tests.py
+++ b/lib/django-1.5/tests/regressiontests/templates/tests.py
@@ -1737,3 +1737,34 @@
template.Template('{% include "child" only %}').render(ctx),
'none'
)
+
+
+class SSITests(TestCase):
+ def setUp(self):
+ self.this_dir = os.path.dirname(os.path.abspath(upath(__file__)))
+ self.ssi_dir = os.path.join(self.this_dir, "templates", "first")
+
+ def render_ssi(self, path):
+ # the path must exist for the test to be reliable
+ self.assertTrue(os.path.exists(path))
+ return template.Template('{%% ssi "%s" %%}' % path).render(Context())
+
+ def test_allowed_paths(self):
+ acceptable_path = os.path.join(self.ssi_dir, "..", "first", "test.html")
+ with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)):
+ self.assertEqual(self.render_ssi(acceptable_path), 'First template\n')
+
+ def test_relative_include_exploit(self):
+ """
+ May not bypass ALLOWED_INCLUDE_ROOTS with relative paths
+
+ e.g. if ALLOWED_INCLUDE_ROOTS = ("/var/www",), it should not be
+ possible to do {% ssi "/var/www/../../etc/passwd" %}
+ """
+ disallowed_paths = [
+ os.path.join(self.ssi_dir, "..", "ssi_include.html"),
+ os.path.join(self.ssi_dir, "..", "second", "test.html"),
+ ]
+ with override_settings(ALLOWED_INCLUDE_ROOTS=(self.ssi_dir,)):
+ for path in disallowed_paths:
+ self.assertEqual(self.render_ssi(path), '')
diff --git a/lib/django-1.5/tests/regressiontests/test_client_regress/tests.py b/lib/django-1.5/tests/regressiontests/test_client_regress/tests.py
index 5ba5d3c..663173c 100644
--- a/lib/django-1.5/tests/regressiontests/test_client_regress/tests.py
+++ b/lib/django-1.5/tests/regressiontests/test_client_regress/tests.py
@@ -131,6 +131,18 @@
self.assertNotContains(r, 'はたけ')
self.assertNotContains(r, b'\xe3\x81\xaf\xe3\x81\x9f\xe3\x81\x91'.decode('utf-8'))
+ def test_binary_contains(self):
+ r = self.client.get('/test_client_regress/check_binary/')
+ self.assertContains(r, b'%PDF-1.4\r\n%\x93\x8c\x8b\x9e')
+ with self.assertRaises(AssertionError):
+ self.assertContains(r, b'%PDF-1.4\r\n%\x93\x8c\x8b\x9e', count=2)
+
+ def test_binary_not_contains(self):
+ r = self.client.get('/test_client_regress/check_binary/')
+ self.assertNotContains(r, b'%ODF-1.4\r\n%\x93\x8c\x8b\x9e')
+ with self.assertRaises(AssertionError):
+ self.assertNotContains(r, b'%PDF-1.4\r\n%\x93\x8c\x8b\x9e')
+
def test_nontext_contains(self):
r = self.client.get('/test_client_regress/no_template_view/')
self.assertContains(r, ugettext_lazy('once'))
diff --git a/lib/django-1.5/tests/regressiontests/test_client_regress/urls.py b/lib/django-1.5/tests/regressiontests/test_client_regress/urls.py
index d869c23..1332537 100644
--- a/lib/django-1.5/tests/regressiontests/test_client_regress/urls.py
+++ b/lib/django-1.5/tests/regressiontests/test_client_regress/urls.py
@@ -28,6 +28,7 @@
(r'^check_session/$', views.check_session_view),
(r'^request_methods/$', views.request_methods_view),
(r'^check_unicode/$', views.return_unicode),
+ (r'^check_binary/$', views.return_undecodable_binary),
(r'^parse_unicode_json/$', views.return_json_file),
(r'^check_headers/$', views.check_headers),
(r'^check_headers_redirect/$', RedirectView.as_view(url='/test_client_regress/check_headers/')),
diff --git a/lib/django-1.5/tests/regressiontests/test_client_regress/views.py b/lib/django-1.5/tests/regressiontests/test_client_regress/views.py
index 9b06548..7e86ffd 100644
--- a/lib/django-1.5/tests/regressiontests/test_client_regress/views.py
+++ b/lib/django-1.5/tests/regressiontests/test_client_regress/views.py
@@ -70,6 +70,11 @@
def return_unicode(request):
return render_to_response('unicode.html')
+def return_undecodable_binary(request):
+ return HttpResponse(
+ b'%PDF-1.4\r\n%\x93\x8c\x8b\x9e ReportLab Generated PDF document http://www.reportlab.com'
+ )
+
def return_json_file(request):
"A view that parses and returns a JSON string as a file."
match = CONTENT_TYPE_RE.match(request.META['CONTENT_TYPE'])
diff --git a/lib/django-1.5/tests/regressiontests/test_runner/deprecation_app/tests.py b/lib/django-1.5/tests/regressiontests/test_runner/deprecation_app/tests.py
index e676c3e..6dee088 100644
--- a/lib/django-1.5/tests/regressiontests/test_runner/deprecation_app/tests.py
+++ b/lib/django-1.5/tests/regressiontests/test_runner/deprecation_app/tests.py
@@ -2,6 +2,8 @@
from django.test import TestCase
+warnings.warn("module-level warning from deprecation_app", DeprecationWarning)
+
class DummyTest(TestCase):
def test_warn(self):
warnings.warn("warning from test", DeprecationWarning)
diff --git a/lib/django-1.5/tests/regressiontests/test_runner/tests.py b/lib/django-1.5/tests/regressiontests/test_runner/tests.py
index 5df421c..3418e2a 100644
--- a/lib/django-1.5/tests/regressiontests/test_runner/tests.py
+++ b/lib/django-1.5/tests/regressiontests/test_runner/tests.py
@@ -285,6 +285,65 @@
db.connections = old_db_connections
+class AliasedDefaultTestSetupTest(unittest.TestCase):
+ def test_setup_aliased_default_database(self):
+ """
+ Test that setup_datebases() doesn't fail when 'default' is aliased
+ """
+ runner = DjangoTestSuiteRunner(verbosity=0)
+ old_db_connections = db.connections
+ try:
+ db.connections = db.ConnectionHandler({
+ 'default': {
+ 'NAME': 'dummy'
+ },
+ 'aliased': {
+ 'NAME': 'dummy'
+ }
+ })
+ old_config = runner.setup_databases()
+ runner.teardown_databases(old_config)
+ except Exception as e:
+ self.fail("setup_databases/teardown_databases unexpectedly raised "
+ "an error: %s" % e)
+ finally:
+ db.connections = old_db_connections
+
+
+class AliasedDatabaseTest(unittest.TestCase):
+ def test_setup_aliased_databases(self):
+ from django.db.backends.dummy.base import DatabaseCreation
+
+ runner = DjangoTestSuiteRunner(verbosity=0)
+ old_db_connections = db.connections
+ old_destroy_test_db = DatabaseCreation.destroy_test_db
+ old_create_test_db = DatabaseCreation.create_test_db
+ try:
+ destroyed_names = []
+ DatabaseCreation.destroy_test_db = lambda self, old_database_name, verbosity=1: destroyed_names.append(old_database_name)
+ DatabaseCreation.create_test_db = lambda self, verbosity=1, autoclobber=False: self._get_test_db_name()
+
+ db.connections = db.ConnectionHandler({
+ 'default': {
+ 'ENGINE': 'django.db.backends.dummy',
+ 'NAME': 'dbname',
+ },
+ 'other': {
+ 'ENGINE': 'django.db.backends.dummy',
+ 'NAME': 'dbname',
+ }
+ })
+
+ old_config = runner.setup_databases()
+ runner.teardown_databases(old_config)
+
+ self.assertEqual(destroyed_names.count('dbname'), 1)
+ finally:
+ DatabaseCreation.create_test_db = old_create_test_db
+ DatabaseCreation.destroy_test_db = old_destroy_test_db
+ db.connections = old_db_connections
+
+
class DeprecationDisplayTest(AdminScriptTestCase):
# tests for 19546
def setUp(self):
@@ -298,7 +357,8 @@
def test_runner_deprecation_verbosity_default(self):
args = ['test', '--settings=regressiontests.settings']
out, err = self.run_django_admin(args)
- self.assertTrue("DeprecationWarning: warning from test" in err)
+ self.assertIn("DeprecationWarning: warning from test", err)
+ self.assertIn("DeprecationWarning: module-level warning from deprecation_app", err)
@unittest.skipIf(sys.version_info[:2] == (2, 6),
"On Python 2.6, DeprecationWarnings are visible anyway")
diff --git a/lib/django-1.5/tests/regressiontests/utils/encoding.py b/lib/django-1.5/tests/regressiontests/utils/encoding.py
index d191845..7aaba25 100644
--- a/lib/django-1.5/tests/regressiontests/utils/encoding.py
+++ b/lib/django-1.5/tests/regressiontests/utils/encoding.py
@@ -2,7 +2,7 @@
from __future__ import unicode_literals
from django.utils import unittest
-from django.utils.encoding import force_bytes
+from django.utils.encoding import force_bytes, filepath_to_uri
class TestEncodingUtils(unittest.TestCase):
@@ -15,3 +15,9 @@
exc = ValueError(error_msg)
result = force_bytes(exc)
self.assertEqual(result, error_msg.encode('utf-8'))
+
+ def test_filepath_to_uri(self):
+ self.assertEqual(filepath_to_uri('upload\\чубака.mp4'),
+ 'upload/%D1%87%D1%83%D0%B1%D0%B0%D0%BA%D0%B0.mp4')
+ self.assertEqual(filepath_to_uri('upload\\чубака.mp4'.encode('utf-8')),
+ 'upload/%D1%87%D1%83%D0%B1%D0%B0%D0%BA%D0%B0.mp4')
diff --git a/lib/django-1.5/tests/regressiontests/utils/html.py b/lib/django-1.5/tests/regressiontests/utils/html.py
index cb8c217..ba0f7dd 100644
--- a/lib/django-1.5/tests/regressiontests/utils/html.py
+++ b/lib/django-1.5/tests/regressiontests/utils/html.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import unittest
@@ -157,3 +158,13 @@
)
for value, tags, output in items:
self.assertEqual(f(value, tags), output)
+
+ def test_smart_urlquote(self):
+ quote = html.smart_urlquote
+ # Ensure that IDNs are properly quoted
+ self.assertEqual(quote('http://öäü.com/'), 'http://xn--4ca9at.com/')
+ self.assertEqual(quote('http://öäü.com/öäü/'), 'http://xn--4ca9at.com/%C3%B6%C3%A4%C3%BC/')
+ # Ensure that everything unsafe is quoted, !*'();:@&=+$,/?#[]~ is considered safe as per RFC
+ self.assertEqual(quote('http://example.com/path/öäü/'), 'http://example.com/path/%C3%B6%C3%A4%C3%BC/')
+ self.assertEqual(quote('http://example.com/%C3%B6/ä/'), 'http://example.com/%C3%B6/%C3%A4/')
+ self.assertEqual(quote('http://example.com/?x=1&y=2'), 'http://example.com/?x=1&y=2')
diff --git a/lib/django-1.5/tests/regressiontests/utils/itercompat.py b/lib/django-1.5/tests/regressiontests/utils/itercompat.py
new file mode 100644
index 0000000..02d3463
--- /dev/null
+++ b/lib/django-1.5/tests/regressiontests/utils/itercompat.py
@@ -0,0 +1,11 @@
+from django.test import TestCase
+
+from .models import Category, Thing
+
+
+class TestIsIterator(TestCase):
+ def test_regression(self):
+ """This failed on Django 1.5/Py2.6 because category has a next method."""
+ category = Category.objects.create(name='category')
+ Thing.objects.create(category=category)
+ Thing.objects.filter(category=category)
diff --git a/lib/django-1.5/tests/regressiontests/utils/models.py b/lib/django-1.5/tests/regressiontests/utils/models.py
index 97a72ba..de8f9e3 100644
--- a/lib/django-1.5/tests/regressiontests/utils/models.py
+++ b/lib/django-1.5/tests/regressiontests/utils/models.py
@@ -1 +1,13 @@
-# Test runner needs a models.py file.
+from django.db import models
+
+
+class Category(models.Model):
+ name = models.CharField(max_length=100)
+
+ def next(self):
+ return self
+
+
+class Thing(models.Model):
+ name = models.CharField(max_length=100)
+ category = models.ForeignKey(Category)
diff --git a/lib/django-1.5/tests/regressiontests/utils/module_loading.py b/lib/django-1.5/tests/regressiontests/utils/module_loading.py
index 3fc92b0..a9e3706 100644
--- a/lib/django-1.5/tests/regressiontests/utils/module_loading.py
+++ b/lib/django-1.5/tests/regressiontests/utils/module_loading.py
@@ -3,9 +3,10 @@
import imp
from zipimport import zipimporter
+from django.core.exceptions import ImproperlyConfigured
from django.utils import unittest
from django.utils.importlib import import_module
-from django.utils.module_loading import module_has_submodule
+from django.utils.module_loading import import_by_path, module_has_submodule
from django.utils._os import upath
@@ -103,6 +104,23 @@
self.assertFalse(module_has_submodule(egg_module, 'no_such_module'))
self.assertRaises(ImportError, import_module, 'egg_module.sub1.sub2.no_such_module')
+
+class ModuleImportTestCase(unittest.TestCase):
+ def test_import_by_path(self):
+ cls = import_by_path(
+ 'django.utils.module_loading.import_by_path')
+ self.assertEqual(cls, import_by_path)
+
+ # Test exceptions raised
+ for path in ('no_dots_in_path', 'unexistent.path',
+ 'tests.regressiontests.utils.unexistent'):
+ self.assertRaises(ImproperlyConfigured, import_by_path, path)
+
+ with self.assertRaises(ImproperlyConfigured) as cm:
+ import_by_path('unexistent.module.path', error_prefix="Foo")
+ self.assertTrue(str(cm.exception).startswith('Foo'))
+
+
class ProxyFinder(object):
def __init__(self):
self._cache = {}
diff --git a/lib/django-1.5/tests/regressiontests/utils/simplelazyobject.py b/lib/django-1.5/tests/regressiontests/utils/simplelazyobject.py
index 3f81e8f..2dd382c 100644
--- a/lib/django-1.5/tests/regressiontests/utils/simplelazyobject.py
+++ b/lib/django-1.5/tests/regressiontests/utils/simplelazyobject.py
@@ -121,3 +121,25 @@
self.assertEqual(unpickled, x)
self.assertEqual(six.text_type(unpickled), six.text_type(x))
self.assertEqual(unpickled.name, x.name)
+
+ def test_pickle_py2_regression(self):
+ from django.contrib.auth.models import User
+
+ # See ticket #20212
+ user = User.objects.create_user('johndoe', 'john@example.com', 'pass')
+ x = SimpleLazyObject(lambda: user)
+
+ # This would fail with "TypeError: can't pickle instancemethod objects",
+ # only on Python 2.X.
+ pickled = pickle.dumps(x)
+
+ # Try the variant protocol levels.
+ pickled = pickle.dumps(x, 0)
+ pickled = pickle.dumps(x, 1)
+ pickled = pickle.dumps(x, 2)
+
+ if not six.PY3:
+ import cPickle
+
+ # This would fail with "TypeError: expected string or Unicode object, NoneType found".
+ pickled = cPickle.dumps(x)
diff --git a/lib/django-1.5/tests/regressiontests/utils/tests.py b/lib/django-1.5/tests/regressiontests/utils/tests.py
index 11dd7c3..2847a7a 100644
--- a/lib/django-1.5/tests/regressiontests/utils/tests.py
+++ b/lib/django-1.5/tests/regressiontests/utils/tests.py
@@ -19,6 +19,7 @@
from .html import TestUtilsHtml
from .http import TestUtilsHttp, ETagProcessingTests, HttpDateProcessingTests
from .ipv6 import TestUtilsIPv6
+from .itercompat import TestIsIterator
from .jslex import JsToCForGettextTest, JsTokensTest
from .module_loading import CustomLoader, DefaultLoader, EggLoader
from .numberformat import TestNumberFormat
diff --git a/lib/django-1.5/tests/requirements/base.txt b/lib/django-1.5/tests/requirements/base.txt
new file mode 100644
index 0000000..99e2b7f
--- /dev/null
+++ b/lib/django-1.5/tests/requirements/base.txt
@@ -0,0 +1,5 @@
+docutils
+Markdown
+PyYAML
+pytz
+selenium
diff --git a/lib/django-1.5/tests/requirements/mysql.txt b/lib/django-1.5/tests/requirements/mysql.txt
new file mode 100644
index 0000000..c7a2347
--- /dev/null
+++ b/lib/django-1.5/tests/requirements/mysql.txt
@@ -0,0 +1 @@
+MySQL-python
diff --git a/lib/django-1.5/tests/requirements/oracle.txt b/lib/django-1.5/tests/requirements/oracle.txt
new file mode 100644
index 0000000..ae5b734
--- /dev/null
+++ b/lib/django-1.5/tests/requirements/oracle.txt
@@ -0,0 +1 @@
+cx_oracle
diff --git a/lib/django-1.5/tests/requirements/postgres.txt b/lib/django-1.5/tests/requirements/postgres.txt
new file mode 100644
index 0000000..658130b
--- /dev/null
+++ b/lib/django-1.5/tests/requirements/postgres.txt
@@ -0,0 +1 @@
+psycopg2
diff --git a/lib/django-1.5/tests/requirements/py2.txt b/lib/django-1.5/tests/requirements/py2.txt
new file mode 100644
index 0000000..4832e4e
--- /dev/null
+++ b/lib/django-1.5/tests/requirements/py2.txt
@@ -0,0 +1,5 @@
+-r base.txt
+PIL
+py-bcrypt
+python-memcached
+Textile
diff --git a/lib/django-1.5/tests/requirements/py3.txt b/lib/django-1.5/tests/requirements/py3.txt
new file mode 100644
index 0000000..ced3eed
--- /dev/null
+++ b/lib/django-1.5/tests/requirements/py3.txt
@@ -0,0 +1,2 @@
+-r base.txt
+python3-memcached
diff --git a/lib/django-1.5/tests/runtests.py b/lib/django-1.5/tests/runtests.py
index 8c56e27..00bb852 100644
--- a/lib/django-1.5/tests/runtests.py
+++ b/lib/django-1.5/tests/runtests.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+import logging
import os
import shutil
import subprocess
@@ -106,6 +107,14 @@
# in our tests.
settings.MANAGERS = ("admin@djangoproject.com",)
+ if verbosity > 0:
+ # Ensure any warnings captured to logging are piped through a verbose
+ # logging handler. If any -W options were passed explicitly on command
+ # line, warnings are not captured, and this has no effect.
+ logger = logging.getLogger('py.warnings')
+ handler = logging.StreamHandler()
+ logger.addHandler(handler)
+
# Load all the ALWAYS_INSTALLED_APPS.
# (This import statement is intentionally delayed until after we
# access settings because of the USE_I18N dependency.)
diff --git a/lib/endpoints-1.0/endpoints/api_config.py b/lib/endpoints-1.0/endpoints/api_config.py
index 64b20c9..feeac99 100644
--- a/lib/endpoints-1.0/endpoints/api_config.py
+++ b/lib/endpoints-1.0/endpoints/api_config.py
@@ -1119,10 +1119,15 @@
self.__auth_level = auth_level
def __safe_name(self, method_name):
- """Restrict method name to a-zA-Z0-9, first char lowercase."""
+ """Restrict method name to a-zA-Z0-9_, first char lowercase."""
- safe_name = re.sub('[^\.a-zA-Z0-9]', '', method_name)
+ safe_name = re.sub('[^\.a-zA-Z0-9_]', '', method_name)
+
+
+ safe_name = safe_name.lstrip('_')
+
+
return safe_name[0:1].lower() + safe_name[1:]
@@ -1320,7 +1325,7 @@
ResourceContainer.add_to_cache(invoke_remote.remote, request_message)
invoke_remote.method_info = _MethodInfo(
- name=name or api_method.__name__, path=path or '',
+ name=name or api_method.__name__, path=path or api_method.__name__,
http_method=http_method or DEFAULT_HTTP_METHOD,
cache_control=cache_control, scopes=scopes, audiences=audiences,
allowed_client_ids=allowed_client_ids, auth_level=auth_level)
@@ -1923,7 +1928,7 @@
auth_level = (method_info.auth_level
if method_info.auth_level is not None
else service.api_info.auth_level)
- if auth_level:
+ if auth_level is not None:
descriptor['authLevel'] = AUTH_LEVEL.reverse_mapping[auth_level]
return descriptor
diff --git a/lib/endpoints-1.0/endpoints/message_parser.py b/lib/endpoints-1.0/endpoints/message_parser.py
index 0d7a99f..fdf8d49 100644
--- a/lib/endpoints-1.0/endpoints/message_parser.py
+++ b/lib/endpoints-1.0/endpoints/message_parser.py
@@ -52,7 +52,7 @@
messages.Variant.UINT64: ('string', 'uint64'),
messages.Variant.SINT32: ('integer', 'int32'),
messages.Variant.SINT64: ('string', 'int64'),
- None: ('integer', 'int32')},
+ None: ('integer', 'int64')},
messages.FloatField: {messages.Variant.FLOAT: ('number', 'float'),
messages.Variant.DOUBLE: ('number', 'double'),
None: ('number', 'float')},
diff --git a/lib/protorpc/LICENSE b/lib/protorpc-1.0/LICENSE
similarity index 100%
rename from lib/protorpc/LICENSE
rename to lib/protorpc-1.0/LICENSE
diff --git a/lib/protorpc-1.0/protorpc/__init__.py b/lib/protorpc-1.0/protorpc/__init__.py
new file mode 100644
index 0000000..9005262
--- /dev/null
+++ b/lib/protorpc-1.0/protorpc/__init__.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 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.
+#
+
+"""Main module for ProtoRPC package."""
+
+__author__ = 'rafek@google.com (Rafe Kaplan)'
+__version__ = '1.0'
diff --git a/lib/protorpc/protorpc/definition.py b/lib/protorpc-1.0/protorpc/definition.py
similarity index 100%
rename from lib/protorpc/protorpc/definition.py
rename to lib/protorpc-1.0/protorpc/definition.py
diff --git a/lib/protorpc/protorpc/descriptor.py b/lib/protorpc-1.0/protorpc/descriptor.py
similarity index 100%
rename from lib/protorpc/protorpc/descriptor.py
rename to lib/protorpc-1.0/protorpc/descriptor.py
diff --git a/lib/protorpc/protorpc/generate.py b/lib/protorpc-1.0/protorpc/generate.py
similarity index 100%
rename from lib/protorpc/protorpc/generate.py
rename to lib/protorpc-1.0/protorpc/generate.py
diff --git a/lib/protorpc/protorpc/generate_proto.py b/lib/protorpc-1.0/protorpc/generate_proto.py
similarity index 100%
rename from lib/protorpc/protorpc/generate_proto.py
rename to lib/protorpc-1.0/protorpc/generate_proto.py
diff --git a/lib/protorpc/protorpc/generate_python.py b/lib/protorpc-1.0/protorpc/generate_python.py
similarity index 100%
rename from lib/protorpc/protorpc/generate_python.py
rename to lib/protorpc-1.0/protorpc/generate_python.py
diff --git a/lib/protorpc/protorpc/google_imports.py b/lib/protorpc-1.0/protorpc/google_imports.py
similarity index 100%
rename from lib/protorpc/protorpc/google_imports.py
rename to lib/protorpc-1.0/protorpc/google_imports.py
diff --git a/lib/protorpc/protorpc/message_types.py b/lib/protorpc-1.0/protorpc/message_types.py
similarity index 100%
rename from lib/protorpc/protorpc/message_types.py
rename to lib/protorpc-1.0/protorpc/message_types.py
diff --git a/lib/protorpc/protorpc/messages.py b/lib/protorpc-1.0/protorpc/messages.py
similarity index 100%
rename from lib/protorpc/protorpc/messages.py
rename to lib/protorpc-1.0/protorpc/messages.py
diff --git a/lib/protorpc/protorpc/protobuf.py b/lib/protorpc-1.0/protorpc/protobuf.py
similarity index 100%
rename from lib/protorpc/protorpc/protobuf.py
rename to lib/protorpc-1.0/protorpc/protobuf.py
diff --git a/lib/protorpc/protorpc/protojson.py b/lib/protorpc-1.0/protorpc/protojson.py
similarity index 100%
rename from lib/protorpc/protorpc/protojson.py
rename to lib/protorpc-1.0/protorpc/protojson.py
diff --git a/lib/protorpc/protorpc/protourlencode.py b/lib/protorpc-1.0/protorpc/protourlencode.py
similarity index 100%
rename from lib/protorpc/protorpc/protourlencode.py
rename to lib/protorpc-1.0/protorpc/protourlencode.py
diff --git a/lib/protorpc/protorpc/registry.py b/lib/protorpc-1.0/protorpc/registry.py
similarity index 100%
rename from lib/protorpc/protorpc/registry.py
rename to lib/protorpc-1.0/protorpc/registry.py
diff --git a/lib/protorpc/protorpc/remote.py b/lib/protorpc-1.0/protorpc/remote.py
similarity index 100%
rename from lib/protorpc/protorpc/remote.py
rename to lib/protorpc-1.0/protorpc/remote.py
diff --git a/lib/protorpc/protorpc/static/base.html b/lib/protorpc-1.0/protorpc/static/base.html
similarity index 100%
rename from lib/protorpc/protorpc/static/base.html
rename to lib/protorpc-1.0/protorpc/static/base.html
diff --git a/lib/protorpc/protorpc/static/forms.html b/lib/protorpc-1.0/protorpc/static/forms.html
similarity index 100%
rename from lib/protorpc/protorpc/static/forms.html
rename to lib/protorpc-1.0/protorpc/static/forms.html
diff --git a/lib/protorpc/protorpc/static/forms.js b/lib/protorpc-1.0/protorpc/static/forms.js
similarity index 100%
rename from lib/protorpc/protorpc/static/forms.js
rename to lib/protorpc-1.0/protorpc/static/forms.js
diff --git a/lib/protorpc/protorpc/static/jquery-1.4.2.min.js b/lib/protorpc-1.0/protorpc/static/jquery-1.4.2.min.js
similarity index 100%
rename from lib/protorpc/protorpc/static/jquery-1.4.2.min.js
rename to lib/protorpc-1.0/protorpc/static/jquery-1.4.2.min.js
diff --git a/lib/protorpc/protorpc/static/jquery.json-2.2.min.js b/lib/protorpc-1.0/protorpc/static/jquery.json-2.2.min.js
similarity index 100%
rename from lib/protorpc/protorpc/static/jquery.json-2.2.min.js
rename to lib/protorpc-1.0/protorpc/static/jquery.json-2.2.min.js
diff --git a/lib/protorpc/protorpc/static/methods.html b/lib/protorpc-1.0/protorpc/static/methods.html
similarity index 100%
rename from lib/protorpc/protorpc/static/methods.html
rename to lib/protorpc-1.0/protorpc/static/methods.html
diff --git a/lib/protorpc/protorpc/transport.py b/lib/protorpc-1.0/protorpc/transport.py
similarity index 100%
rename from lib/protorpc/protorpc/transport.py
rename to lib/protorpc-1.0/protorpc/transport.py
diff --git a/lib/protorpc/protorpc/util.py b/lib/protorpc-1.0/protorpc/util.py
similarity index 100%
rename from lib/protorpc/protorpc/util.py
rename to lib/protorpc-1.0/protorpc/util.py
diff --git a/lib/protorpc/protorpc/webapp/__init__.py b/lib/protorpc-1.0/protorpc/webapp/__init__.py
similarity index 100%
rename from lib/protorpc/protorpc/webapp/__init__.py
rename to lib/protorpc-1.0/protorpc/webapp/__init__.py
diff --git a/lib/protorpc/protorpc/webapp/forms.py b/lib/protorpc-1.0/protorpc/webapp/forms.py
similarity index 100%
rename from lib/protorpc/protorpc/webapp/forms.py
rename to lib/protorpc-1.0/protorpc/webapp/forms.py
diff --git a/lib/protorpc/protorpc/webapp/service_handlers.py b/lib/protorpc-1.0/protorpc/webapp/service_handlers.py
similarity index 100%
rename from lib/protorpc/protorpc/webapp/service_handlers.py
rename to lib/protorpc-1.0/protorpc/webapp/service_handlers.py
diff --git a/lib/protorpc/protorpc/wsgi/__init__.py b/lib/protorpc-1.0/protorpc/wsgi/__init__.py
similarity index 100%
rename from lib/protorpc/protorpc/wsgi/__init__.py
rename to lib/protorpc-1.0/protorpc/wsgi/__init__.py
diff --git a/lib/protorpc/protorpc/wsgi/service.py b/lib/protorpc-1.0/protorpc/wsgi/service.py
similarity index 100%
rename from lib/protorpc/protorpc/wsgi/service.py
rename to lib/protorpc-1.0/protorpc/wsgi/service.py
diff --git a/lib/protorpc/protorpc/wsgi/util.py b/lib/protorpc-1.0/protorpc/wsgi/util.py
similarity index 100%
rename from lib/protorpc/protorpc/wsgi/util.py
rename to lib/protorpc-1.0/protorpc/wsgi/util.py
diff --git a/old_dev_appserver.py b/old_dev_appserver.py
index fa956c5..b15365d 100644
--- a/old_dev_appserver.py
+++ b/old_dev_appserver.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php b/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php
index f7f7049..ee87857 100644
--- a/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php
+++ b/php/sdk/google/appengine/api/cloud_storage/CloudStorageTools.php
@@ -80,7 +80,7 @@
* expected to be a closure that accepts a key, value pair where key is the
* header name, and value is the header value.
*/
- static $send_header = null;
+ static private $send_header = null;
/**
* Create an absolute URL that can be used by a user to asynchronously upload
@@ -107,7 +107,7 @@
* @throws CloudStorageException Thrown when there is a failure using the
* blobstore service.
*/
- public static function createUploadUrl($success_path, $options=array()) {
+ public static function createUploadUrl($success_path, $options = array()) {
$req = new CreateUploadURLRequest();
$resp = new CreateUploadURLResponse();
diff --git a/php/sdk/google/appengine/api/log/LogService.php b/php/sdk/google/appengine/api/log/LogService.php
index 8d47e37..bcbb6af 100644
--- a/php/sdk/google/appengine/api/log/LogService.php
+++ b/php/sdk/google/appengine/api/log/LogService.php
@@ -280,14 +280,14 @@
}
private static function setDefaultModuleVersion($request) {
- $mv = $request->addModuleVersion();
- $current_module = getenv('CURRENT_MODULE_ID');
- if ($current_module !== 'default') {
- $mv->setModuleId($current_module);
- }
- $current_version = getenv('CURRENT_VERSION_ID');
- $whole_version = explode('.', $current_version)[0];
- $mv->setVersionId($whole_version);
+ $mv = $request->addModuleVersion();
+ $current_module = getenv('CURRENT_MODULE_ID');
+ if ($current_module !== 'default') {
+ $mv->setModuleId($current_module);
+ }
+ $current_version = getenv('CURRENT_VERSION_ID');
+ $whole_version = explode('.', $current_version)[0];
+ $mv->setVersionId($whole_version);
}
/**
@@ -317,11 +317,10 @@
self::setDefaultModuleVersion($request);
if (is_string($request_ids)) {
- if (!preg_match(self::$REQUEST_ID_REGEX, $request_ids)) {
- throw new \InvalidArgumentException(
- "Invalid request id $request_ids");
- }
- $request->addRequestId($request_ids);
+ if (!preg_match(self::$REQUEST_ID_REGEX, $request_ids)) {
+ throw new \InvalidArgumentException("Invalid request id $request_ids");
+ }
+ $request->addRequestId($request_ids);
} else if (is_array($request_ids)) {
foreach ($request_ids as $id) {
if (!is_string($id)) {
diff --git a/php/sdk/google/appengine/api/mail/AdminMessage.php b/php/sdk/google/appengine/api/mail/AdminMessage.php
index f72807c..929f6c8 100644
--- a/php/sdk/google/appengine/api/mail/AdminMessage.php
+++ b/php/sdk/google/appengine/api/mail/AdminMessage.php
@@ -37,10 +37,9 @@
* Returns the class variable $set_functions array, or the corresponding
* value in that array if a key is provided.
*
- * @param string $key Key to get corresponding value for.
* @return mixed $set_functions array, or a string value from that array.
*/
- protected function getFunctionArray($key = null) {
+ protected function getFunctionArray() {
$allowed_functions = array_diff(self::$set_functions,
self::$disallowed_functions);
return $allowed_functions;
diff --git a/php/sdk/google/appengine/api/mail/BaseMessage.php b/php/sdk/google/appengine/api/mail/BaseMessage.php
index ed0a18e..8aaa01c 100644
--- a/php/sdk/google/appengine/api/mail/BaseMessage.php
+++ b/php/sdk/google/appengine/api/mail/BaseMessage.php
@@ -292,7 +292,7 @@
"Mail Service Error: Invalid header name.");
default:
throw $e;
- }
+ }
}
/**
diff --git a/php/sdk/google/appengine/api/mail/MailService.php b/php/sdk/google/appengine/api/mail/MailService.php
index 1181848..c2290ef 100644
--- a/php/sdk/google/appengine/api/mail/MailService.php
+++ b/php/sdk/google/appengine/api/mail/MailService.php
@@ -17,6 +17,8 @@
/**
* Allow users to send mail using the App Engine mail APIs.
*
+ *
+ * @codingStandardsIgnoreFile
*/
// Until we have a C extension for the devappserver2 environment, the mail
diff --git a/php/sdk/google/appengine/api/mail/Message.php b/php/sdk/google/appengine/api/mail/Message.php
index 459b217..60a4249 100644
--- a/php/sdk/google/appengine/api/mail/Message.php
+++ b/php/sdk/google/appengine/api/mail/Message.php
@@ -106,10 +106,9 @@
* Returns the class variable $set_functions array, or the corresponding
* value in that array if a key is provided.
*
- * @param string $key Key to get corresponding value for.
* @return mixed $set_functions array, or a string value from that array.
*/
- protected function getFunctionArray($key = null) {
+ protected function getFunctionArray() {
return self::$set_functions;
}
diff --git a/php/sdk/google/appengine/api/mail_service_pb.php b/php/sdk/google/appengine/api/mail_service_pb.php
index ba2d023..29597cf 100644
--- a/php/sdk/google/appengine/api/mail_service_pb.php
+++ b/php/sdk/google/appengine/api/mail_service_pb.php
@@ -29,6 +29,7 @@
const UNAUTHORIZED_SENDER = 3;
const INVALID_ATTACHMENT_TYPE = 4;
const INVALID_HEADER_NAME = 5;
+ const EMPTY_CONTENT_ID = 6;
}
}
namespace google\appengine {
@@ -105,9 +106,27 @@
public function hasData() {
return isset($this->Data);
}
+ public function getContentid() {
+ if (!isset($this->ContentID)) {
+ return '';
+ }
+ return $this->ContentID;
+ }
+ public function setContentid($val) {
+ $this->ContentID = $val;
+ return $this;
+ }
+ public function clearContentid() {
+ unset($this->ContentID);
+ return $this;
+ }
+ public function hasContentid() {
+ return isset($this->ContentID);
+ }
public function clear() {
$this->clearFilename();
$this->clearData();
+ $this->clearContentid();
}
public function byteSizePartial() {
$res = 0;
@@ -119,6 +138,10 @@
$res += 1;
$res += $this->lengthString(strlen($this->Data));
}
+ if (isset($this->ContentID)) {
+ $res += 1;
+ $res += $this->lengthString(strlen($this->ContentID));
+ }
return $res;
}
public function outputPartial($out) {
@@ -130,6 +153,10 @@
$out->putVarInt32(18);
$out->putPrefixedString($this->Data);
}
+ if (isset($this->ContentID)) {
+ $out->putVarInt32(26);
+ $out->putPrefixedString($this->ContentID);
+ }
}
public function tryMerge($d) {
while($d->avail() > 0) {
@@ -145,6 +172,11 @@
$this->setData(substr($d->buffer(), $d->pos(), $length));
$d->skip($length);
break;
+ case 26:
+ $length = $d->getVarInt32();
+ $this->setContentid(substr($d->buffer(), $d->pos(), $length));
+ $d->skip($length);
+ break;
case 0:
throw new \google\net\ProtocolBufferDecodeError();
break;
@@ -166,6 +198,9 @@
if ($x->hasData()) {
$this->setData($x->getData());
}
+ if ($x->hasContentid()) {
+ $this->setContentid($x->getContentid());
+ }
}
public function equals($x) {
if ($x === $this) { return true; }
@@ -173,6 +208,8 @@
if (isset($this->FileName) && $this->FileName !== $x->FileName) return false;
if (isset($this->Data) !== isset($x->Data)) return false;
if (isset($this->Data) && $this->Data !== $x->Data) return false;
+ if (isset($this->ContentID) !== isset($x->ContentID)) return false;
+ if (isset($this->ContentID) && $this->ContentID !== $x->ContentID) return false;
return true;
}
public function shortDebugString($prefix = "") {
@@ -183,6 +220,9 @@
if (isset($this->Data)) {
$res .= $prefix . "Data: " . $this->debugFormatString($this->Data) . "\n";
}
+ if (isset($this->ContentID)) {
+ $res .= $prefix . "ContentID: " . $this->debugFormatString($this->ContentID) . "\n";
+ }
return $res;
}
}
diff --git a/php/sdk/google/appengine/api/taskqueue/PushQueue.php b/php/sdk/google/appengine/api/taskqueue/PushQueue.php
index fd1b481..ac33c0f 100644
--- a/php/sdk/google/appengine/api/taskqueue/PushQueue.php
+++ b/php/sdk/google/appengine/api/taskqueue/PushQueue.php
@@ -38,7 +38,6 @@
use google\appengine\TaskQueueBulkAddResponse;
use google\appengine\TaskQueueServiceError\ErrorCode;
-
/**
* A PushQueue executes PushTasks by sending the task back to the application
* in the form of an HTTP request to one of the application's handlers.
@@ -59,11 +58,11 @@
'DELETE' => RequestMethod::DELETE
];
-/**
- * Construct a PushQueue
- *
- * @param string $name The name of the queue.
- */
+ /**
+ * Construct a PushQueue
+ *
+ * @param string $name The name of the queue.
+ */
public function __construct($name = 'default') {
if (!is_string($name)) {
throw new \InvalidArgumentException(
diff --git a/php/sdk/google/appengine/api/taskqueue/PushQueueTest.php b/php/sdk/google/appengine/api/taskqueue/PushQueueTest.php
index e6fc02a..38bacb7 100644
--- a/php/sdk/google/appengine/api/taskqueue/PushQueueTest.php
+++ b/php/sdk/google/appengine/api/taskqueue/PushQueueTest.php
@@ -35,7 +35,7 @@
$mockTime = 12345.6;
// This mocks out PHP's microtime() function.
-function microtime($get_as_float=false) {
+function microtime($get_as_float = false) {
if (!$get_as_float) {
die('microtime called with get_as_float=false');
}
diff --git a/php/sdk/google/appengine/api/taskqueue/PushTask.php b/php/sdk/google/appengine/api/taskqueue/PushTask.php
index 04730bc..1877496 100644
--- a/php/sdk/google/appengine/api/taskqueue/PushTask.php
+++ b/php/sdk/google/appengine/api/taskqueue/PushTask.php
@@ -37,7 +37,6 @@
use google\appengine\TaskQueueAddRequest\RequestMethod;
-
/**
* A PushTask encapsulates a unit of work that an application places onto a
* Push Queue for asnychronous execution. The queue executes that work by
@@ -103,7 +102,7 @@
* executes.</li>
* </ul>
*/
- public function __construct($url_path, $query_data=[], $options=[]) {
+ public function __construct($url_path, $query_data = [], $options = []) {
if (!is_string($url_path)) {
throw new \InvalidArgumentException('url_path must be a string. ' .
'Actual type: ' . gettype($url_path));
@@ -112,7 +111,7 @@
throw new \InvalidArgumentException(
'url_path must begin with \'/\'.');
}
- if (strpos($url_path, "?") !== false) {
+ if (strpos($url_path, '?') !== false) {
throw new \InvalidArgumentException(
'query strings not allowed in url_path.');
}
@@ -178,7 +177,7 @@
if (strlen($this->url) > self::MAX_URL_LENGTH) {
throw new \InvalidArgumentException(
'URL length greater than maximum of ' .
- PushTask::MAX_URL_LENGTH . '. URL: ' . $this->url);
+ self::MAX_URL_LENGTH . '. URL: ' . $this->url);
}
// Handle user specified headers.
@@ -195,7 +194,7 @@
if (empty($h)) {
continue;
}
- if (strpos($h, ':') == false) {
+ if (strpos($h, ':') === false) {
throw new \InvalidArgumentException(
'Each header must contain a colon. Header: ' . $h);
}
@@ -203,7 +202,6 @@
strncasecmp('content-type', $h, strlen('content-type')) == 0) {
throw new \InvalidArgumentException('Content-type header may not ' .
'be specified as it is set by the task.');
- continue;
}
$this->headers[] = $h;
}
diff --git a/php/sdk/google/appengine/api/taskqueue/PushTaskTest.php b/php/sdk/google/appengine/api/taskqueue/PushTaskTest.php
index eb0d262..2f1795a 100644
--- a/php/sdk/google/appengine/api/taskqueue/PushTaskTest.php
+++ b/php/sdk/google/appengine/api/taskqueue/PushTaskTest.php
@@ -34,7 +34,7 @@
$mockTime = 12345.6;
// This mocks out PHP's microtime() function.
-function microtime($get_as_float=false) {
+function microtime($get_as_float = false) {
if (!$get_as_float) {
die('microtime called with get_as_float=false');
}
@@ -370,7 +370,7 @@
$task_name = (new PushTask('/someUrl', [],
['header' => "custom-header: 54321\r\n" .
- "another-custom-header: abc"]))->add();
+ 'another-custom-header: abc']))->add();
$this->assertEquals('fred', $task_name);
$this->apiProxyMock->verify();
}
diff --git a/php/sdk/google/appengine/api/taskqueue/TaskAlreadyExistsException.php b/php/sdk/google/appengine/api/taskqueue/TaskAlreadyExistsException.php
index 8b34cc9..a17cf06 100644
--- a/php/sdk/google/appengine/api/taskqueue/TaskAlreadyExistsException.php
+++ b/php/sdk/google/appengine/api/taskqueue/TaskAlreadyExistsException.php
@@ -19,7 +19,7 @@
namespace google\appengine\api\taskqueue;
-require_once "google/appengine/api/taskqueue/TaskQueueException.php";
+require_once 'google/appengine/api/taskqueue/TaskQueueException.php';
/**
* Thrown when adding a task, but a task with the same name already exists in
diff --git a/php/sdk/google/appengine/api/taskqueue/TransientTaskQueueException.php b/php/sdk/google/appengine/api/taskqueue/TransientTaskQueueException.php
index cb0d73f..973db24 100644
--- a/php/sdk/google/appengine/api/taskqueue/TransientTaskQueueException.php
+++ b/php/sdk/google/appengine/api/taskqueue/TransientTaskQueueException.php
@@ -19,7 +19,7 @@
namespace google\appengine\api\taskqueue;
-require_once "google/appengine/api/taskqueue/TaskQueueException.php";
+require_once 'google/appengine/api/taskqueue/TaskQueueException.php';
/**
* Thrown when there is a transient failure using Task Queue service, i.e.
diff --git a/php/sdk/google/appengine/api/users/User.php b/php/sdk/google/appengine/api/users/User.php
index ff55c0a..4068ab2 100644
--- a/php/sdk/google/appengine/api/users/User.php
+++ b/php/sdk/google/appengine/api/users/User.php
@@ -87,7 +87,7 @@
*/
public function getNickname() {
if ($this->email != null && $this->auth_domain != null &&
- util\endsWith($this->email, $this->auth_domain)) {
+ util\endsWith($this->email, '@' . $this->auth_domain)) {
$suffixLen = strlen($this->auth_domain) + 1;
return substr($this->email, 0, -$suffixLen);
} else if ($this->federated_identity) {
diff --git a/php/sdk/google/appengine/api/users/UserService.php b/php/sdk/google/appengine/api/users/UserService.php
index 253a0b7..d951c28 100644
--- a/php/sdk/google/appengine/api/users/UserService.php
+++ b/php/sdk/google/appengine/api/users/UserService.php
@@ -105,9 +105,9 @@
*/
public static function getCurrentUser() {
$email = getenv('USER_EMAIL');
- $userId = getenv('USER_ID');
- $federatedIdentity = getenv('FEDERATED_IDENTITY');
- $federatedProvider = getenv('FEDERATED_PROVIDER');
+ $userId = getenv('USER_ID');
+ $federatedIdentity = getenv('FEDERATED_IDENTITY');
+ $federatedProvider = getenv('FEDERATED_PROVIDER');
if (!$email && !$federatedIdentity) {
return null;
diff --git a/php/sdk/google/appengine/api/users/UserTest.php b/php/sdk/google/appengine/api/users/UserTest.php
index c67d7e9..ba8f75b 100644
--- a/php/sdk/google/appengine/api/users/UserTest.php
+++ b/php/sdk/google/appengine/api/users/UserTest.php
@@ -95,6 +95,6 @@
'bill@example.com', 'http://google.com/bill', null, '22772');
$this->assertEquals(
"User(email='bill@example.com',federated_identity=" .
- "'http://google.com/bill',user_id='22772')", (string)$bill);
+ "'http://google.com/bill',user_id='22772')", (string) $bill);
}
}
diff --git a/remote_api_shell.py b/remote_api_shell.py
index fa956c5..b15365d 100644
--- a/remote_api_shell.py
+++ b/remote_api_shell.py
@@ -99,7 +99,7 @@
os.path.join(DIR_PATH, 'lib', 'fancy_urllib'),
os.path.join(DIR_PATH, 'lib', 'ipaddr'),
os.path.join(DIR_PATH, 'lib', 'jinja2-2.6'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
os.path.join(DIR_PATH, 'lib', 'PyAMF'),
os.path.join(DIR_PATH, 'lib', 'markupsafe'),
os.path.join(DIR_PATH, 'lib', 'webob_0_9'),
diff --git a/run_tests.py b/run_tests.py
index 9d5db32..f05c059 100644
--- a/run_tests.py
+++ b/run_tests.py
@@ -39,7 +39,7 @@
os.path.join(DIR_PATH, 'lib', 'webob-1.2.3'),
os.path.join(DIR_PATH, 'lib', 'webapp2-2.5.1'),
os.path.join(DIR_PATH, 'lib', 'mox'),
- os.path.join(DIR_PATH, 'lib', 'protorpc'),
+ os.path.join(DIR_PATH, 'lib', 'protorpc-1.0'),
]