goa/__init__.py
changeset 0 b97547f5f1fa
child 447 0e52d72104a6
--- /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