In [8]:
from urllib.request import urlopen
import warnings
import os
import json
URL = 'http://www.oreilly.com/pub/sc/osconfeed'
JSON = 'data/osconfeed.json'
def load():
if not os.path.exists(JSON):
msg = 'downloading {} to {}'.format(URL, JSON)
warnings.warn(msg)
with urlopen(URL) as remote, open(JSON, 'wb') as local:
local.write(remote.read())
with open(JSON) as fp:
return json.load(fp)
In [9]:
feed = load()
In [4]:
sorted(feed['Schedule'].keys())
Out[4]:
In [5]:
for key, value in sorted(feed['Schedule'].items()):
print('{:3} {}'.format(len(value), key))
In [ ]:
feed['Schedule']['speakers'][-1]['name']
In [ ]:
feed['Schedule']['speakers'][-1]['serial']
In [ ]:
feed['Schedule']['events'][40]['name']
In [ ]:
feed['Schedule']['events'][40]['speakers']
In [7]:
from collections import abc
class FrozenJSON:
"""A read-only facade for navigating a JSON-like object
using attribute notation"""
def __init__(self, mapping):
self.__data = dict(mapping) #1
def __getattr__(self, name): #2
if hasattr(self.__data, name):
return getattr(self.__data, name) #3
else:
return FrozenJSON.build(self.__data[name]) #4
@classmethod
def build(cls, obj): #5
if isinstance(obj, abc.Mapping): #6
return cls(obj)
elif isinstance(obj, abc.MutableSequence): #7
return [cls.build(item) for item in obj]
else: #8
return obj
In [8]:
from osconfeed import load
raw_feed = load()
feed = FrozenJSON(raw_feed)
In [9]:
raw_feed = load()
In [10]:
feed = FrozenJSON(raw_feed)
In [11]:
len(feed.Schedule.speakers)
Out[11]:
In [12]:
sorted(feed.Schedule.keys())
Out[12]:
In [13]:
for key, value in sorted(feed.Schedule.items()):
print('{:3} {}'.format(len(value), key))
In [14]:
feed.Schedule.speakers[-1].name
Out[14]:
In [15]:
talk = feed.Schedule.events[40]
In [16]:
type(talk)
Out[16]:
In [17]:
talk.name
Out[17]:
In [18]:
talk.speakers
Out[18]:
In [19]:
talk.flavor
In [20]:
grad = FrozenJSON({'name': 'Jim Bo', 'class': 1982})
In [21]:
grad.class
In [22]:
getattr(grad,'class')
Out[22]:
In [24]:
from collections import abc
import keyword
class FrozenJSON:
"""A read-only facade for navigating a JSON-like object
using attribute notation"""
def __init__(self, mapping):
self.__data = {}
for key, value in mapping.items():
if keyword.iskeyword(key):
key += '_'
self.__data[key] = value
def __getattr__(self, name):
if hasattr(self.__data, name):
return getattr(self.__data, name)
else:
return FrozenJSON.build(self.__data[name])
@classmethod
def build(cls, obj):
if isinstance(obj, abc.Mapping):
return cls(obj)
elif isinstance(obj, abc.MutableSequence):
return [cls.build(item) for item in obj]
else:
return obj
In [27]:
grad = FrozenJSON({'name': 'Jim Bo', 'class': 1982})
grad.class_
Out[27]:
In [28]:
x = FrozenJSON({'2be': 'or not'})
x.2be
In [1]:
from collections import abc
class FrozenJSON:
"""A read-only facade for navigating a JSON-like object
using attribute notation"""
def __new__(cls, arg): #1
if isinstance(arg, abc.Mapping):
return super().__new__(cls) #2
elif isinstance(arg, abc.MutableSequence): #3
return [cls(item) for item in arg]
else:
return arg
def __init__(self, mapping):
self.__data = {}
for key, value in mapping.items():
if keyword.iskeyword(key):
key += '_'
self.__data[key] = value
def __getattr__(self, name):
if hasattr(self.__data, name):
return getattr(self.__data, name)
else:
return FrozenJSON(self.__data[name]) #4
In [7]:
import warnings
import osconfeed
DB_NAME = 'data/schedule1_db'
CONFERENCE = 'conference.115'
class Record:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def load_db(db):
raw_data = osconfeed.load()
warnings.warn('loading' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items():
record_type = collection[:-1]
for record in rec_list:
key = '{}.{}'.format(record_type, record['serial'])
record['serial'] = key
db[key] = Record(**record)
In [8]:
import shelve
db = shelve.open(DB_NAME)
In [11]:
if CONFERENCE not in db:
load_db(db)
In [12]:
speaker = db['speaker.3471']
In [13]:
type(speaker)
Out[13]:
In [14]:
speaker.name, speaker.twitter
Out[14]:
In [15]:
db.close()
In [13]:
import warnings
import inspect
import osconfeed
DB_NAME = 'data/schedule2_db'
CONFERENCE = 'conference.115'
class Record:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __eq__(self, other):
if isinstance(other, Record):
return self.__dict__ == other.__dict__
else:
return NotImplemented
class MissingDatabaseError(RuntimeError):
"""Raised when a database is required but was not set."""
class DbRecord(Record):
__db = None
@staticmethod
def set_db(db):
DbRecord.__db = db
@staticmethod
def get_db():
return DbRecord.__db
@classmethod
def fetch(cls, ident):
db = cls.get_db()
try:
return db[ident]
except TypeError:
if db is None:
msg = "database not set; call '{}.set_db(mydb)'"
raise MissingDatabaseError(msg.format(cls.__name__))
else:
raise
def __repr__(self):
if hasattr(self, 'serial'):
cls_name = self.__class__.__name__
return '<{} serial={!r}>'.format(cls_name, self.serial)
else:
return super().__repr__()
class Event(DbRecord):
@property
def venue(self):
key = 'venue.{}'.format(self.venue_serial)
return self.__class__.fetch(key)
@property
def speakers(self):
if not hasattr(self, '_speaker_objs'):
spkr_serials = self.__dict__['speakers']
fetch = self.__class__.fetch
self._speaker_objs = [fetch('speaker.{}'.format(key))
for key in spkr_serials]
return self._speaker_objs
def __repr__(self):
if hasattr(self, 'name'):
cls_name = self.__class__.__name__
return '<{} {!r}>'.format(cls_name, self.name)
else:
return super().__repr__()
def load_db(db):
raw_data = osconfeed.load()
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items():
record_type = collection[:-1]
cls_name = record_type.capitalize()
cls = globals().get(cls_name, DbRecord)
if inspect.isclass(cls) and issubclass(cls, DbRecord):
factory = cls
else:
factory = DbRecord
for record in rec_list:
key = '{}.{}'.format(record_type, record['serial'])
record['serial'] = key
db[key] = factory(**record)
In [15]:
import shelve
db = shelve.open(DB_NAME)
if CONFERENCE not in db:
load_db(db)
In [16]:
DbRecord.set_db(db)
In [17]:
event = DbRecord.fetch('event.33950')
In [18]:
event
Out[18]:
In [19]:
event.venue
Out[19]:
In [20]:
event.venue.name
Out[20]:
In [21]:
for spkr in event.speakers:
print('{0.serial}: {0.name}'.format(spkr))
In [22]:
event.speakers
Out[22]:
In [23]:
db.close()
In [24]:
class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
In [25]:
raisins = LineItem('Golden raisins', 10, 6.95)
raisins.subtotal()
Out[25]:
In [26]:
raisins.weight = -20
raisins.subtotal()
Out[26]:
In [27]:
class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, value):
if value > 0:
self.__weight = value
else:
raise ValueError('value must be > 0')
In [28]:
walnuts = LineItem('walnuts', 0, 10.00)
In [29]:
class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
def get_weight(self):
return self.__weight
def set_weight(self):
if value > 0:
self.__weight = value
else:
raise ValueError('value must be > 0')
weight = property(get_weight, set_weight)
In [30]:
class Class:
data = 'the class data attr'
@property
def prop(self):
return 'the prop value'
In [31]:
obj = Class()
vars(obj)
Out[31]:
In [32]:
obj.data
Out[32]:
In [33]:
obj.data = 'bar'
In [34]:
vars(obj)
Out[34]:
In [35]:
obj.data
Out[35]:
In [36]:
Class.data
Out[36]:
In [37]:
Class.prop
Out[37]:
In [38]:
obj.prop
Out[38]:
In [39]:
obj.prop = 'foo'
In [40]:
obj.__dict__['prop'] = 'foo'
In [41]:
vars(obj)
Out[41]:
In [42]:
obj.prop
Out[42]:
In [43]:
Class.prop = 'baz'
In [44]:
obj.prop
Out[44]:
In [45]:
obj.data
Out[45]:
In [46]:
Class.data
Out[46]:
In [47]:
Class.data = property(lambda self: 'the "data" prop value')
In [48]:
obj.data
Out[48]:
In [49]:
del Class.data
In [50]:
obj.data
Out[50]:
In [54]:
class Foo:
@property
def bar(self):
"""The bar attribute"""
return self.__dict__['bar']
@property
def bar(self, value):
self.__dict__['bar'] = value
In [55]:
help(Foo.bar)
In [56]:
help(Foo)
In [58]:
def quantity(storage_name):
def qty_getter(instance):
return instance.__dict__[storage_name]
def qty_setter(instance, value):
if value > 0 :
instance.__dict__[storage_name] = value
else:
return ValueError('value must be > 0')
return property(qty_getter, qty_setter)
In [59]:
class LineItem:
weight = quantity('weight')
price = quantity('price')
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
In [60]:
nutmeg = LineItem('Moluccan nutmeg', 8, 13.95)
nutmeg.weight, nutmeg.price
Out[60]:
In [63]:
sorted(vars(nutmeg).items())
Out[63]:
In [ ]: