do not depend on types.StringTypes anymore
[sfa.git] / sfa / storage / model.py
index 7ce8345..923576e 100644 (file)
@@ -1,4 +1,3 @@
-from types import StringTypes
 from datetime import datetime
 
 from sqlalchemy import or_, and_ 
@@ -14,6 +13,7 @@ from sfa.storage.record import Record
 from sfa.util.sfalogging import logger
 from sfa.util.sfatime import utcparse, datetime_to_string
 from sfa.util.xml import XML 
+from sfa.util.py23 import StringType
 
 from sfa.trust.gid import GID
 
@@ -75,7 +75,7 @@ class AlchemyObj(Record):
 # but we had to define another more internal column (classtype) so we 
 # accomodate variants in types like authority+am and the like
 
-class RegRecord (Base, AlchemyObj):
+class RegRecord(Base, AlchemyObj):
     __tablename__       = 'records'
     record_id           = Column (Integer, primary_key=True)
     # this is the discriminator that tells which class to use
@@ -99,7 +99,7 @@ class RegRecord (Base, AlchemyObj):
         if type:                                self.type=type
         if hrn:                                 self.hrn=hrn
         if gid: 
-            if isinstance(gid, StringTypes):    self.gid=gid
+            if isinstance(gid, StringType):     self.gid=gid
             else:                               self.gid=gid.save_to_string(save_parents=True)
         if authority:                           self.authority=authority
         if peer_authority:                      self.peer_authority=peer_authority
@@ -130,7 +130,7 @@ class RegRecord (Base, AlchemyObj):
     @validates ('gid')
     def validate_gid (self, key, gid):
         if gid is None:                     return
-        elif isinstance(gid, StringTypes):  return gid
+        elif isinstance(gid, StringType):   return gid
         else:                               return gid.save_to_string(save_parents=True)
 
     def validate_datetime (self, key, incoming):
@@ -182,7 +182,7 @@ slice_researcher_table = \
 # all subclasses define a convenience constructor with a default value for type, 
 # and when applicable a way to define local fields in a kwd=value argument
 ####################
-class RegAuthority (RegRecord):
+class RegAuthority(RegRecord):
     __tablename__       = 'authorities'
     __mapper_args__     = { 'polymorphic_identity' : 'authority' }
     record_id           = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
@@ -224,7 +224,7 @@ class RegAuthority (RegRecord):
         self.reg_pis = pis
 
 ####################
-class RegSlice (RegRecord):
+class RegSlice(RegRecord):
     __tablename__       = 'slices'
     __mapper_args__     = { 'polymorphic_identity' : 'slice' }
     record_id           = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
@@ -271,7 +271,7 @@ class RegSlice (RegRecord):
         return self.validate_datetime (key, incoming)
 
 ####################
-class RegNode (RegRecord):
+class RegNode(RegRecord):
     __tablename__       = 'nodes'
     __mapper_args__     = { 'polymorphic_identity' : 'node' }
     record_id           = Column (Integer, ForeignKey ("records.record_id"), primary_key=True)
@@ -285,7 +285,7 @@ class RegNode (RegRecord):
         return RegRecord.__repr__(self).replace("Record", "Node")
 
 ####################
-class RegUser (RegRecord):
+class RegUser(RegRecord):
     __tablename__       = 'users'
     # these objects will have type='user' in the records table
     __mapper_args__     = { 'polymorphic_identity' : 'user' }
@@ -324,7 +324,7 @@ class RegUser (RegRecord):
 # meaning, when querying the whole records, we expect there should
 # be a single query to fetch all the keys 
 # or, is it enough that we issue a single query to retrieve all the keys 
-class RegKey (Base):
+class RegKey(Base):
     __tablename__       = 'keys'
     key_id              = Column (Integer, primary_key=True)
     record_id           = Column (Integer, ForeignKey ("records.record_id"))
@@ -489,15 +489,35 @@ augment_map = {'authority': {'reg-pis' : 'reg_pis',},
            }
 
 
+# xxx mystery
+# the way we use sqlalchemy might be a little wrong
+# in any case what has been observed is that (Reg)Records as returned by an sqlalchemy
+# query not always have their __dict__ properly adjusted
+# typically a RegAuthority object would have its object.name set properly, but
+# object.__dict__ has no 'name' key
+# which is an issue because we rely on __dict__ for many things, in particular this
+# is what gets exposed to the drivers (this is historical and dates back before sqlalchemy)
+# so it is recommended to always run this function that will make sure
+# that such built-in fields are properly set in __dict__ too
+# 
 def augment_with_sfa_builtins(local_record):
     # don't ruin the import of that file in a client world
     from sfa.util.xrn import Xrn
     # add a 'urn' field
     setattr(local_record, 'reg-urn', Xrn(xrn=local_record.hrn, type=local_record.type).urn)
     # users have keys and this is needed to synthesize 'users' sent over to CreateSliver
+    fields_to_check = []
     if local_record.type == 'user':
         user_keys = [ key.key for key in local_record.reg_keys ]
         setattr(local_record, 'reg-keys', user_keys)
+        fields_to_check = ['email']
+    elif local_record.type == 'authority':
+        fields_to_check = ['name']
+    for field in fields_to_check:
+        if not field in local_record.__dict__:
+            logger.debug("augment_with_sfa_builtins: hotfixing missing '{}' in {}"
+                         .format(field, local_record.hrn))
+            local_record.__dict__[field] = getattr(local_record, field)
     # search in map according to record type
     type_map = augment_map.get(local_record.type, {})
     # use type-dep. map to do the job