55 lines
1.4 KiB
Python
55 lines
1.4 KiB
Python
"""
|
|
explore2.py: Script to explore the OSCON schedule feed
|
|
|
|
>>> import json
|
|
>>> raw_feed = json.load(open('data/osconfeed.json'))
|
|
>>> feed = FrozenJSON(raw_feed)
|
|
>>> len(feed.Schedule.speakers)
|
|
357
|
|
>>> sorted(feed.Schedule.keys())
|
|
['conferences', 'events', 'speakers', 'venues']
|
|
>>> feed.Schedule.speakers[-1].name
|
|
'Carina C. Zona'
|
|
>>> talk = feed.Schedule.events[40]
|
|
>>> talk.name
|
|
'There *Will* Be Bugs'
|
|
>>> talk.speakers
|
|
[3471, 5199]
|
|
>>> talk.flavor
|
|
Traceback (most recent call last):
|
|
...
|
|
KeyError: 'flavor'
|
|
|
|
"""
|
|
|
|
# tag::EXPLORE2[]
|
|
from collections import abc
|
|
import keyword
|
|
|
|
class FrozenJSON:
|
|
"""A read-only façade 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>
|
|
# end::EXPLORE2[]
|