Skip to content
Snippets Groups Projects
Commit 74a8b3ee authored by Ben Adida's avatar Ben Adida
Browse files

added a few missing components

parent 060f7dab
Branches
Tags
No related merge requests found
Showing
with 1334 additions and 0 deletions
#!/usr/bin/python2.4
#
# Copyright 2008 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.
import sys
import logging
from django.core.management.base import BaseCommand
def run_appcfg():
# import this so that we run through the checks at the beginning
# and report the appropriate errors
import appcfg
# We don't really want to use that one though, it just executes this one
from google.appengine.tools import appcfg
# Reset the logging level to WARN as appcfg will spew tons of logs on INFO
logging.getLogger().setLevel(logging.WARN)
# Note: if we decide to change the name of this command to something other
# than 'rollback' we will have to munge the args to replace whatever
# we called it with 'rollback'
new_args = sys.argv[:]
new_args.append('.')
appcfg.main(new_args)
class Command(BaseCommand):
"""Calls the appcfg.py's rollback command for the current project.
Any additional arguments are passed directly to appcfg.py.
"""
help = 'Calls appcfg.py rollback for the current project.'
args = '[any appcfg.py options]'
def run_from_argv(self, argv):
run_appcfg()
#!/usr/bin/python2.4
#
# Copyright 2008 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.
import os
import sys
from appengine_django.db.base import get_datastore_paths
from django.core.management.base import BaseCommand
def start_dev_appserver():
"""Starts the appengine dev_appserver program for the Django project.
The appserver is run with default parameters. If you need to pass any special
parameters to the dev_appserver you will have to invoke it manually.
"""
from google.appengine.tools import dev_appserver_main
progname = sys.argv[0]
args = []
# hack __main__ so --help in dev_appserver_main works OK.
sys.modules['__main__'] = dev_appserver_main
# Set bind ip/port if specified.
if len(sys.argv) > 2:
addrport = sys.argv[2]
try:
addr, port = addrport.split(":")
except ValueError:
addr, port = None, addrport
if not port.isdigit():
print "Error: '%s' is not a valid port number." % port
sys.exit(1)
else:
addr, port = None, "8000"
if addr:
args.extend(["--address", addr])
if port:
args.extend(["--port", port])
# Add email settings
from django.conf import settings
args.extend(['--smtp_host', settings.EMAIL_HOST,
'--smtp_port', str(settings.EMAIL_PORT),
'--smtp_user', settings.EMAIL_HOST_USER,
'--smtp_password', settings.EMAIL_HOST_PASSWORD])
# Allow skipped files so we don't die
args.extend(['--allow_skipped_files'])
# Pass the application specific datastore location to the server.
p = get_datastore_paths()
args.extend(["--datastore_path", p[0], "--history_path", p[1]])
# Append the current working directory to the arguments.
dev_appserver_main.main([progname] + args + [os.getcwdu()])
class Command(BaseCommand):
"""Overrides the default Django runserver command.
Instead of starting the default Django development server this command
fires up a copy of the full fledged appengine dev_appserver that emulates
the live environment your application will be deployed to.
"""
help = 'Runs a copy of the appengine development server.'
args = '[optional port number, or ipaddr:port]'
def run_from_argv(self, argv):
start_dev_appserver()
# Copyright 2008 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.
import os
import django
from django.core.management.commands import startapp
import appengine_django
class Command(startapp.Command):
def handle_label(self, *args, **kwds):
"""Temporary adjust django.__path__ to load app templates from the
helpers directory.
"""
old_path = django.__path__
django.__path__ = appengine_django.__path__
startapp.Command.handle_label(self, *args, **kwds)
django.__path__ = old_path
class ProjectCommand(Command):
def __init__(self, project_directory):
super(ProjectCommand, self).__init__()
self.project_directory = project_directory
def handle_label(self, app_name, **options):
super(ProjectCommand, self).handle_label(app_name, self.project_directory,
**options)
#!/usr/bin/python2.4
#
# Copyright 2008 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.
import os
import sys
from appengine_django.db.base import destroy_datastore
from appengine_django.db.base import get_test_datastore_paths
from django.core.management.base import BaseCommand
class Command(BaseCommand):
"""Overrides the default Django testserver command.
Instead of starting the default Django development server this command fires
up a copy of the full fledged appengine dev_appserver.
The appserver is always initialised with a blank datastore with the specified
fixtures loaded into it.
"""
help = 'Runs the development server with data from the given fixtures.'
def run_from_argv(self, argv):
fixtures = argv[2:]
# Ensure an on-disk test datastore is used.
from django.db import connection
connection.use_test_datastore = True
connection.test_datastore_inmemory = False
# Flush any existing test datastore.
connection.flush()
# Load the fixtures.
from django.core.management import call_command
call_command('loaddata', 'initial_data')
if fixtures:
call_command('loaddata', *fixtures)
# Build new arguments for dev_appserver.
datastore_path, history_path = get_test_datastore_paths(False)
new_args = argv[0:1]
new_args.extend(['--datastore_path', datastore_path])
new_args.extend(['--history_path', history_path])
new_args.extend([os.getcwdu()])
# Add email settings
from django.conf import settings
new_args.extend(['--smtp_host', settings.EMAIL_HOST,
'--smtp_port', str(settings.EMAIL_PORT),
'--smtp_user', settings.EMAIL_HOST_USER,
'--smtp_password', settings.EMAIL_HOST_PASSWORD])
# Allow skipped files so we don't die
new_args.extend(['--allow_skipped_files'])
# Start the test dev_appserver.
from google.appengine.tools import dev_appserver_main
dev_appserver_main.main(new_args)
#!/usr/bin/python2.4
#
# Copyright 2008 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.
import sys
import logging
from django.core.management.base import BaseCommand
def run_appcfg():
# import this so that we run through the checks at the beginning
# and report the appropriate errors
import appcfg
# We don't really want to use that one though, it just executes this one
from google.appengine.tools import appcfg
# Reset the logging level to WARN as appcfg will spew tons of logs on INFO
logging.getLogger().setLevel(logging.WARN)
# Note: if we decide to change the name of this command to something other
# than 'update' we will have to munge the args to replace whatever
# we called it with 'update'
new_args = sys.argv[:]
new_args.append('.')
appcfg.main(new_args)
class Command(BaseCommand):
"""Calls the appcfg.py's update command for the current project.
Any additional arguments are passed directly to appcfg.py.
"""
help = 'Calls appcfg.py update for the current project.'
args = '[any appcfg.py options]'
def run_from_argv(self, argv):
run_appcfg()
#!/usr/bin/python2.4
#
# Copyright 2008 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.
import sys
import logging
from django.core.management.base import BaseCommand
def run_appcfg():
# import this so that we run through the checks at the beginning
# and report the appropriate errors
import appcfg
# We don't really want to use that one though, it just executes this one
from google.appengine.tools import appcfg
# Reset the logging level to WARN as appcfg will spew tons of logs on INFO
logging.getLogger().setLevel(logging.WARN)
# Note: if we decide to change the name of this command to something other
# than 'vacuum_indexes' we will have to munge the args to replace whatever
# we called it with 'vacuum_indexes'
new_args = sys.argv[:]
new_args.append('.')
appcfg.main(new_args)
class Command(BaseCommand):
"""Calls the appcfg.py's vacuum_indexes command for the current project.
Any additional arguments are passed directly to appcfg.py.
"""
help = 'Calls appcfg.py vacuum_indexes for the current project.'
args = '[any appcfg.py options]'
def run_from_argv(self, argv):
run_appcfg()
#!/usr/bin/python2.4
#
# Copyright 2008 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.
import sys
import types
from google.appengine.ext import db
from django import VERSION
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields import Field
from django.db.models.options import Options
from django.db.models.loading import register_models, get_model
class ModelManager(object):
"""Replacement for the default Django model manager."""
def __init__(self, owner):
self.owner = owner
def __getattr__(self, name):
"""Pass all attribute requests through to the real model"""
return getattr(self.owner, name)
class ModelOptions(object):
"""Replacement for the default Django options class.
This class sits at ._meta of each model. The primary information supplied by
this class that needs to be stubbed out is the list of fields on the model.
"""
def __init__(self, cls):
self.object_name = cls.__name__
self.module_name = self.object_name.lower()
model_module = sys.modules[cls.__module__]
self.app_label = model_module.__name__.split('.')[-2]
self.abstract = False
class pk:
"""Stub the primary key to always be 'key_name'"""
name = "key_name"
def __str__(self):
return "%s.%s" % (self.app_label, self.module_name)
@property
def many_to_many(self):
"""The datastore does not support many to many relationships."""
return []
class Relation(object):
def __init__(self, to):
self.field_name = "key_name"
def PropertyWrapper(prop):
"""Wrapper for db.Property to make it look like a Django model Property"""
if isinstance(prop, db.Reference):
prop.rel = Relation(prop.reference_class)
else:
prop.rel = None
prop.serialize = True
return prop
class PropertiedClassWithDjango(db.PropertiedClass):
"""Metaclass for the combined Django + App Engine model class.
This metaclass inherits from db.PropertiedClass in the appengine library.
This metaclass has two additional purposes:
1) Register each model class created with Django (the parent class will take
care of registering it with the appengine libraries).
2) Add the (minimum number) of attributes and methods to make Django believe
the class is a normal Django model.
The resulting classes are still not generally useful as Django classes and
are intended to be used by Django only in limited situations such as loading
and dumping fixtures.
"""
def __new__(cls, name, bases, attrs):
"""Creates a combined appengine and Django model.
The resulting model will be known to both the appengine libraries and
Django.
"""
if name == 'BaseModel':
# This metaclass only acts on subclasses of BaseModel.
return super(PropertiedClassWithDjango, cls).__new__(cls, name,
bases, attrs)
new_class = super(PropertiedClassWithDjango, cls).__new__(cls, name,
bases, attrs)
new_class._meta = ModelOptions(new_class)
new_class.objects = ModelManager(new_class)
new_class._default_manager = new_class.objects
new_class.DoesNotExist = types.ClassType('DoesNotExist',
(ObjectDoesNotExist,), {})
m = get_model(new_class._meta.app_label, name, False)
if m:
return m
register_models(new_class._meta.app_label, new_class)
return get_model(new_class._meta.app_label, name, False)
def __init__(cls, name, bases, attrs):
"""Initialises the list of Django properties.
This method takes care of wrapping the properties created by the superclass
so that they look like Django properties and installing them into the
._meta object of the class so that Django can find them at the appropriate
time.
"""
super(PropertiedClassWithDjango, cls).__init__(name, bases, attrs)
if name == 'BaseModel':
# This metaclass only acts on subclasses of BaseModel.
return
fields = [PropertyWrapper(p) for p in cls._properties.values()]
cls._meta.local_fields = fields
class BaseModel(db.Model):
"""Combined appengine and Django model.
All models used in the application should derive from this class.
"""
__metaclass__ = PropertiedClassWithDjango
def __eq__(self, other):
if not isinstance(other, self.__class__):
return False
return self._get_pk_val() == other._get_pk_val()
def __ne__(self, other):
return not self.__eq__(other)
def _get_pk_val(self):
"""Return the string representation of the model's key"""
return unicode(self.key())
def __repr__(self):
"""Create a string that can be used to construct an equivalent object.
e.g. eval(repr(obj)) == obj
"""
# First, creates a dictionary of property names and values. Note that
# property values, not property objects, has to be passed in to constructor.
def _MakeReprTuple(prop_name):
prop = getattr(self.__class__, prop_name)
return (prop_name, prop.get_value_for_datastore(self))
d = dict([_MakeReprTuple(prop_name) for prop_name in self.properties()])
return "%s(**%s)" % (self.__class__.__name__, repr(d))
class RegistrationTestModel(BaseModel):
"""Used to check registration with Django is working correctly.
Django 0.96 only recognises models defined within an applications models
module when get_models() is called so this definition must be here rather
than within the associated test (tests/model_test.py).
"""
pass
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""This file acts as a very minimal replacement for the 'imp' module.
It contains only what Django expects to use and does not actually implement the
same functionality as the real 'imp' module.
"""
def find_module(name, path=None):
"""Django needs imp.find_module, but it works fine if nothing is found."""
raise ImportError
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""
A Python "serializer", based on the default Django python serializer.
The only customisation is in the deserialization process which needs to take
special care to resolve the name and parent attributes of the key for each
entity and also recreate the keys for any references appropriately.
"""
from django.conf import settings
from django.core.serializers import base
from django.core.serializers import python
from django.db import models
from google.appengine.api import datastore_types
from google.appengine.ext import db
from django.utils.encoding import smart_unicode
Serializer = python.Serializer
class FakeParent(object):
"""Fake parent 'model' like object.
This class exists to allow a parent object to be provided to a new model
without having to load the parent instance itself.
"""
def __init__(self, parent_key):
self._entity = parent_key
def Deserializer(object_list, **options):
"""Deserialize simple Python objects back into Model instances.
It's expected that you pass the Python objects themselves (instead of a
stream or a string) to the constructor
"""
models.get_apps()
for d in object_list:
# Look up the model and starting build a dict of data for it.
Model = python._get_model(d["model"])
data = {}
key = resolve_key(Model._meta.module_name, d["pk"])
if key.name():
data["key_name"] = key.name()
parent = None
if key.parent():
parent = FakeParent(key.parent())
m2m_data = {}
# Handle each field
for (field_name, field_value) in d["fields"].iteritems():
if isinstance(field_value, str):
field_value = smart_unicode(
field_value, options.get("encoding",
settings.DEFAULT_CHARSET),
strings_only=True)
field = Model.properties()[field_name]
if isinstance(field, db.Reference):
# Resolve foreign key references.
data[field.name] = resolve_key(Model._meta.module_name, field_value)
if not data[field.name].name():
raise base.DeserializationError(u"Cannot load Reference with "
"unnamed key: '%s'" % field_value)
else:
data[field.name] = field.validate(field_value)
# Create the new model instance with all it's data, but no parent.
object = Model(**data)
# Now add the parent into the hidden attribute, bypassing the type checks
# in the Model's __init__ routine.
object._parent = parent
# When the deserialized object is saved our replacement DeserializedObject
# class will set object._parent to force the real parent model to be loaded
# the first time it is referenced.
yield base.DeserializedObject(object, m2m_data)
def resolve_key(model, key_data):
"""Creates a Key instance from a some data.
Args:
model: The name of the model this key is being resolved for. Only used in
the fourth case below (a plain key_name string).
key_data: The data to create a key instance from. May be in four formats:
* The str() output of a key instance. Eg. A base64 encoded string.
* The repr() output of a key instance. Eg. A string for eval().
* A list of arguments to pass to db.Key.from_path.
* A single string value, being the key_name of the instance. When this
format is used the resulting key has no parent, and is for the model
named in the model parameter.
Returns:
An instance of db.Key. If the data cannot be used to create a Key instance
an error will be raised.
"""
if isinstance(key_data, list):
# The key_data is a from_path sequence.
return db.Key.from_path(*key_data)
elif isinstance(key_data, basestring):
if key_data.find("from_path") != -1:
# key_data is encoded in repr(key) format
return eval(key_data)
else:
try:
# key_data encoded a str(key) format
return db.Key(key_data)
except datastore_types.datastore_errors.BadKeyError, e:
# Final try, assume it's a plain key name for the model.
return db.Key.from_path(model, key_data)
else:
raise base.DeserializationError(u"Invalid key data: '%s'" % key_data)
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""
Replaces the default Django XML serializer with one that uses the built in
ToXml method for each entity.
"""
import re
from django.conf import settings
from django.core.serializers import base
from django.core.serializers import xml_serializer
from django.db import models
from google.appengine.api import datastore_types
from google.appengine.ext import db
from python import FakeParent
getInnerText = xml_serializer.getInnerText
class Serializer(xml_serializer.Serializer):
"""A Django Serializer class to convert datastore models to XML.
This class relies on the ToXml method of the entity behind each model to do
the hard work.
"""
def __init__(self, *args, **kwargs):
super(Serializer, self).__init__(*args, **kwargs)
self._objects = []
def handle_field(self, obj, field):
"""Fields are not handled individually."""
pass
def handle_fk_field(self, obj, field):
"""Fields are not handled individually."""
pass
def start_object(self, obj):
"""Nothing needs to be done to start an object."""
pass
def end_object(self, obj):
"""Serialize the object to XML and add to the list of objects to output.
The output of ToXml is manipulated to replace the datastore model name in
the "kind" tag with the Django model name (which includes the Django
application name) to make importing easier.
"""
xml = obj._entity.ToXml()
xml = xml.replace(u"""kind="%s" """ % obj._entity.kind(),
u"""kind="%s" """ % unicode(obj._meta))
self._objects.append(xml)
def getvalue(self):
"""Wrap the serialized objects with XML headers and return."""
str = u"""<?xml version="1.0" encoding="utf-8"?>\n"""
str += u"""<django-objects version="1.0">\n"""
str += u"".join(self._objects)
str += u"""</django-objects>"""
return str
class Deserializer(xml_serializer.Deserializer):
"""A Django Deserializer class to convert XML to Django objects.
This is a fairly manualy and simplistic XML parser, it supports just enough
functionality to read the keys and fields for an entity from the XML file and
construct a model object.
"""
def next(self):
"""Replacement next method to look for 'entity'.
The default next implementation exepects 'object' nodes which is not
what the entity's ToXml output provides.
"""
for event, node in self.event_stream:
if event == "START_ELEMENT" and node.nodeName == "entity":
self.event_stream.expandNode(node)
return self._handle_object(node)
raise StopIteration
def _handle_object(self, node):
"""Convert an <entity> node to a DeserializedObject"""
Model = self._get_model_from_node(node, "kind")
data = {}
key = db.Key(node.getAttribute("key"))
if key.name():
data["key_name"] = key.name()
parent = None
if key.parent():
parent = FakeParent(key.parent())
m2m_data = {}
# Deseralize each field.
for field_node in node.getElementsByTagName("property"):
# If the field is missing the name attribute, bail (are you
# sensing a pattern here?)
field_name = field_node.getAttribute("name")
if not field_name:
raise base.DeserializationError("<field> node is missing the 'name' "
"attribute")
field = Model.properties()[field_name]
field_value = getInnerText(field_node).strip()
if isinstance(field, db.Reference):
m = re.match("tag:.*\[(.*)\]", field_value)
if not m:
raise base.DeserializationError(u"Invalid reference value: '%s'" %
field_value)
key = m.group(1)
key_obj = db.Key(key)
if not key_obj.name():
raise base.DeserializationError(u"Cannot load Reference with "
"unnamed key: '%s'" % field_value)
data[field.name] = key_obj
else:
data[field.name] = field.validate(field_value)
# Create the new model instance with all it's data, but no parent.
object = Model(**data)
# Now add the parent into the hidden attribute, bypassing the type checks
# in the Model's __init__ routine.
object._parent = parent
# When the deserialized object is saved our replacement DeserializedObject
# class will set object._parent to force the real parent model to be loaded
# the first time it is referenced.
return base.DeserializedObject(object, m2m_data)
#!/usr/bin/python2.4
#
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from datetime import datetime
from django.contrib.sessions.backends import base
from django.core.exceptions import SuspiciousOperation
from appengine_django.sessions.models import Session
class SessionStore(base.SessionBase):
"""A key-based session store for Google App Engine."""
def load(self):
session = self._get_session(self.session_key)
if session:
try:
return self.decode(session.session_data)
except SuspiciousOperation:
# Create a new session_key for extra security.
pass
self.session_key = self._get_new_session_key()
self._session_cache = {}
self.save()
# Ensure the user is notified via a new cookie.
self.modified = True
return {}
def save(self, must_create=False):
if must_create and self.exists(self.session_key):
raise base.CreateError
session = Session(
key_name='k:' + self.session_key,
session_data = self.encode(self._session),
expire_date = self.get_expiry_date())
session.put()
def exists(self, session_key):
return Session.get_by_key_name('k:' + session_key) is not None
def delete(self, session_key=None):
if session_key is None:
session_key = self._session_key
session = self._get_session(session_key=session_key)
if session:
session.delete()
def _get_session(self, session_key):
session = Session.get_by_key_name('k:' + session_key)
if session:
if session.expire_date > datetime.now():
return session
session.delete()
return None
def create(self):
while True:
self.session_key = self._get_new_session_key()
try:
# Save immediately to ensure we have a unique entry in the
# database.
self.save(must_create=True)
except base.CreateError:
# Key wasn't unique. Try again.
continue
self.modified = True
self._session_cache = {}
return
#!/usr/bin/python2.4
#
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from google.appengine.ext import db
class Session(db.Model):
"""Django compatible App Engine Datastore session model."""
session_data = db.BlobProperty()
expire_date = db.DateTimeProperty()
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""Loads all the _test.py files into the top level of the package.
This file is a hack around the fact that Django expects the tests "module" to
be a single tests.py file and cannot handle a tests package inside an
application.
All _test.py files inside this package are imported and any classes derived
from unittest.TestCase are then referenced from this file itself so that they
appear at the top level of the tests "module" that Django will import.
"""
import os
import re
import types
import unittest
TEST_RE = r"^.*_test.py$"
# Search through every file inside this package.
test_names = []
test_dir = os.path.dirname( __file__)
for filename in os.listdir(test_dir):
if not re.match(TEST_RE, filename):
continue
# Import the test file and find all TestClass clases inside it.
test_module = __import__('appengine_django.tests.%s' %
filename[:-3], {}, {},
filename[:-3])
for name in dir(test_module):
item = getattr(test_module, name)
if not (isinstance(item, (type, types.ClassType)) and
issubclass(item, unittest.TestCase)):
continue
# Found a test, bring into the module namespace.
exec "%s = item" % name
test_names.append(name)
# Hide everything other than the test cases from other modules.
__all__ = test_names
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""
Tests that the manage.py commands execute correctly.
These tests only verify that the commands execute and exit with a success code.
They are intended to catch import exceptions and similar problems, it is left
up to tests in other modules to verify that the functionality of each command
works correctly.
"""
import os
import re
import signal
import subprocess
import tempfile
import time
import unittest
from django.db.models import get_models
from google.appengine.ext import db
from appengine_django.models import BaseModel
from appengine_django.models import ModelManager
from appengine_django.models import ModelOptions
from appengine_django.models import RegistrationTestModel
class CommandsTest(unittest.TestCase):
"""Unit tests for the manage.py commands."""
# How many seconds to wait for a command to exit.
COMMAND_TIMEOUT = 10
def runCommand(self, command, args=None, int_after=None, input=None):
"""Helper to run the specified command in a child process.
Args:
command: The name of the command to run.
args: List of command arguments to run the command with.
int_after: If set to a positive integer, SIGINT will be sent to the
running child process after this many seconds to cause an exit. This
should be less than the COMMAND_TIMEOUT value (10 seconds).
input: A string to write to stdin when the command starts. stdin is
closed after the string is written.
Returns:
rc: The integer return code of the process.
output: A string containing the childs output.
"""
if not args:
args = []
start = time.time()
int_sent = False
fd = subprocess.PIPE
child = subprocess.Popen(["./manage.py", command] + args, stdin=fd,
stdout=fd, stderr=fd, cwd=os.getcwdu())
if input:
child.stdin.write(input)
child.stdin.close()
while 1:
rc = child.poll()
if rc is not None:
# Child has exited.
break
elapsed = time.time() - start
if int_after and int_after > 0 and elapsed > int_after and not int_sent:
# Sent SIGINT as requested, give child time to exit cleanly.
os.kill(child.pid, signal.SIGINT)
start = time.time()
int_sent = True
continue
if elapsed < self.COMMAND_TIMEOUT:
continue
# Command is over time, kill and exit loop.
os.kill(child.pid, signal.SIGKILL)
time.sleep(2) # Give time for the signal to be received.
break
# Return status and output.
return rc, child.stdout.read(), child.stderr.read()
def assertCommandSucceeds(self, command, *args, **kwargs):
"""Asserts that the specified command successfully completes.
Args:
command: The name of the command to run.
All other arguments are passed directly through to the runCommand
routine.
Raises:
This function does not return anything but will raise assertion errors if
the command does not exit successfully.
"""
rc, stdout, stderr = self.runCommand(command, *args, **kwargs)
fd, tempname = tempfile.mkstemp()
os.write(fd, stdout)
os.close(fd)
self.assertEquals(0, rc,
"%s did not return successfully (rc: %d): Output in %s" %
(command, rc, tempname))
os.unlink(tempname)
def getCommands(self):
"""Returns a list of valid commands for manage.py.
Args:
None
Returns:
A list of valid commands for manage.py as read from manage.py's help
output.
"""
rc, stdout, stderr = self.runCommand("help")
parts = re.split("Available subcommands:", stderr)
if len(parts) < 2:
return []
return [t.strip() for t in parts[-1].split("\n") if t.strip()]
def testDiffSettings(self):
"""Tests the diffsettings command."""
self.assertCommandSucceeds("diffsettings")
def testDumpData(self):
"""Tests the dumpdata command."""
self.assertCommandSucceeds("dumpdata")
def testFlush(self):
"""Tests the flush command."""
self.assertCommandSucceeds("flush")
def testLoadData(self):
"""Tests the loaddata command."""
self.assertCommandSucceeds("loaddata")
def testLoadData(self):
"""Tests the loaddata command."""
self.assertCommandSucceeds("loaddata")
def testReset(self):
"""Tests the reste command."""
self.assertCommandSucceeds("reset", ["appengine_django"])
def testRunserver(self):
"""Tests the runserver command."""
self.assertCommandSucceeds("runserver", int_after=2.0)
def testShell(self):
"""Tests the shell command."""
self.assertCommandSucceeds("shell", input="exit")
def testUpdate(self):
"""Tests that the update command exists.
Cannot test that it works without mocking out parts of dev_appserver so for
now we just assume that if it is present it will work.
"""
cmd_list = self.getCommands()
self.assert_("update" in cmd_list)
def testZipCommandListFiltersCorrectly(self):
"""When running under a zipfile test that only valid commands are found."""
cmd_list = self.getCommands()
self.assert_("__init__" not in cmd_list)
self.assert_("base" not in cmd_list)
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""Tests that the core module functionality is present and functioning."""
import unittest
from django.test import TestCase as DjangoTestCase
from appengine_django import appid
from appengine_django import have_appserver
class AppengineDjangoTest(unittest.TestCase):
"""Tests that the helper module has been correctly installed."""
def testAppidProvided(self):
"""Tests that application ID and configuration has been loaded."""
self.assert_(appid is not None)
def testAppserverDetection(self):
"""Tests that the appserver detection flag is present and correct."""
# It seems highly unlikely that these tests would ever be run from within
# an appserver.
self.assertEqual(have_appserver, False)
class DjangoTestCaseTest(DjangoTestCase):
"""Tests that the tests can be subclassed from Django's TestCase class."""
def testPassing(self):
"""Tests that tests with Django's TestCase class work."""
self.assert_(True)
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""Tests that the db module correctly initialises the API stubs."""
import unittest
from django.db import connection
from django.db.backends.appengine.base import DatabaseWrapper
from appengine_django import appid
from appengine_django.db import base
class DatastoreTest(unittest.TestCase):
"""Tests that the datastore stubs have been correctly setup."""
def testDjangoDBConnection(self):
"""Tests that the Django DB connection is using our replacement."""
self.assert_(isinstance(connection, DatabaseWrapper))
def testDjangoDBConnectionStubs(self):
"""Tests that members required by Django are stubbed."""
self.assert_(hasattr(connection, "features"))
self.assert_(hasattr(connection, "ops"))
def testDjangoDBErrorClasses(self):
"""Tests that the error classes required by Django are stubbed."""
self.assert_(hasattr(base, "DatabaseError"))
self.assert_(hasattr(base, "IntegrityError"))
def testDatastorePath(self):
"""Tests that the datastore path contains the app name."""
d_path, h_path = base.get_datastore_paths()
self.assertNotEqual(-1, d_path.find("django_%s" % appid))
self.assertNotEqual(-1, h_path.find("django_%s" % appid))
def testTestInMemoryDatastorePath(self):
"""Tests that the test datastore is using the in-memory datastore."""
td_path, th_path = base.get_test_datastore_paths()
self.assert_(td_path is None)
self.assert_(th_path is None)
def testTestFilesystemDatastorePath(self):
"""Tests that the test datastore is on the filesystem when requested."""
td_path, th_path = base.get_test_datastore_paths(False)
self.assertNotEqual(-1, td_path.find("testdatastore"))
self.assertNotEqual(-1, th_path.find("testdatastore"))
#!/usr/bin/python2.4
#
# Copyright 2008 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.
"""Ensures the App Engine memcache API works as Django's memcache backend."""
import unittest
from django.core.cache import get_cache
from appengine_django import appid
from appengine_django import have_appserver
class AppengineMemcacheTest(unittest.TestCase):
"""Tests that the memcache backend works."""
def setUp(self):
"""Get the memcache cache module so it is available to tests."""
self._cache = get_cache("memcached://")
def testSimpleSetGet(self):
"""Tests that a simple set/get operation through the cache works."""
self._cache.set("test_key", "test_value")
self.assertEqual(self._cache.get("test_key"), "test_value")
def testDelete(self):
"""Tests that delete removes values from the cache."""
self._cache.set("test_key", "test_value")
self.assertEqual(self._cache.has_key("test_key"), True)
self._cache.delete("test_key")
self.assertEqual(self._cache.has_key("test_key"), False)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment