diff -r 000000000000 -r b97547f5f1fa goa/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/goa/__init__.py Wed Nov 05 15:52:50 2008 +0100 @@ -0,0 +1,202 @@ +"""cubicweb on google appengine + +:organization: Logilab +:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" +__docformat__ = "restructuredtext en" + + +from datetime import datetime, time, date +from mx.DateTime import DateTime, Date, Time + +def mx2datetime(mxobj, yamstype): + """converts a mx date object (DateTime, Date or Time) into a + regular python datetime object + """ + #if yamstype == 'Datetime': + # don't use date, db model doesn't actually support it, only datetime + return datetime(mxobj.year, mxobj.month, mxobj.day, + mxobj.hour, mxobj.minute, int(mxobj.second)) +# elif yamstype == 'Date': +# return date(mxobj.year, mxobj.month, mxobj.day) +# # XXX don't support time either, what should we do here ? +# return time(mxobj.hour, mxobj.minute, int(mxobj.second)) + +def datetime2mx(datetimeobj, yamstype=None): + """converts a mx date object (DateTime, Date or Time) into a + regular python datetime object + """ + if yamstype is None: + yamstype = guess_yamstype_from_date(datetimeobj) + assert yamstype is not None + if yamstype == 'Datetime': + # don't use date, db model doesn't actually support it, only datetime + return DateTime(datetimeobj.year, datetimeobj.month, datetimeobj.day, + datetimeobj.hour, datetimeobj.minute, int(datetimeobj.second)) + elif yamstype == 'Date': + return Date(datetimeobj.year, datetimeobj.month, datetimeobj.day) + # XXX don't support time either, what should we do here ? + return Time(datetimeobj.hour, datetimeobj.minute, int(datetimeobj.second)) + + +def guess_yamstype_for_date(datetimeobj): + """guesses yams correct type according to `datetimeobj`'s type""" + if isinstance(datetimeobj, datetime): + return 'Datetime' + elif isinstance(datetimeobj, date): + return 'Date' + elif isinstance(datetimeobj, time): + return 'Time' + return None + + +def use_mx_for_dates(func): + """decorator to convert func's return value into mx objects + instead of datetime objects + """ + def wrapper(*args, **kwargs): + value = func(*args, **kwargs) + yamstype = guess_yamstype_for_date(value) + if yamstype is None: + return value + return datetime2mx(value, yamstype) + return wrapper + + +try: + # WARNING: do not import the google's db module here since it will take + # precedence over our own db submodule + from google.appengine.api.datastore import Key, Get, Query + from google.appengine.api.datastore_errors import BadKeyError +except ImportError: + # not in google app environment + pass +else: + + import os + _SS = os.environ.get('SERVER_SOFTWARE') + if _SS is None: + MODE = 'test' + elif _SS.startswith('Dev'): + MODE = 'dev' + else: + MODE = 'prod' + + from cubicweb.server import SOURCE_TYPES + from cubicweb.goa.gaesource import GAESource + SOURCE_TYPES['gae'] = GAESource + + + def do_monkey_patch(): + + # monkey patch yams Bytes validator since it should take a bytes string with gae + # and not a StringIO + def check_bytes(eschema, value): + """check value is a bytes string""" + return isinstance(value, str) + from yams import constraints + constraints.BASE_CHECKERS['Bytes'] = check_bytes + + def rql_for_eid(eid): + return 'Any X WHERE X eid "%s"' % eid + from cubicweb.common import uilib + uilib.rql_for_eid = rql_for_eid + + def typed_eid(eid): + try: + return str(Key(eid)) + except BadKeyError: + raise ValueError(eid) + import cubicweb + cubicweb.typed_eid = typed_eid + + # XXX monkey patch cubicweb.schema.CubicWebSchema to have string eid with + # optional cardinality (since eid is set after the validation) + + import re + from yams import buildobjs as ybo + + def add_entity_type(self, edef): + edef.name = edef.name.encode() + assert re.match(r'[A-Z][A-Za-z0-9]*[a-z]+[0-9]*$', edef.name), repr(edef.name) + eschema = super(CubicWebSchema, self).add_entity_type(edef) + if not eschema.is_final(): + # automatically add the eid relation to non final entity types + rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Bytes', + cardinality='?1', uid=True) + self.add_relation_def(rdef) + rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type) + self.add_relation_def(rdef) + self._eid_index[eschema.eid] = eschema + return eschema + + from cubicweb.schema import CubicWebSchema + CubicWebSchema.add_entity_type = add_entity_type + + + # don't reset vreg on repository set_schema + from cubicweb.server import repository + orig_set_schema = repository.Repository.set_schema + def set_schema(self, schema, resetvreg=True): + orig_set_schema(self, schema, False) + repository.Repository.set_schema = set_schema + # deactivate function ensuring relation cardinality consistency + repository.del_existing_rel_if_needed = lambda *args: None + + def get_cubes(self): + """return the list of top level cubes used by this instance""" + config = self.config + cubes = config['included-cubes'] + config['included-yams-cubes'] + return config.expand_cubes(cubes) + repository.Repository.get_cubes = get_cubes + + from rql import RQLHelper + RQLHelper.simplify = lambda x,r: None + + # activate entity caching on the server side + + def set_entity_cache(self, entity): + self._query_data.setdefault('_eid_cache', {})[entity.eid] = entity + + def entity_cache(self, eid): + return self._query_data['_eid_cache'][eid] + + def drop_entity_cache(self, eid=None): + if eid is None: + self._query_data['_eid_cache'] = {} + elif '_eid_cache' in self._query_data: + self._query_data['_eid_cache'].pop(eid, None) + + def datastore_get(self, key): + if isinstance(key, basestring): + key = Key(key) + try: + gentity = self._query_data['_key_cache'][key] + #self.critical('cached %s', gentity) + except KeyError: + gentity = Get(key) + #self.critical('Get %s', gentity) + self._query_data.setdefault('_key_cache', {})[key] = gentity + return gentity + + def clear_datastore_cache(self, key=None): + if key is None: + self._query_data['_key_cache'] = {} + else: + if isinstance(key, basestring): + key = Key(key) + self._query_data['_key_cache'].pop(key, None) + + from cubicweb.server.session import Session + Session.set_entity_cache = set_entity_cache + Session.entity_cache = entity_cache + Session.drop_entity_cache = drop_entity_cache + Session.datastore_get = datastore_get + Session.clear_datastore_cache = clear_datastore_cache + + from docutils.frontend import OptionParser + # avoid a call to expanduser which is not available under gae + def get_standard_config_files(self): + return self.standard_config_files + OptionParser.get_standard_config_files = get_standard_config_files