Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

root/trunk/json.py

Revision 453 (checked in by fumanchu, 6 years ago)

MAJOR REFACTOR: moved Arena from assumed root of storage graph to arbitrary node. Still lots of dark corners to finish migrating, but the core works. This now means users can bind Sandboxes to any store, and can mediate multiple stores at any point in the SM graph.

  • Property svn:eol-style set to native
Line 
1 """JSON conversion support for Dejavu Units."""
2
3 import datetime
4 import decimal
5 import time
6
7 import dejavu
8 from simplejson import JSONEncoder, JSONDecoder
9
10 __all__ = ["Encoder", "Decoder", "Converter"]
11
12 class Null(object):
13     class meta(type):
14         def __new__(cls, *args, **kwargs):
15             if '_inst' not in vars(cls):
16                 cls._inst = type.__new__(cls, *args, **kwargs)
17             return cls._inst
18     __metaclass__ = meta
19     def __init__(self, *args, **kwargs): pass
20     def __call__(self, *args, **kwargs): return self
21     def __repr__(self): return "Null()"
22     def __nonzero__(self): return False
23     def __getattr__(self, name): return self
24     def __setattr__(self, name, value): return self
25     def __delattr__(self, name): return self
26
27 class Encoder(JSONEncoder):
28     """Extends the base simplejson JSONEncoder for Dejavu."""
29    
30     DATE_FMT = "%Y-%m-%d"
31     TIME_FMT = "%H:%M:%S"
32     DATETIME_FMT = "%s %s" % (DATE_FMT, TIME_FMT)
33    
34     def default(self, o):
35         _special_types = (datetime.date, datetime.time, datetime.datetime,
36                           decimal.Decimal)
37         if isinstance(o, dejavu.Unit):
38             cls = o.__class__
39             d = {'__dejavu.Unit__': True,
40                  '__class__': cls.__name__}
41             for key in o.properties:
42                 val = getattr(o, key)
43                 type = getattr(cls, key).type
44                 if val is None and type in _special_types:
45                     val = type
46                 d[key] = val
47             return d
48        
49         # We MUST check for a datetime.datetime instance before datetime.date.
50         # datetime.datetime is a subclass of datetime.date, and therefore
51         # instances of it are also instances of datetime.date.
52         if isinstance(o, datetime.datetime) or o is datetime.datetime:
53             if o is datetime.datetime:
54                 o = Null()
55             return {'__datetime__': True,
56                     'value': o.strftime(self.DATETIME_FMT)}
57        
58         if isinstance(o, datetime.date) or o is datetime.date:
59             if o is datetime.date:
60                 o = Null()
61             return {'__date__': True,
62                     'value': o.strftime(self.DATE_FMT)}
63        
64         if isinstance(o, datetime.time) or o is datetime.time:
65             if o is datetime.time:
66                 o = Null()
67             return {'__time__': True,
68                     'value': o.strftime(self.TIME_FMT)}
69        
70         if isinstance(o, decimal.Decimal) or o is decimal.Decimal:
71             if o is decimal.Decimal:
72                 value = None
73             else:
74                 value = unicode(o)
75             return {'__decimal__': True,
76                     'value': value}
77        
78         if isinstance(o, Null):
79             return None
80        
81         else:
82             return JSONEncoder.default(self, o)
83
84 class Decoder(JSONDecoder):
85     """Extends the base simplejson JSONDecoder for Dejavu."""
86    
87     DATE_FMT = "%Y-%m-%d"
88     TIME_FMT = "%H:%M:%S"
89     DATETIME_FMT = "%s %s" % (DATE_FMT, TIME_FMT)
90    
91     def __init__(self, store=None, encoding=None, object_hook=None):
92         JSONDecoder.__init__(self, encoding, object_hook)
93         if not self.object_hook:
94             self.object_hook = self.json_to_python
95         self.store = store
96    
97     def json_to_python(self, d):
98         if '__datetime__' in d:
99             if d['value'] is None:
100                 return None
101             strp = time.strptime(d['value'], self.DATETIME_FMT)[:7]
102             return datetime.datetime(*strp)
103         if '__date__' in d:
104             if d['value'] is None:
105                 return None
106             strp = time.strptime(d['value'], self.DATE_FMT)[:3]
107             return datetime.date(*strp)
108         if '__time__' in d:
109             if d['value'] is None:
110                 return None
111             strp = time.strptime(d['value'], self.TIME_FMT)[3:6]
112             return datetime.time(*strp)
113         if '__decimal__' in d:
114             if d['value'] is None:
115                 return None
116             return decimal.Decimal(d['value'])
117         if '__dejavu.Unit__' in d:
118             # If d['ID'] != None, should we lookup the Unit in storage set
119             # any changed attributes, and then return the Unit?  The approach
120             # below is to return a new instance of the Unit with the values
121             # from d.  It is then up to library user to explicitly set the
122             # property values on any existing Unit.
123             clsname = d['__class__']
124             cls = self.store.class_by_name(clsname)
125             params = dict([(str(k), v) for k,v in d.iteritems() if not k.startswith('__')])
126             unit = cls(**params)
127             return unit
128         return d
129
130 class Converter(object):
131     """Provides two-way conversion of Units/JSON via loads and dumps methods.
132     
133     Also converts datetime.date, datetime.time, datetime.datetime and
134     decimal.Decimal to/from JSON.
135
136     This is accomplished by the Encoder and Decoder classes, which are
137     subclasses of their counterparts in simplejson.  If you wish to change
138     the output of the converter at all, you should probably subclass the
139     Encoder/Decoder and then make a cusom Converter subclass with your
140     encoder/decoder as class attributes.
141     """
142    
143     encoder = Encoder
144     decoder = Decoder
145    
146     def __init__(self, store):
147         self.store = store
148    
149     def loads(self, s, encoding=None, **kw):
150         return self.decoder(encoding=encoding, store=self.store, **kw).decode(s)
151
152     def dumps(self, obj, skipkeys=False, ensure_ascii=False,
153               check_circular=True, allow_nan=True, indent=None, **kw):
154         return self.encoder(skipkeys, ensure_ascii, check_circular,
155                             allow_nan, indent, **kw).encode(obj)
Note: See TracBrowser for help on using the browser.