diff --git a/app.yaml.sample b/app.yaml.sample deleted file mode 100644 index 6b2f9d7e06610b8aefc1285b338f64f818913b3c..0000000000000000000000000000000000000000 --- a/app.yaml.sample +++ /dev/null @@ -1,41 +0,0 @@ -application: XXX-helios -version: 1 -runtime: python -api_version: 1 - -default_expiration: '1h' - -handlers: -- url: /remote_api - script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py - login: admin - -- url: /static/auth - static_dir: auth/media - secure: always - -- url: /static/helios - static_dir: helios/media - secure: always - -- url: /static - static_dir: server_ui/media - secure: always - -# this overrides the style for the booth -# without having to fork the code -- url: /booth/css - static_dir: server_ui/media/boothcss - secure: always - -- url: /booth - static_dir: heliosbooth - secure: always - -- url: /verifier - static_dir: heliosverifier - secure: always - -- url: /.* - script: django-gae.py - secure: always diff --git a/appengine_django/__init__.py b/appengine_django/__init__.py deleted file mode 100644 index 89fd4970571a6d14e69f69faf1f98460be587572..0000000000000000000000000000000000000000 --- a/appengine_django/__init__.py +++ /dev/null @@ -1,550 +0,0 @@ -#!/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. - -"""Support for integrating a Django project with the appengine infrastructure. - -This requires Django 1.0beta1 or greater. - -This module enables you to use the Django manage.py utility and *some* of it's -subcommands. View the help of manage.py for exact details. - -Additionally this module takes care of initialising the datastore (and a test -datastore) so that the Django test infrastructure can be used for your -appengine project. - -To use this module add the following two lines to your main.py and manage.py -scripts at the end of your imports: - from appengine_django import InstallAppengineHelperForDjango - InstallAppengineHelperForDjango() - -If you would like to use a version of Django other than that provided by the -system all you need to do is include it in a directory just above this helper, -eg: - appengine_django/__init__.py - This file - django/... - your private copy of Django. -""" - -import logging -import os -import re -import sys -import unittest -import zipfile - - -DIR_PATH = os.path.abspath(os.path.dirname(__file__)) -PARENT_DIR = os.path.dirname(DIR_PATH) -if PARENT_DIR.endswith(".zip"): - # Check for appengine_django itself being in a zipfile. - PARENT_DIR = os.path.dirname(PARENT_DIR) - -# Add this project to the start of sys path to enable direct imports. -sys.path = [PARENT_DIR,] + sys.path - -# Try to import the appengine code from the system path. -try: - from google.appengine.api import apiproxy_stub_map -except ImportError, e: - # Not on the system path. Build a list of alternative paths where it may be. - # First look within the project for a local copy, then look for where the Mac - # OS SDK installs it. - paths = [os.path.join(PARENT_DIR, '.google_appengine'), - os.path.join(PARENT_DIR, 'google_appengine'), - '/usr/local/google_appengine'] - # Then if on windows, look for where the Windows SDK installed it. - for path in os.environ.get('PATH', '').split(';'): - path = path.rstrip('\\') - if path.endswith('google_appengine'): - paths.append(path) - try: - from win32com.shell import shell - from win32com.shell import shellcon - id_list = shell.SHGetSpecialFolderLocation( - 0, shellcon.CSIDL_PROGRAM_FILES) - program_files = shell.SHGetPathFromIDList(id_list) - paths.append(os.path.join(program_files, 'Google', - 'google_appengine')) - except ImportError, e: - # Not windows. - pass - # Loop through all possible paths and look for the SDK dir. - SDK_PATH = None - for sdk_path in paths: - if os.path.exists(sdk_path): - SDK_PATH = os.path.realpath(sdk_path) - break - if SDK_PATH is None: - # The SDK could not be found in any known location. - sys.stderr.write("The Google App Engine SDK could not be found!\n") - sys.stderr.write("See README for installation instructions.\n") - sys.exit(1) - if SDK_PATH == os.path.join(PARENT_DIR, 'google_appengine'): - logging.warn('Loading the SDK from the \'google_appengine\' subdirectory ' - 'is now deprecated!') - logging.warn('Please move the SDK to a subdirectory named ' - '\'.google_appengine\' instead.') - logging.warn('See README for further details.') - # Add the SDK and the libraries within it to the system path. - EXTRA_PATHS = [ - SDK_PATH, - os.path.join(SDK_PATH, 'lib', 'antlr3'), - os.path.join(SDK_PATH, 'lib', 'django'), - os.path.join(SDK_PATH, 'lib', 'ipaddr'), - os.path.join(SDK_PATH, 'lib', 'webob'), - os.path.join(SDK_PATH, 'lib', 'yaml', 'lib'), - ] - # Add SDK paths at the start of sys.path, but after the local directory which - # was added to the start of sys.path on line 50 above. The local directory - # must come first to allow the local imports to override the SDK and - # site-packages directories. - sys.path = sys.path[0:1] + EXTRA_PATHS + sys.path[1:] - from google.appengine.api import apiproxy_stub_map - -# Try to import Django 1.0 through App Engine -try: - from google.appengine.dist import use_library - use_library('django', '1.0') -except ImportError: - pass - -# Look for a zipped copy of Django. -have_django_zip = False -django_zip_path = os.path.join(PARENT_DIR, 'django.zip') -if os.path.exists(django_zip_path): - have_django_zip = True - sys.path.insert(1, django_zip_path) - -# Remove the standard version of Django if a local copy has been provided. -if have_django_zip or os.path.exists(os.path.join(PARENT_DIR, 'django')): - for k in [k for k in sys.modules if k.startswith('django')]: - del sys.modules[k] - -# Must set this env var *before* importing any more of Django. -os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - -from django import VERSION -from django.conf import settings - -from google.appengine.api import yaml_errors - -# Flags made available this module -appid = None -have_appserver = False - -# Hide everything other than the flags above and the install function. -__all__ = ("appid", "have_appserver", "have_django_zip", - "django_zip_path", "InstallAppengineHelperForDjango") - - -INCOMPATIBLE_COMMANDS = ["adminindex", "createcachetable", "dbshell", - "inspectdb", "runfcgi", "syncdb", "validate"] - - -def LoadAppengineEnvironment(): - """Loads the appengine environment. - - Returns: - This function has no return value, but it sets the following parameters on - this package: - - appid: The name of the application. - - have_appserver: Boolean parameter which is True if the code is being run - from within the appserver environment. - """ - global appid, have_appserver - - # Detect if we are running under an appserver. - have_appserver = False - stub = apiproxy_stub_map.apiproxy.GetStub("datastore_v3") - if stub: - have_appserver = True - - # Load the application identifier. - if have_appserver: - appid = os.environ.get("APPLICATION_ID", "unknown") - else: - # Running as manage.py script, read from config file. - try: - from google.appengine.tools import dev_appserver - appconfig, unused_matcher = dev_appserver.LoadAppConfig(PARENT_DIR, {}) - appid = appconfig.application - except (ImportError, yaml_errors.EventListenerYAMLError), e: - logging.warn("Could not read the Application ID from app.yaml. " - "This may break things in unusual ways!") - # Something went wrong. - appid = "unknown" - - logging.debug("Loading application '%s' %s an appserver" % - (appid, have_appserver and "with" or "without")) - - -def InstallAppengineDatabaseBackend(): - """Installs the appengine database backend into Django. - - The appengine database lives in the db/ subdirectory of this package, but is - known as "appengine" to Django. This function installs the module where - Django expects to find its database backends. - """ - from appengine_django import db - sys.modules['django.db.backends.appengine'] = db - logging.debug("Installed appengine database backend") - - -def InstallGoogleMemcache(): - """Installs the Google memcache into Django. - - By default django tries to import standard memcache module. - Because appengine memcache is API compatible with Python memcache module, - we can trick Django to think it is installed and to use it. - - Now you can use CACHE_BACKEND = 'memcached://' in settings.py. IP address - and port number are not required. - """ - from google.appengine.api import memcache - sys.modules['memcache'] = memcache - logging.debug("Installed App Engine memcache backend") - - -def InstallDjangoModuleReplacements(): - """Replaces internal Django modules with App Engine compatible versions.""" - - # Replace the session module with a partial replacement overlay using - # __path__ so that portions not replaced will fall through to the original - # implementation. - try: - from django.contrib import sessions - orig_path = sessions.__path__[0] - sessions.__path__.insert(0, os.path.join(DIR_PATH, 'sessions')) - from django.contrib.sessions import backends - backends.__path__.append(os.path.join(orig_path, 'backends')) - except ImportError: - logging.debug("No Django session support available") - - # Replace incompatible dispatchers. - import django.core.signals - import django.db - import django.dispatch.dispatcher - - # Rollback occurs automatically on Google App Engine. Disable the Django - # rollback handler. - try: - # pre 1.0 - from django.dispatch import errors - CheckedException = errors.DispatcherKeyError - def _disconnectSignal(): - django.dispatch.dispatcher.disconnect( - django.db._rollback_on_exception, - django.core.signals.got_request_exception) - except ImportError: - CheckedException = KeyError - def _disconnectSignal(): - django.core.signals.got_request_exception.disconnect( - django.db._rollback_on_exception) - - try: - _disconnectSignal() - except CheckedException, e: - logging.debug("Django rollback handler appears to be already disabled.") - -def PatchDjangoSerializationModules(): - """Monkey patches the Django serialization modules. - - The standard Django serialization modules to not correctly handle the - datastore models provided by this package. This method installs replacements - for selected modules and methods to give Django the capability to correctly - serialize and deserialize datastore models. - """ - # These can't be imported until InstallAppengineDatabaseBackend has run. - from django.core.serializers import python - from appengine_django.serializer.python import Deserializer - if not hasattr(settings, "SERIALIZATION_MODULES"): - settings.SERIALIZATION_MODULES = {} - base_module = "appengine_django" - settings.SERIALIZATION_MODULES["xml"] = "%s.serializer.xml" % base_module - python.Deserializer = Deserializer - PatchDeserializedObjectClass() - DisableModelValidation() - logging.debug("Installed appengine json and python serialization modules") - - -def PatchDeserializedObjectClass(): - """Patches the DeserializedObject class. - - The default implementation calls save directly on the django Model base - class to avoid pre-save handlers. The model class provided by this package - is not derived from the Django Model class and therefore must be called - directly. - - Additionally we need to clear the internal _parent attribute as it may - contain a FakeParent class that is used to deserialize instances without - needing to load the parent instance itself. See the PythonDeserializer for - more details. - """ - # This can't be imported until InstallAppengineDatabaseBackend has run. - from django.core.serializers import base - class NewDeserializedObject(base.DeserializedObject): - def save(self, save_m2m=True): - self.object.save() - self.object._parent = None - base.DeserializedObject = NewDeserializedObject - logging.debug("Replacement DeserializedObject class installed") - -def DisableModelValidation(): - """Disables Django's model validation routines. - - The model validation is primarily concerned with validating foreign key - references. There is no equivalent checking code for datastore References at - this time. - - Validation needs to be disabled or serialization/deserialization will fail. - """ - from django.core.management import validation - validation.get_validation_errors = lambda x, y=0: 0 - logging.debug("Django SQL model validation disabled") - -def CleanupDjangoSettings(): - """Removes incompatible entries from the django settings module.""" - - # Ensure this module is installed as an application. - apps = getattr(settings, "INSTALLED_APPS", ()) - found = False - for app in apps: - if app.endswith("appengine_django"): - found = True - break - if not found: - logging.warn("appengine_django module is not listed as an application!") - apps += ("appengine_django",) - setattr(settings, "INSTALLED_APPS", apps) - logging.info("Added 'appengine_django' as an application") - - # Ensure the database backend is appropriately configured. - dbe = getattr(settings, "DATABASE_ENGINE", "") - if dbe != "appengine": - settings.DATABASE_ENGINE = "appengine" - logging.warn("DATABASE_ENGINE is not configured as 'appengine'. " - "Value overriden!") - for var in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]: - val = getattr(settings, "DATABASE_%s" % var, "") - if val: - setattr(settings, "DATABASE_%s" % var, "") - logging.warn("DATABASE_%s should be blank. Value overriden!") - - # Remove incompatible middleware modules. - mw_mods = list(getattr(settings, "MIDDLEWARE_CLASSES", ())) - disallowed_middleware_mods = ( - 'django.middleware.doc.XViewMiddleware',) - for modname in mw_mods[:]: - if modname in disallowed_middleware_mods: - # Currently only the CommonMiddleware has been ported. As other base - # modules are converted, remove from the disallowed_middleware_mods - # tuple. - mw_mods.remove(modname) - logging.warn("Middleware module '%s' is not compatible. Removed!" % - modname) - setattr(settings, "MIDDLEWARE_CLASSES", tuple(mw_mods)) - - # Remove incompatible application modules - app_mods = list(getattr(settings, "INSTALLED_APPS", ())) - disallowed_apps = ( - 'django.contrib.contenttypes', - 'django.contrib.sites',) - for app in app_mods[:]: - if app in disallowed_apps: - app_mods.remove(app) - logging.warn("Application module '%s' is not compatible. Removed!" % app) - setattr(settings, "INSTALLED_APPS", tuple(app_mods)) - - # Remove incompatible session backends. - session_backend = getattr(settings, "SESSION_ENGINE", "") - if session_backend.endswith("file"): - logging.warn("File session backend is not compatible. Overriden " - "to use db backend!") - setattr(settings, "SESSION_ENGINE", "django.contrib.sessions.backends.db") - - -def ModifyAvailableCommands(): - """Removes incompatible commands and installs replacements where possible.""" - if have_appserver: - # Commands are not used when running from an appserver. - return - from django.core import management - project_directory = os.path.join(__path__[0], "../") - if have_django_zip: - FindCommandsInZipfile.orig = management.find_commands - management.find_commands = FindCommandsInZipfile - management.get_commands() - # Replace startapp command which is set by previous call to get_commands(). - from appengine_django.management.commands.startapp import ProjectCommand - management._commands['startapp'] = ProjectCommand(project_directory) - RemoveCommands(management._commands) - logging.debug("Removed incompatible Django manage.py commands") - - -def FindCommandsInZipfile(management_dir): - """ - Given a path to a management directory, returns a list of all the command - names that are available. - - This implementation also works when Django is loaded from a zip. - - Returns an empty list if no commands are defined. - """ - zip_marker = ".zip%s" % os.sep - if zip_marker not in management_dir: - return FindCommandsInZipfile.orig(management_dir) - - # Django is sourced from a zipfile, ask zip module for a list of files. - filename, path = management_dir.split(zip_marker) - zipinfo = zipfile.ZipFile("%s.zip" % filename) - - # Add commands directory to management path. - path = os.path.join(path, "commands") - - # The zipfile module returns paths in the format of the operating system - # that created the zipfile! This may not match the path to the zipfile - # itself. Convert operating system specific characters to a standard - # character (#) to compare paths to work around this. - path_normalise = re.compile(r"[/\\]") - path = path_normalise.sub("#", path) - def _IsCmd(t): - """Returns true if t matches the criteria for a command module.""" - filename = os.path.basename(t) - t = path_normalise.sub("#", t) - if not t.startswith(path): - return False - if filename.startswith("_") or not t.endswith(".py"): - return False - return True - - return [os.path.basename(f)[:-3] for f in zipinfo.namelist() if _IsCmd(f)] - - -def RemoveCommands(command_dict): - """Removes incompatible commands from the specified command dictionary.""" - for cmd in command_dict.keys(): - if cmd.startswith("sql"): - del command_dict[cmd] - elif cmd in INCOMPATIBLE_COMMANDS: - del command_dict[cmd] - - -def InstallReplacementImpModule(): - """Install a replacement for the imp module removed by the appserver. - - This is only to find mangement modules provided by applications. - """ - if not have_appserver: - return - modname = 'appengine_django.replacement_imp' - imp_mod = __import__(modname, {}, [], ['']) - sys.modules['imp'] = imp_mod - logging.debug("Installed replacement imp module") - - -def InstallAppengineHelperForDjango(): - """Installs and Patches all of the classes/methods required for integration. - - If the variable DEBUG_APPENGINE_DJANGO is set in the environment verbose - logging of the actions taken will be enabled. - """ - # Adding this again here to solve a problem that happens when context - # switching from webapp.template to django.template. - # TODO(elsigh): Maybe there is a deeper, fixable problem somewhere? - os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - - if VERSION < (1, 0, None): - logging.error("Django 1.0 or greater is required!") - sys.exit(1) - - if os.getenv("DEBUG_APPENGINE_DJANGO"): - logging.getLogger().setLevel(logging.DEBUG) - else: - logging.getLogger().setLevel(logging.INFO) - logging.debug("Loading the Google App Engine Helper for Django...") - - # Force Django to reload its settings. - settings._target = None - - LoadAppengineEnvironment() - InstallReplacementImpModule() - InstallAppengineDatabaseBackend() - InstallModelForm() - InstallGoogleMemcache() - InstallDjangoModuleReplacements() - PatchDjangoSerializationModules() - CleanupDjangoSettings() - ModifyAvailableCommands() - InstallGoogleSMTPConnection() - InstallAuthentication() - - logging.debug("Successfully loaded the Google App Engine Helper for Django.") - - -def InstallGoogleSMTPConnection(): - from appengine_django import mail as gmail - from django.core import mail - logging.debug("Installing Google Email Adapter for Django") - mail.SMTPConnection = gmail.GoogleSMTPConnection - mail.mail_admins = gmail.mail_admins - mail.mail_managers = gmail.mail_managers - - -def InstallAuthentication(): - if "django.contrib.auth" not in settings.INSTALLED_APPS: - return - try: - from appengine_django.auth import models as helper_models - from django.contrib.auth import models - models.User = helper_models.User - models.Group = helper_models.Group - models.Permission = helper_models.Permission - models.Message = helper_models.Message - from django.contrib.auth import middleware as django_middleware - from appengine_django.auth.middleware import AuthenticationMiddleware - django_middleware.AuthenticationMiddleware = AuthenticationMiddleware - from django.contrib.auth import decorators as django_decorators - from appengine_django.auth.decorators import login_required - django_decorators.login_required = login_required - from django.contrib import auth as django_auth - from django.contrib.auth import tests as django_tests - django_auth.suite = unittest.TestSuite - django_tests.suite = unittest.TestSuite - logging.debug("Installing authentication framework") - except ImportError: - logging.debug("No Django authentication support available") - - -def InstallModelForm(): - """Replace Django ModelForm with the AppEngine ModelForm.""" - # This MUST happen as early as possible, but after any auth model patching. - from google.appengine.ext.db import djangoforms as aeforms - try: - # pre 1.0 - from django import newforms as forms - except ImportError: - from django import forms - - forms.ModelForm = aeforms.ModelForm - - # Extend ModelForm with support for EmailProperty - # TODO: This should be submitted to the main App Engine SDK. - from google.appengine.ext.db import EmailProperty - def get_form_field(self, **kwargs): - """Return a Django form field appropriate for an email property.""" - defaults = {'form_class': forms.EmailField} - defaults.update(kwargs) - return super(EmailProperty, self).get_form_field(**defaults) - EmailProperty.get_form_field = get_form_field diff --git a/appengine_django/auth/__init__.py b/appengine_django/auth/__init__.py deleted file mode 100644 index d2db207407af006d1609defe407a7efdb1c99e2f..0000000000000000000000000000000000000000 --- a/appengine_django/auth/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -""" -Authentication module that mimics the behavior of Django's authentication -implementation. - -Limitations: - - all user permissions methods are not available (requires contenttypes) -""" - -from django.template import add_to_builtins - -add_to_builtins('appengine_django.auth.templatetags') diff --git a/appengine_django/auth/decorators.py b/appengine_django/auth/decorators.py deleted file mode 100644 index d897c24713068fb70041c94d537f07fb1ce8a086..0000000000000000000000000000000000000000 --- a/appengine_django/auth/decorators.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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. - -"""Decorators for the authentication framework.""" - -from django.http import HttpResponseRedirect - -from google.appengine.api import users - - -def login_required(function): - """Implementation of Django's login_required decorator. - - The login redirect URL is always set to request.path - """ - def login_required_wrapper(request, *args, **kw): - if request.user.is_authenticated(): - return function(request, *args, **kw) - return HttpResponseRedirect(users.create_login_url(request.path)) - return login_required_wrapper diff --git a/appengine_django/auth/middleware.py b/appengine_django/auth/middleware.py deleted file mode 100644 index a727e4703dff68520e85874509c6c24b6336fd31..0000000000000000000000000000000000000000 --- a/appengine_django/auth/middleware.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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 django.contrib.auth.models import AnonymousUser - -from google.appengine.api import users - -from appengine_django.auth.models import User - - -class LazyUser(object): - def __get__(self, request, obj_type=None): - if not hasattr(request, '_cached_user'): - user = users.get_current_user() - if user: - request._cached_user = User.get_djangouser_for_user(user) - else: - request._cached_user = AnonymousUser() - return request._cached_user - - -class AuthenticationMiddleware(object): - def process_request(self, request): - request.__class__.user = LazyUser() - return None diff --git a/appengine_django/auth/models.py b/appengine_django/auth/models.py deleted file mode 100644 index d93e2404438f60269fe3385ca1a453e807b0d88d..0000000000000000000000000000000000000000 --- a/appengine_django/auth/models.py +++ /dev/null @@ -1,172 +0,0 @@ -# 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. - -""" -App Engine compatible models for the Django authentication framework. -""" - -from django.core import mail -from django.core.exceptions import ImproperlyConfigured -from django.db import models -from django.utils.encoding import smart_str -import urllib - -from django.db.models.manager import EmptyManager - -from google.appengine.api import users -from google.appengine.ext import db - -from appengine_django.models import BaseModel - - -class User(BaseModel): - """A model with the same attributes and methods as a Django user model. - - The model has two additions. The first addition is a 'user' attribute which - references a App Engine user. The second is the 'get_djangouser_for_user' - classmethod that should be used to retrieve a DjangoUser instance from a App - Engine user object. - """ - user = db.UserProperty(required=True) - username = db.StringProperty(required=True) - first_name = db.StringProperty() - last_name = db.StringProperty() - email = db.EmailProperty() - password = db.StringProperty() - is_staff = db.BooleanProperty(default=False, required=True) - is_active = db.BooleanProperty(default=True, required=True) - is_superuser = db.BooleanProperty(default=False, required=True) - last_login = db.DateTimeProperty(auto_now_add=True, required=True) - date_joined = db.DateTimeProperty(auto_now_add=True, required=True) - groups = EmptyManager() - user_permissions = EmptyManager() - - def __unicode__(self): - return self.username - - def __str__(self): - return unicode(self).encode('utf-8') - - @classmethod - def get_djangouser_for_user(cls, user): - query = cls.all().filter("user =", user) - if query.count() == 0: - django_user = cls(user=user, email=user.email(), username=user.nickname()) - django_user.save() - else: - django_user = query.get() - return django_user - - def set_password(self, raw_password): - raise NotImplementedError - - def check_password(self, raw_password): - raise NotImplementedError - - def set_unusable_password(self): - raise NotImplementedError - - def has_usable_password(self): - raise NotImplementedError - - def get_group_permissions(self): - return self.user_permissions - - def get_all_permissions(self): - return self.user_permissions - - def has_perm(self, perm): - return False - - def has_perms(self, perm_list): - return False - - def has_module_perms(self, module): - return False - - def get_and_delete_messages(self): - """Gets and deletes messages for this user""" - msgs = [] - for msg in self.message_set: - msgs.append(msg) - msg.delete() - return msgs - - def is_anonymous(self): - """Always return False""" - return False - - def is_authenticated(self): - """Always return True""" - return True - - def get_absolute_url(self): - return "/users/%s/" % urllib.quote(smart_str(self.username)) - - def get_full_name(self): - full_name = u'%s %s' % (self.first_name, self.last_name) - return full_name.strip() - - def email_user(self, subject, message, from_email): - """Sends an email to this user. - - According to the App Engine email API the from_email must be the - email address of a registered administrator for the application. - """ - mail.send_mail(subject, - message, - from_email, - [self.email]) - - def get_profile(self): - """ - Returns site-specific profile for this user. Raises - SiteProfileNotAvailable if this site does not allow profiles. - - When using the App Engine authentication framework, users are created - automatically. - """ - from django.contrib.auth.models import SiteProfileNotAvailable - if not hasattr(self, '_profile_cache'): - from django.conf import settings - if not hasattr(settings, "AUTH_PROFILE_MODULE"): - raise SiteProfileNotAvailable - try: - app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') - model = models.get_model(app_label, model_name) - self._profile_cache = model.all().filter("user =", self).get() - if not self._profile_cache: - raise model.DoesNotExist - except (ImportError, ImproperlyConfigured): - raise SiteProfileNotAvailable - return self._profile_cache - - -class Group(BaseModel): - """Group model not fully implemented yet.""" - # TODO: Implement this model, requires contenttypes - name = db.StringProperty() - permissions = EmptyManager() - - -class Message(BaseModel): - """User message model""" - user = db.ReferenceProperty(User) - message = db.TextProperty() - - -class Permission(BaseModel): - """Permission model not fully implemented yet.""" - # TODO: Implement this model, requires contenttypes - name = db.StringProperty() diff --git a/appengine_django/auth/templatetags.py b/appengine_django/auth/templatetags.py deleted file mode 100644 index 82378905bf2842f3a54ba771728d5e142114a3a3..0000000000000000000000000000000000000000 --- a/appengine_django/auth/templatetags.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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. - -""" -Template tags for the auth module. These are inserted into Django as "built-in" -tags so you do not need to use the load statement in your template to get -access to them. -""" - -from django.template import Library -from django.template import Node - -from google.appengine.api import users - - -class AuthLoginUrlsNode(Node): - """Template node that creates an App Engine login or logout URL. - - If create_login_url is True the App Engine's login URL is rendered into - the template, otherwise the logout URL. - """ - def __init__(self, create_login_url, redirect): - self.redirect = redirect - self.create_login_url = create_login_url - - def render(self, context): - if self.create_login_url: - return users.create_login_url(self.redirect) - else: - return users.create_logout_url(self.redirect) - - -def auth_login_urls(parser, token): - """Template tag registered as 'auth_login_url' and 'auth_logout_url' - when the module is imported. - - Both tags take an optional argument that specifies the redirect URL and - defaults to '/'. - """ - bits = list(token.split_contents()) - if len(bits) == 2: - redirect = bits[1] - else: - redirect = "/" - login = bits[0] == "auth_login_url" - return AuthLoginUrlsNode(login, redirect) - - -register = Library() -register.tag("auth_login_url", auth_login_urls) -register.tag("auth_logout_url", auth_login_urls) diff --git a/appengine_django/auth/tests.py b/appengine_django/auth/tests.py deleted file mode 100644 index 20aecfa4bb88c871b246a4ca3bcf832e5cafdc26..0000000000000000000000000000000000000000 --- a/appengine_django/auth/tests.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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. - -BASIC_TESTS = """ ->>> from google.appengine.api import users ->>> from models import User, AnonymousUser ->>> appengine_user = users.User("test@example.com") ->>> django_user = User.get_djangouser_for_user(appengine_user) ->>> django_user.email == appengine_user.email() -True ->>> django_user.username == appengine_user.nickname() -True ->>> django_user.user == appengine_user -True - ->>> django_user.username = 'test2' ->>> key = django_user.save() ->>> django_user.username == 'test2' -True - ->>> django_user2 = User.get_djangouser_for_user(appengine_user) ->>> django_user2 == django_user -True - ->>> django_user.is_authenticated() -True ->>> django_user.is_staff -False ->>> django_user.is_active -True - ->>> a = AnonymousUser() ->>> a.is_authenticated() -False ->>> a.is_staff -False ->>> a.is_active -False ->>> a.groups.all() -[] ->>> a.user_permissions.all() -[] - - -""" - -__test__ = {'BASIC_TESTS': BASIC_TESTS} diff --git a/appengine_django/conf/app_template/__init__.py b/appengine_django/conf/app_template/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/appengine_django/conf/app_template/models.py b/appengine_django/conf/app_template/models.py deleted file mode 100644 index 4d7c5d0ea42b6067c512dc55bc3172fda5304d01..0000000000000000000000000000000000000000 --- a/appengine_django/conf/app_template/models.py +++ /dev/null @@ -1,4 +0,0 @@ -from appengine_django.models import BaseModel -from google.appengine.ext import db - -# Create your models here. diff --git a/appengine_django/conf/app_template/views.py b/appengine_django/conf/app_template/views.py deleted file mode 100644 index 60f00ef0ef347811e7b0c0921b7fda097acd9fcc..0000000000000000000000000000000000000000 --- a/appengine_django/conf/app_template/views.py +++ /dev/null @@ -1 +0,0 @@ -# Create your views here. diff --git a/appengine_django/db/__init__.py b/appengine_django/db/__init__.py deleted file mode 100755 index 619bc789db6217d72668b1e8f2346c86d03b29db..0000000000000000000000000000000000000000 --- a/appengine_django/db/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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. - -# Explicitly set the name of this package to "appengine". -# -# The rationale for this is so that Django can refer to the database as -# "appengine" even though at a filesystem level it appears as the "db" package -# within the appengine_django package. -__name__ = "appengine" diff --git a/appengine_django/db/base.py b/appengine_django/db/base.py deleted file mode 100755 index 8a90182cf2f304d773a8af8dd2c197d0a1aa9b8d..0000000000000000000000000000000000000000 --- a/appengine_django/db/base.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/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 module looks after initialising the appengine api stubs.""" - -import logging -import os - -from appengine_django import appid -from appengine_django import have_appserver -from appengine_django.db.creation import DatabaseCreation - - -from django.db.backends import BaseDatabaseWrapper -from django.db.backends import BaseDatabaseFeatures -from django.db.backends import BaseDatabaseOperations - - -def get_datastore_paths(): - """Returns a tuple with the path to the datastore and history file. - - The datastore is stored in the same location as dev_appserver uses by - default, but the name is altered to be unique to this project so multiple - Django projects can be developed on the same machine in parallel. - - Returns: - (datastore_path, history_path) - """ - from google.appengine.tools import dev_appserver_main - datastore_path = dev_appserver_main.DEFAULT_ARGS['datastore_path'] - history_path = dev_appserver_main.DEFAULT_ARGS['history_path'] - datastore_path = datastore_path.replace("dev_appserver", "django_%s" % appid) - history_path = history_path.replace("dev_appserver", "django_%s" % appid) - return datastore_path, history_path - - -def get_test_datastore_paths(inmemory=True): - """Returns a tuple with the path to the test datastore and history file. - - If inmemory is true, (None, None) is returned to request an in-memory - datastore. If inmemory is false the path returned will be similar to the path - returned by get_datastore_paths but with a different name. - - Returns: - (datastore_path, history_path) - """ - if inmemory: - return None, None - datastore_path, history_path = get_datastore_paths() - datastore_path = datastore_path.replace("datastore", "testdatastore") - history_path = history_path.replace("datastore", "testdatastore") - return datastore_path, history_path - - -def destroy_datastore(datastore_path, history_path): - """Destroys the appengine datastore at the specified paths.""" - for path in [datastore_path, history_path]: - if not path: continue - try: - os.remove(path) - except OSError, e: - if e.errno != 2: - logging.error("Failed to clear datastore: %s" % e) - - -class DatabaseError(Exception): - """Stub class for database errors. Required by Django""" - pass - - -class IntegrityError(Exception): - """Stub class for database integrity errors. Required by Django""" - pass - - -class DatabaseFeatures(BaseDatabaseFeatures): - """Stub class to provide the feaures member expected by Django""" - pass - - -class DatabaseOperations(BaseDatabaseOperations): - """Stub class to provide the options member expected by Django""" - pass - - -class DatabaseWrapper(BaseDatabaseWrapper): - """App Engine database definition for Django. - - This "database" backend does not support any of the standard backend - operations. The only task that it performs is to setup the api stubs required - by the appengine libraries if they have not already been initialised by an - appserver. - """ - - def __init__(self, *args, **kwargs): - super(DatabaseWrapper, self).__init__(*args, **kwargs) - self.features = DatabaseFeatures() - self.ops = DatabaseOperations() - self.creation = DatabaseCreation(self) - self.use_test_datastore = kwargs.get("use_test_datastore", False) - self.test_datastore_inmemory = kwargs.get("test_datastore_inmemory", True) - if have_appserver: - return - self._setup_stubs() - - def _get_paths(self): - if self.use_test_datastore: - return get_test_datastore_paths(self.test_datastore_inmemory) - else: - return get_datastore_paths() - - def _setup_stubs(self): - # If this code is being run without an appserver (eg. via a django - # commandline flag) then setup a default stub environment. - from google.appengine.tools import dev_appserver_main - args = dev_appserver_main.DEFAULT_ARGS.copy() - args['datastore_path'], args['history_path'] = self._get_paths() - from google.appengine.tools import dev_appserver - dev_appserver.SetupStubs(appid, **args) - if self.use_test_datastore: - logging.debug("Configured API stubs for the test datastore") - else: - logging.debug("Configured API stubs for the development datastore") - - def flush(self): - """Helper function to remove the current datastore and re-open the stubs""" - destroy_datastore(*self._get_paths()) - self._setup_stubs() - - def close(self): - pass - - def _commit(self): - pass - - def cursor(self, *args): - pass diff --git a/appengine_django/db/creation.py b/appengine_django/db/creation.py deleted file mode 100755 index 0e0e27775a4efcd9ddc49f969d60af8cac40f68a..0000000000000000000000000000000000000000 --- a/appengine_django/db/creation.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/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 logging - -from django.conf import settings -from django.db.backends.creation import BaseDatabaseCreation - - -class DatabaseCreation(BaseDatabaseCreation): - - def create_test_db(self, *args, **kw): - """Destroys the test datastore. A new store will be recreated on demand""" - settings.DATABASE_SUPPORTS_TRANSACTIONS = False - self.destroy_test_db() - self.connection.use_test_datastore = True - self.connection.flush() - - - def destroy_test_db(self, *args, **kw): - """Destroys the test datastore files.""" - from appengine_django.db.base import destroy_datastore - from appengine_django.db.base import get_test_datastore_paths - destroy_datastore(*get_test_datastore_paths()) - logging.debug("Destroyed test datastore") diff --git a/appengine_django/mail.py b/appengine_django/mail.py deleted file mode 100644 index bf3e2ddf99540fd77e8b1da36e8284d953af6e4e..0000000000000000000000000000000000000000 --- a/appengine_django/mail.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/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 module replaces the Django mail implementation with a version that sends -email via the mail API provided by Google App Engine. - -Multipart / HTML email is not yet supported. -""" - -import logging - -from django.core import mail -from django.core.mail import SMTPConnection -from django.conf import settings - -from google.appengine.api import mail as gmail - - -class GoogleSMTPConnection(SMTPConnection): - def __init__(self, host=None, port=None, username=None, password=None, - use_tls=None, fail_silently=False): - self.use_tls = (use_tls is not None) and use_tls or settings.EMAIL_USE_TLS - self.fail_silently = fail_silently - self.connection = None - - def open(self): - self.connection = True - - def close(self): - pass - - def _send(self, email_message): - """A helper method that does the actual sending.""" - if not email_message.to: - return False - try: - if (isinstance(email_message,gmail.EmailMessage)): - e = message - elif (isinstance(email_message,mail.EmailMessage)): - e = gmail.EmailMessage(sender=email_message.from_email, - to=email_message.to, - subject=email_message.subject, - body=email_message.body) - if email_message.extra_headers.get('Reply-To', None): - e.reply_to = email_message.extra_headers['Reply-To'] - if email_message.bcc: - e.bcc = list(email_message.bcc) - #TODO - add support for html messages and attachments... - e.send() - except: - if not self.fail_silently: - raise - return False - return True - - -def mail_admins(subject, message, fail_silently=False): - """Sends a message to the admins, as defined by the ADMINS setting.""" - _mail_group(settings.ADMINS, subject, message, fail_silently) - - -def mail_managers(subject, message, fail_silently=False): - """Sends a message to the managers, as defined by the MANAGERS setting.""" - _mail_group(settings.MANAGERS, subject, message, fail_silently) - - -def _mail_group(group, subject, message, fail_silently=False): - """Sends a message to an administrative group.""" - if group: - mail.send_mail(settings.EMAIL_SUBJECT_PREFIX + subject, message, - settings.SERVER_EMAIL, [a[1] for a in group], - fail_silently) - return - # If the group had no recipients defined, default to the App Engine admins. - try: - gmail.send_mail_to_admins(settings.SERVER_EMAIL, - settings.EMAIL_SUBJECT_PREFIX + subject, - message) - except: - if not fail_silently: - raise diff --git a/appengine_django/management/__init__.py b/appengine_django/management/__init__.py deleted file mode 100755 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/appengine_django/management/commands/__init__.py b/appengine_django/management/commands/__init__.py deleted file mode 100755 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/appengine_django/management/commands/console.py b/appengine_django/management/commands/console.py deleted file mode 100755 index 2c4069706e8cdd45670230bcb922676b1d950c63..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/console.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/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 code -import getpass -import os -import sys - -from django.conf import settings -from django.core.management.base import BaseCommand - -from google.appengine.ext.remote_api import remote_api_stub - - -def auth_func(): - return raw_input('Username:'), getpass.getpass('Password:') - -class Command(BaseCommand): - """ Start up an interactive console backed by your app using remote_api """ - - help = 'Start up an interactive console backed by your app using remote_api.' - - def run_from_argv(self, argv): - app_id = argv[2] - if len(argv) > 3: - host = argv[3] - else: - host = '%s.appspot.com' % app_id - - remote_api_stub.ConfigureRemoteDatastore(app_id, - '/remote_api', - auth_func, - host) - - code.interact('App Engine interactive console for %s' % (app_id,), - None, - locals()) diff --git a/appengine_django/management/commands/flush.py b/appengine_django/management/commands/flush.py deleted file mode 100755 index c5f3f8c23036c778f6aae206a6a4f06c2532680a..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/flush.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/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 logging -import os -import sys - -from django.core.management.base import BaseCommand - - -class Command(BaseCommand): - """Overrides the default Django flush command. - """ - help = 'Clears the current datastore and loads the initial fixture data.' - - def run_from_argv(self, argv): - from django.db import connection - connection.flush() - from django.core.management import call_command - call_command('loaddata', 'initial_data') - - def handle(self, *args, **kwargs): - self.run_from_argv(None) diff --git a/appengine_django/management/commands/reset.py b/appengine_django/management/commands/reset.py deleted file mode 100755 index 126f38634086a0de7e3a838d1ba832df0618c105..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/reset.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/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 logging -import os -import sys - -from django.core.management.base import BaseCommand - - -class Command(BaseCommand): - """Overrides the default Django reset command. - """ - help = 'Clears the current datastore.' - - def run_from_argv(self, argv): - from django.db import connection - connection.flush() diff --git a/appengine_django/management/commands/rollback.py b/appengine_django/management/commands/rollback.py deleted file mode 100755 index 6ce9e4eaca1d64a5c0e087bcf019381ad37689c8..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/rollback.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/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() diff --git a/appengine_django/management/commands/runserver.py b/appengine_django/management/commands/runserver.py deleted file mode 100755 index 07d080d1f9e2a3668701e0788c5f029b1fa45869..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/runserver.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/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() diff --git a/appengine_django/management/commands/startapp.py b/appengine_django/management/commands/startapp.py deleted file mode 100644 index 2648cbddfc5f8669165b3d937a294579b70f6b04..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/startapp.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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) - diff --git a/appengine_django/management/commands/testserver.py b/appengine_django/management/commands/testserver.py deleted file mode 100755 index 5a50a7084122a31c2679ec8701967b382be6dabd..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/testserver.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/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) diff --git a/appengine_django/management/commands/update.py b/appengine_django/management/commands/update.py deleted file mode 100755 index e489d5d09607b10ca16a2a393aa065a89e55d5ea..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/update.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/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() diff --git a/appengine_django/management/commands/vacuum_indexes.py b/appengine_django/management/commands/vacuum_indexes.py deleted file mode 100755 index ab276b414b7d7fb318d9e16114c9c109049655b2..0000000000000000000000000000000000000000 --- a/appengine_django/management/commands/vacuum_indexes.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/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() diff --git a/appengine_django/models.py b/appengine_django/models.py deleted file mode 100755 index 0b9f6dcb546e6de40176e503861b400b58f41e83..0000000000000000000000000000000000000000 --- a/appengine_django/models.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/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 diff --git a/appengine_django/replacement_imp.py b/appengine_django/replacement_imp.py deleted file mode 100644 index 330aaf01dc666d4071b7566b0e0cefee3afba721..0000000000000000000000000000000000000000 --- a/appengine_django/replacement_imp.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 diff --git a/appengine_django/serializer/__init__.py b/appengine_django/serializer/__init__.py deleted file mode 100755 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/appengine_django/serializer/python.py b/appengine_django/serializer/python.py deleted file mode 100755 index bce16e70e00be1fd64ea6aee224b9e9b279167a7..0000000000000000000000000000000000000000 --- a/appengine_django/serializer/python.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/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) diff --git a/appengine_django/serializer/xml.py b/appengine_django/serializer/xml.py deleted file mode 100755 index f67588a527fdd636f4adeda9a83f2abe40f89948..0000000000000000000000000000000000000000 --- a/appengine_django/serializer/xml.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/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) diff --git a/appengine_django/sessions/__init__.py b/appengine_django/sessions/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/appengine_django/sessions/backends/__init__.py b/appengine_django/sessions/backends/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/appengine_django/sessions/backends/db.py b/appengine_django/sessions/backends/db.py deleted file mode 100644 index e2e3aa40f43bfd13827842e510e304117225afb6..0000000000000000000000000000000000000000 --- a/appengine_django/sessions/backends/db.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/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 diff --git a/appengine_django/sessions/models.py b/appengine_django/sessions/models.py deleted file mode 100644 index 16816448c99d3631c066c9b18be232e79802dea2..0000000000000000000000000000000000000000 --- a/appengine_django/sessions/models.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/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() diff --git a/appengine_django/tests/__init__.py b/appengine_django/tests/__init__.py deleted file mode 100644 index b511f5858a19aec6068a463be1a553c9c49eb813..0000000000000000000000000000000000000000 --- a/appengine_django/tests/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/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 diff --git a/appengine_django/tests/commands_test.py b/appengine_django/tests/commands_test.py deleted file mode 100755 index a02ddbfc0ae28b51e9c33e145e7c3f6907adee84..0000000000000000000000000000000000000000 --- a/appengine_django/tests/commands_test.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/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) diff --git a/appengine_django/tests/core_test.py b/appengine_django/tests/core_test.py deleted file mode 100755 index e2f51c839315ef666ecfcc956ad49513cca6c700..0000000000000000000000000000000000000000 --- a/appengine_django/tests/core_test.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/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) diff --git a/appengine_django/tests/db_test.py b/appengine_django/tests/db_test.py deleted file mode 100755 index 452e8f93411266528007339d19107fc41d8afb07..0000000000000000000000000000000000000000 --- a/appengine_django/tests/db_test.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/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")) diff --git a/appengine_django/tests/memcache_test.py b/appengine_django/tests/memcache_test.py deleted file mode 100644 index 4e5f02e28ce9eaf24fe48bae819d304807ba718e..0000000000000000000000000000000000000000 --- a/appengine_django/tests/memcache_test.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/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) diff --git a/appengine_django/tests/model_test.py b/appengine_django/tests/model_test.py deleted file mode 100755 index 8611d8b31112b4c80cc9ec70ca4248d7b61d40ad..0000000000000000000000000000000000000000 --- a/appengine_django/tests/model_test.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/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 combined appengine and Django models function correctly.""" - - -import unittest - -from django import VERSION -from django.db.models import get_models -from django import forms - -from google.appengine.ext.db import djangoforms -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 TestModelWithProperties(BaseModel): - """Test model class for checking property -> Django field setup.""" - property1 = db.StringProperty() - property2 = db.IntegerProperty() - property3 = db.Reference() - - -class ModelTest(unittest.TestCase): - """Unit tests for the combined model class.""" - - def testModelRegisteredWithDjango(self): - """Tests that a combined model class has been registered with Django.""" - self.assert_(RegistrationTestModel in get_models()) - - def testDatastoreModelProperties(self): - """Tests that a combined model class still has datastore properties.""" - self.assertEqual(3, len(TestModelWithProperties.properties())) - - def testDjangoModelClass(self): - """Tests the parts of a model required by Django are correctly stubbed.""" - # Django requires model options to be found at ._meta. - self.assert_(isinstance(RegistrationTestModel._meta, ModelOptions)) - # Django requires a manager at .objects - self.assert_(isinstance(RegistrationTestModel.objects, ModelManager)) - # Django requires ._default_manager. - self.assert_(hasattr(RegistrationTestModel, "_default_manager")) - - def testDjangoModelFields(self): - """Tests that a combined model class has (faked) Django fields.""" - fields = TestModelWithProperties._meta.local_fields - self.assertEqual(3, len(fields)) - # Check each fake field has the minimal properties that Django needs. - for field in fields: - # The Django serialization code looks for rel to determine if the field - # is a relationship/reference to another model. - self.assert_(hasattr(field, "rel")) - # serialize is required to tell Django to serialize the field. - self.assertEqual(True, field.serialize) - if field.name == "property3": - # Extra checks for the Reference field. - # rel.field_name is used during serialization to find the field in the - # other model that this field is related to. This should always be - # 'key_name' for appengine models. - self.assertEqual("key_name", field.rel.field_name) - - def testDjangoModelOptionsStub(self): - """Tests that the options stub has the required properties by Django.""" - # Django requires object_name and app_label for serialization output. - self.assertEqual("RegistrationTestModel", - RegistrationTestModel._meta.object_name) - self.assertEqual("appengine_django", RegistrationTestModel._meta.app_label) - # The pk.name member is required during serialization for dealing with - # related fields. - self.assertEqual("key_name", RegistrationTestModel._meta.pk.name) - # The many_to_many method is called by Django in the serialization code to - # find m2m relationships. m2m is not supported by the datastore. - self.assertEqual([], RegistrationTestModel._meta.many_to_many) - - def testDjangoModelManagerStub(self): - """Tests that the manager stub acts as Django would expect.""" - # The serialization code calls model.objects.all() to retrieve all objects - # to serialize. - self.assertEqual([], list(RegistrationTestModel.objects.all())) - - def testDjangoModelPK(self): - """Tests that each model instance has a 'primary key' generated.""" - obj = RegistrationTestModel(key_name="test") - obj.put() - pk = obj._get_pk_val() - self.assert_(pk) - new_obj = RegistrationTestModel.get(pk) - self.assertEqual(obj.key(), new_obj.key()) - - def testModelFormPatched(self): - """Tests that the Django ModelForm is being successfully patched.""" - self.assertEqual(djangoforms.ModelForm, forms.ModelForm) diff --git a/appengine_django/tests/serialization_test.py b/appengine_django/tests/serialization_test.py deleted file mode 100755 index 39b92ea195491d8f77e708b8d4d81be9c4e1e35b..0000000000000000000000000000000000000000 --- a/appengine_django/tests/serialization_test.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/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 serialization modules are functioning correctly. - -In particular, these tests verify that the modifications made to the standard -Django serialization modules function correctly and that the combined datastore -and Django models can be dumped and loaded to all of the provided formats. -""" - - -import os -import re -import unittest -from StringIO import StringIO - -from django.core import serializers - -from google.appengine.ext import db -from appengine_django.models import BaseModel - - -class ModelA(BaseModel): - description = db.StringProperty() - - -class ModelB(BaseModel): - description = db.StringProperty() - friend = db.Reference(ModelA) - - -class TestAllFormats(type): - - def __new__(cls, name, bases, attrs): - """Extends base test functions to be called for every serialisation format. - - Looks for functions matching 'run.*Test', where the wildcard in the middle - matches the desired test name and ensures that a test case is setup to call - that function once for every defined serialisation format. The test case - that is created will be called 'test<format><name>'. Eg, for the function - 'runKeyedObjectTest' functions like 'testJsonKeyedObject' will be created. - """ - test_formats = serializers.get_serializer_formats() - test_formats.remove("python") # Python serializer is only used indirectly. - - for func_name in attrs.keys(): - m = re.match("^run(.*)Test$", func_name) - if not m: - continue - for format in test_formats: - test_name = "test%s%s" % (format.title(), m.group(1)) - test_func = eval("lambda self: getattr(self, \"%s\")(\"%s\")" % - (func_name, format)) - attrs[test_name] = test_func - - return super(TestAllFormats, cls).__new__(cls, name, bases, attrs) - - -class SerializationTest(unittest.TestCase): - """Unit tests for the serialization/deserialization functionality. - - Tests that every loaded serialization format can successfully dump and then - reload objects without the objects changing. - """ - __metaclass__ = TestAllFormats - - def compareObjects(self, orig, new, format="unknown"): - """Compares two objects to ensure they are identical. - - Args: - orig: The original object, must be an instance of db.Model. - new: The new object, must be an instance of db.Model. - format: The serialization format being tested, used to make error output - more helpful. - - Raises: - The function has no return value, but will raise assertion errors if the - objects do not match correctly. - """ - if orig.key().name(): - # Only compare object keys when the key is named. Key IDs are not static - # and will change between dump/load. If you want stable Keys they need to - # be named! - self.assertEqual(orig.key(), new.key(), - "keys not equal after %s serialization: %s != %s" % - (format, repr(orig.key()), repr(new.key()))) - - for key in orig.properties().keys(): - oval = getattr(orig, key) - nval = getattr(new, key) - if isinstance(orig.properties()[key], db.Reference): - # Need to compare object keys not the objects themselves. - oval = oval.key() - nval = nval.key() - self.assertEqual(oval, nval, "%s attribute differs after %s " - "serialization: %s != %s" % (key, format, oval, nval)) - - def doSerialisationTest(self, format, obj, rel_attr=None, obj_ref=None): - """Runs a serialization test on an object for the specified format. - - Args: - format: The name of the Django serialization class to use. - obj: The object to {,de}serialize, must be an instance of db.Model. - rel_attr: Name of the attribute of obj references another model. - obj_ref: The expected object reference, must be an instance of db.Model. - - Raises: - The function has no return value but raises assertion errors if the - object cannot be successfully serialized and then deserialized back to an - identical object. If rel_attr and obj_ref are specified the deserialized - object must also retain the references from the original object. - """ - serialised = serializers.serialize(format, [obj]) - # Try and get the object back from the serialized string. - result = list(serializers.deserialize(format, StringIO(serialised))) - self.assertEqual(1, len(result), - "%s serialization should create 1 object" % format) - result[0].save() # Must save back into the database to get a Key. - self.compareObjects(obj, result[0].object, format) - if rel_attr and obj_ref: - rel = getattr(result[0].object, rel_attr) - if callable(rel): - rel = rel() - self.compareObjects(rel, obj_ref, format) - - def doLookupDeserialisationReferenceTest(self, lookup_dict, format): - """Tests the Key reference is loaded OK for a format. - - Args: - lookup_dict: A dictionary indexed by format containing serialized strings - of the objects to load. - format: The format to extract from the dict and deserialize. - - Raises: - This function has no return value but raises assertion errors if the - string cannot be deserialized correctly or the resulting object does not - reference the object correctly. - """ - if format not in lookup_dict: - # Check not valid for this format. - return - obj = ModelA(description="test object", key_name="test") - obj.put() - s = lookup_dict[format] - result = list(serializers.deserialize(format, StringIO(s))) - self.assertEqual(1, len(result), "expected 1 object from %s" % format) - result[0].save() - self.compareObjects(obj, result[0].object.friend, format) - - def doModelKeyDeserialisationReferenceTest(self, lookup_dict, format): - """Tests a model with a key can be loaded OK for a format. - - Args: - lookup_dict: A dictionary indexed by format containing serialized strings - of the objects to load. - format: The format to extract from the dict and deserialize. - - Returns: - This function has no return value but raises assertion errors if the - string cannot be deserialized correctly or the resulting object is not an - instance of ModelA with a key named 'test'. - """ - if format not in lookup_dict: - # Check not valid for this format. - return - s = lookup_dict[format] - result = list(serializers.deserialize(format, StringIO(s))) - self.assertEqual(1, len(result), "expected 1 object from %s" % format) - result[0].save() - self.assert_(isinstance(result[0].object, ModelA)) - self.assertEqual("test", result[0].object.key().name()) - - # Lookup dicts for the above (doLookupDeserialisationReferenceTest) function. - SERIALIZED_WITH_KEY_AS_LIST = { - "json": """[{"pk": "agR0ZXN0chMLEgZNb2RlbEIiB21vZGVsYmkM", """ - """"model": "tests.modelb", "fields": {"description": "test", """ - """"friend": ["ModelA", "test"] }}]""", - "yaml": """- fields: {description: !!python/unicode 'test', friend: """ - """ [ModelA, test]}\n model: tests.modelb\n pk: """ - """ agR0ZXN0chMLEgZNb2RlbEEiB21vZGVsYWkM\n""" - } - SERIALIZED_WITH_KEY_REPR = { - "json": """[{"pk": "agR0ZXN0chMLEgZNb2RlbEIiB21vZGVsYmkM", """ - """"model": "tests.modelb", "fields": {"description": "test", """ - """"friend": "datastore_types.Key.from_path(""" - """'ModelA', 'test')" }}]""", - "yaml": """- fields: {description: !!python/unicode 'test', friend: """ - """\'datastore_types.Key.from_path("ModelA", "test")\'}\n """ - """model: tests.modelb\n pk: """ - """ agR0ZXN0chMLEgZNb2RlbEEiB21vZGVsYWkM\n""" - } - - # Lookup dict for the doModelKeyDeserialisationReferenceTest function. - MK_SERIALIZED_WITH_LIST = { - "json": """[{"pk": ["ModelA", "test"], "model": "tests.modela", """ - """"fields": {}}]""", - "yaml": """-\n fields: {description: null}\n model: tests.modela\n """ - """pk: [ModelA, test]\n""" - } - MK_SERIALIZED_WITH_KEY_REPR = { - "json": """[{"pk": "datastore_types.Key.from_path('ModelA', 'test')", """ - """"model": "tests.modela", "fields": {}}]""", - "yaml": """-\n fields: {description: null}\n model: tests.modela\n """ - """pk: \'datastore_types.Key.from_path("ModelA", "test")\'\n""" - } - MK_SERIALIZED_WITH_KEY_AS_TEXT = { - "json": """[{"pk": "test", "model": "tests.modela", "fields": {}}]""", - "yaml": """-\n fields: {description: null}\n model: tests.modela\n """ - """pk: test\n""" - } - - # Lookup dict for the function. - SERIALIZED_WITH_NON_EXISTANT_PARENT = { - "json": """[{"pk": "ahhnb29nbGUtYXBwLWVuZ2luZS1kamFuZ29yIgsSBk1vZG""" - """VsQiIGcGFyZW50DAsSBk1vZGVsQSIEdGVzdAw", """ - """"model": "tests.modela", "fields": """ - """{"description": null}}]""", - "yaml": """- fields: {description: null}\n """ - """model: tests.modela\n """ - """pk: ahhnb29nbGUtYXBwLWVuZ2luZS1kamFuZ29yIgsSBk1""" - """vZGVsQiIGcGFyZW50DAsSBk1vZGVsQSIEdGVzdAw\n""", - "xml": """<?xml version="1.0" encoding="utf-8"?>\n""" - """<django-objects version="1.0">\n""" - """<entity kind="tests.modela" key="ahhnb29nbGUtYXBwL""" - """WVuZ2luZS1kamFuZ29yIgsSBk1vZGVsQiIGcGFyZW50DA""" - """sSBk1vZGVsQSIEdGVzdAw">\n """ - """<key>tag:google-app-engine-django.gmail.com,""" - """2008-05-13:ModelA[ahhnb29nbGUtYXBwLWVuZ2luZS1kam""" - """FuZ29yIgsSBk1vZGVsQiIGcGFyZW50DAsSBk1vZGVsQSIEdGVzdAw""" - """]</key>\n <property name="description" """ - """type="null"></property>\n</entity>\n</django-objects>""" - } - - # The following functions are all expanded by the metaclass to be run once - # for every registered Django serialization module. - - def runKeyedObjectTest(self, format): - """Test serialization of a basic object with a named key.""" - obj = ModelA(description="test object", key_name="test") - obj.put() - self.doSerialisationTest(format, obj) - - def runObjectWithIdTest(self, format): - """Test serialization of a basic object with a numeric ID key.""" - obj = ModelA(description="test object") - obj.put() - self.doSerialisationTest(format, obj) - - def runObjectWithReferenceTest(self, format): - """Test serialization of an object that references another object.""" - obj = ModelA(description="test object", key_name="test") - obj.put() - obj2 = ModelB(description="friend object", friend=obj) - obj2.put() - self.doSerialisationTest(format, obj2, "friend", obj) - - def runObjectWithParentTest(self, format): - """Test serialization of an object that has a parent object reference.""" - obj = ModelA(description="parent object", key_name="parent") - obj.put() - obj2 = ModelA(description="child object", key_name="child", parent=obj) - obj2.put() - self.doSerialisationTest(format, obj2, "parent", obj) - - def runObjectWithNonExistantParentTest(self, format): - """Test deserialization of an object referencing a non-existant parent.""" - self.doModelKeyDeserialisationReferenceTest( - self.SERIALIZED_WITH_NON_EXISTANT_PARENT, format) - - def runCreateKeyReferenceFromListTest(self, format): - """Tests that a reference specified as a list in json/yaml can be loaded OK.""" - self.doLookupDeserialisationReferenceTest(self.SERIALIZED_WITH_KEY_AS_LIST, - format) - - def runCreateKeyReferenceFromReprTest(self, format): - """Tests that a reference specified as repr(Key) in can loaded OK.""" - self.doLookupDeserialisationReferenceTest(self.SERIALIZED_WITH_KEY_REPR, - format) - - def runCreateModelKeyFromListTest(self, format): - """Tests that a model key specified as a list can be loaded OK.""" - self.doModelKeyDeserialisationReferenceTest(self.MK_SERIALIZED_WITH_LIST, - format) - - def runCreateModelKeyFromReprTest(self, format): - """Tests that a model key specified as a repr(Key) can be loaded OK.""" - self.doModelKeyDeserialisationReferenceTest( - self.MK_SERIALIZED_WITH_KEY_REPR, format) - - def runCreateModelKeyFromTextTest(self, format): - """Tests that a reference specified as a plain key_name loads OK.""" - self.doModelKeyDeserialisationReferenceTest( - self.MK_SERIALIZED_WITH_KEY_AS_TEXT, format) - - -if __name__ == '__main__': - unittest.main() diff --git a/auth b/auth index ad0f994edd52be83fe3b49d3c67c55e851c90423..ff9972a362b1ff398cedda94bceaa249724029b8 160000 --- a/auth +++ b/auth @@ -1 +1 @@ -Subproject commit ad0f994edd52be83fe3b49d3c67c55e851c90423 +Subproject commit ff9972a362b1ff398cedda94bceaa249724029b8 diff --git a/helios b/helios index b69b30f5e9f6659f21302f74c22e15693fb10eb3..c8e117fb0570cbff3cb94f370dcd08b9a5a269c1 160000 --- a/helios +++ b/helios @@ -1 +1 @@ -Subproject commit b69b30f5e9f6659f21302f74c22e15693fb10eb3 +Subproject commit c8e117fb0570cbff3cb94f370dcd08b9a5a269c1 diff --git a/index.yaml b/index.yaml deleted file mode 100644 index a4b9bba938ef956ece448e6a93a99847d9fd9011..0000000000000000000000000000000000000000 --- a/index.yaml +++ /dev/null @@ -1,64 +0,0 @@ -indexes: - -- kind: django_admin_log - properties: - - name: content_type - - name: object_id - - name: action_time - -- kind: django_admin_log - properties: - - name: user - - name: action_time - direction: desc - -- kind: Voter - properties: - - name: election - - name: uuid - -- kind: Voter - properties: - - name: election - - name: voter_id - -- kind: CastVote - properties: - - name: voter - - name: cast_at - direction: desc - -- kind: Voter - properties: - - name: election - - name: vote_hash - -- kind: Voter - properties: - - name: election - - name: alias - -- kind: Voter - properties: - - name: election - - name: cast_at - -- kind: AuditedBallot - properties: - - name: election - - name: vote_hash - -- kind: Election - properties: - - name: featured_p - - name: short_name - -# AUTOGENERATED - -# This index.yaml is automatically updated whenever the dev_appserver -# detects that a new type of query is run. If you want to manage the -# index.yaml file manually, remove the above marker line (the line -# saying "# AUTOGENERATED"). If you want to manage some indexes -# manually, move them above the marker line. The index.yaml file is -# automatically uploaded to the admin console when you next deploy -# your application using appcfg.py. diff --git a/manage.py b/manage.py index 4d600bcd2c3a72fcb92aeae13ccccc6e3c27558e..5e78ea979ea3846a4602f604e265fc4666beffac 100644 --- a/manage.py +++ b/manage.py @@ -1,26 +1,4 @@ #!/usr/bin/env python -# 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 appengine_django import InstallAppengineHelperForDjango -InstallAppengineHelperForDjango() - -# install auto-retrying for database -import autoretry -autoretry.autoretry_datastore_timeouts(attempts=5.0, interval=0.2, exponent=2.0) - from django.core.management import execute_manager try: import settings # Assumed to be in the same directory. @@ -29,6 +7,5 @@ except ImportError: sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) sys.exit(1) - if __name__ == "__main__": execute_manager(settings) diff --git a/remote_console.py b/remote_console.py deleted file mode 100644 index 10683ef75b4a4681054a033a7dfdf5c64183e26a..0000000000000000000000000000000000000000 --- a/remote_console.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python -import code -import getpass -import sys - -path = "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine" -sys.path.append(path) -sys.path.append("%s/lib/yaml/lib" % path) -sys.path.append("%s/lib/django" % path) -sys.path.append("%s/lib/antlr3" % path) -sys.path.append("%s/lib/ipaddr" % path) - -from appengine_django import InstallAppengineHelperForDjango -InstallAppengineHelperForDjango() - -from google.appengine.ext.remote_api import remote_api_stub -from google.appengine.ext import db - -def auth_func(): - return raw_input('Username:'), getpass.getpass('Password:') - -if len(sys.argv) < 2: - print "Usage: %s app_id [host]" % (sys.argv[0],) -app_id = sys.argv[1] -if len(sys.argv) > 2: - host = sys.argv[2] -else: - host = '%s.appspot.com' % app_id - -remote_api_stub.ConfigureRemoteDatastore(app_id, '/remote_api', auth_func, host) - -code.interact('App Engine interactive console for %s' % (app_id,), None, locals())