update from Atlas

This commit is contained in:
Luciano Ramalho
2015-01-05 03:20:08 -02:00
parent 1edb0cd05b
commit 0618105a47
18 changed files with 822 additions and 123 deletions

48
metaprog/blackknight.py Normal file
View File

@@ -0,0 +1,48 @@
"""
This class is inspired by the Black Knight scene in the movie
"Monty Python and the Holy Grail", where King Arthur fights the
Black Knight, slicing off his arms and legs, but the knight
refuses to concede defeat.
# BEGIN BLACK_KNIGHT_DEMO
>>> knight = BlackKnight()
>>> knight.member
next member is:
'an arm'
>>> del knight.member
BLACK KNIGHT (loses an arm)
-- 'Tis but a scratch.
>>> del knight.member
BLACK KNIGHT (loses another arm)
-- It's just a flesh wound.
>>> del knight.member
BLACK KNIGHT (loses a leg)
-- I'm invincible!
>>> del knight.member
BLACK KNIGHT (loses another leg)
-- All right, we'll call it a draw.
# END BLACK_KNIGHT_DEMO
"""
# BEGIN BLACK_KNIGHT
class BlackKnight:
def __init__(self):
self.members = ['an arm', 'another arm',
'a leg', 'another leg']
self.phrases = ["'Tis but a scratch.",
"It's just a flesh wound.",
"I'm invincible!",
"All right, we'll call it a draw."]
@property
def member(self):
print('next member is:')
return self.members[0]
@member.deleter
def member(self):
text = 'BLACK KNIGHT (loses {})\n-- {}'
print(text.format(self.members.pop(0), self.phrases.pop(0)))
# END BLACK_KNIGHT

23
metaprog/doc_property.py Normal file
View File

@@ -0,0 +1,23 @@
"""
Example of property documentation
>>> f = Foo()
>>> f.bar = 77
>>> f.bar
77
>>> Foo.bar.__doc__
'The bar attribute'
"""
# BEGIN DOC_PROPERTY
class Foo:
@property
def bar(self):
'''The bar attribute'''
return self.__dict__['bar']
@bar.setter
def bar(self, value):
self.__dict__['bar'] = value
# END DOC_PROPERTY

View File

@@ -1,30 +1,30 @@
"""
explore1.py: Script to explore the OSCON schedule feed
# BEGIN EXPLORE1_DEMO
>>> from osconfeed import load
>>> raw_feed = load()
>>> feed = FrozenJSON(raw_feed)
>>> sorted(feed.Schedule.keys())
>>> feed = FrozenJSON(raw_feed) # <1>
>>> len(feed.Schedule.speakers) # <2>
357
>>> sorted(feed.Schedule.keys()) # <3>
['conferences', 'events', 'speakers', 'venues']
>>> for key, value in sorted(feed.Schedule.items()):
... print('{:3} {}'.format(len(value), key))
...
1 conferences
484 events
357 speakers
53 venues
>>> feed.Schedule.speakers[-1].name
>>> feed.Schedule.speakers[-1].name # <4>
'Carina C. Zona'
>>> carina = feed.Schedule.speakers[-1]
>>> carina.twitter
'cczona'
>>> feed.Schedule.events[40].name
>>> talk = feed.Schedule.events[40] # <5>
>>> talk.name
'There *Will* Be Bugs'
>>> feed.Schedule.events[40].speakers
>>> talk.speakers # <6>
[3471, 5199]
>>> talk.flavor # <7>
Traceback (most recent call last):
...
KeyError: 'flavor'
# END EXPLORE1_DEMO
"""
# BEGIN EXPLORE1
from collections import abc
@@ -34,19 +34,20 @@ class FrozenJSON:
"""
def __init__(self, mapping):
self._data = dict(mapping)
self._data = dict(mapping) # <1>
def __getattr__(self, name):
def __getattr__(self, name): # <2>
if hasattr(self._data, name):
return getattr(self._data, name)
return getattr(self._data, name) # <3>
else:
return FrozenJSON.build(self._data[name])
return FrozenJSON.build(self._data[name]) # <4>
@classmethod
def build(cls, obj):
if isinstance(obj, abc.Mapping):
def build(cls, obj): # <5>
if isinstance(obj, abc.Mapping): # <6>
return cls(obj)
elif isinstance(obj, abc.MutableSequence):
elif isinstance(obj, abc.MutableSequence): # <7>
return [cls.build(item) for item in obj]
else:
else: # <8>
return obj
# END EXPLORE1

View File

@@ -4,27 +4,25 @@ explore2.py: Script to explore the OSCON schedule feed
>>> from osconfeed import load
>>> raw_feed = load()
>>> feed = FrozenJSON(raw_feed)
>>> len(feed.Schedule.speakers)
357
>>> sorted(feed.Schedule.keys())
['conferences', 'events', 'speakers', 'venues']
>>> for key, value in sorted(feed.Schedule.items()):
... print('{:3} {}'.format(len(value), key))
...
1 conferences
484 events
357 speakers
53 venues
>>> feed.Schedule.speakers[-1].name
'Carina C. Zona'
>>> carina = feed.Schedule.speakers[-1]
>>> carina.twitter
'cczona'
>>> feed.Schedule.events[40].name
>>> talk = feed.Schedule.events[40]
>>> talk.name
'There *Will* Be Bugs'
>>> feed.Schedule.events[40].speakers
>>> talk.speakers
[3471, 5199]
>>> talk.flavor
Traceback (most recent call last):
...
KeyError: 'flavor'
"""
# BEGIN EXPLORE2
from collections import abc
@@ -33,11 +31,11 @@ class FrozenJSON:
using attribute notation
"""
def __new__(cls, arg):
def __new__(cls, arg): # <1>
if isinstance(arg, abc.Mapping):
return super().__new__(cls)
elif isinstance(arg, abc.MutableSequence):
return [FrozenJSON(item) for item in arg]
return super().__new__(cls) # <2>
elif isinstance(arg, abc.MutableSequence): # <3>
return [cls(item) for item in arg]
else:
return arg
@@ -48,4 +46,5 @@ class FrozenJSON:
if hasattr(self._data, name):
return getattr(self._data, name)
else:
return FrozenJSON(self._data[name])
return FrozenJSON(self._data[name]) # <4>
# END EXPLORE2

View File

@@ -2,12 +2,12 @@
{ "conferences": [{"serial": 115 }],
"events": [
{ "serial": 34505,
"name": "Why Schools Don&#x27;t Use Open Source to Teach Programming",
"name": "Why Schools Don´t Use Open Source to Teach Programming",
"event_type": "40-minute conference session",
"time_start": "2014-07-23 11:30:00",
"time_stop": "2014-07-23 12:10:00",
"venue_serial": 1462,
"description": "Aside from the fact that high school programming curricula often require proprietary IDEs, they also don&#x27;t involve examining any source code from Open Source software projects. What changes would be required in programming curricula to incorporate Open Source? And is that a desirable objective?",
"description": "Aside from the fact that high school programming...",
"website_url": "http://oscon.com/oscon2014/public/schedule/detail/34505",
"speakers": [157509],
"categories": ["Education"] }
@@ -20,7 +20,7 @@
"position": "CTO",
"affiliation": "Sharewave",
"twitter": "sharewaveteam",
"bio": "Robert 'r0ml' Lefkowitz is the CTO at Sharewave, a startup building an investor management portal. This year, he was a resident at the winter session of Hacker School. He is a Distinguished Engineer of the ACM." }
"bio": "Robert ´r0ml´ Lefkowitz is the CTO at Sharewave, a startup..." }
],
"venues": [
{ "serial": 1462,

View File

@@ -1,43 +1,49 @@
"""
osconfeed.py: Script to download the OSCON schedule feed
>>> feed = load()
>>> sorted(feed['Schedule'].keys())
# BEGIN OSCONFEED_DEMO
>>> feed = load() # <1>
>>> sorted(feed['Schedule'].keys()) # <2>
['conferences', 'events', 'speakers', 'venues']
>>> for key, value in sorted(feed['Schedule'].items()):
... print('{:3} {}'.format(len(value), key))
... print('{:3} {}'.format(len(value), key)) # <3>
...
1 conferences
484 events
357 speakers
53 venues
>>> feed['Schedule']['speakers'][-1]['name']
>>> feed['Schedule']['speakers'][-1]['name'] # <4>
'Carina C. Zona'
>>> carina = feed['Schedule']['speakers'][-1]
>>> carina['twitter']
'cczona'
>>> feed['Schedule']['speakers'][-1]['serial'] # <5>
141590
>>> feed['Schedule']['events'][40]['name']
'There *Will* Be Bugs'
>>> feed['Schedule']['events'][40]['speakers']
>>> feed['Schedule']['events'][40]['speakers'] # <6>
[3471, 5199]
# END OSCONFEED_DEMO
"""
# BEGIN OSCONFEED
from urllib.request import urlopen
import warnings
import os
import json
URL = 'http://www.oreilly.com/pub/sc/osconfeed'
JSON_NAME = 'data/osconfeed.json'
JSON = 'data/osconfeed.json'
def load():
if not os.path.exists(JSON_NAME):
msg = 'downloading {} to {}'.format(URL, JSON_NAME)
warnings.warn(msg)
with urlopen(URL) as remote, open(JSON_NAME, 'wb') as local:
if not os.path.exists(JSON):
msg = 'downloading {} to {}'.format(URL, JSON)
warnings.warn(msg) # <1>
with urlopen(URL) as remote, open(JSON, 'wb') as local: # <2>
local.write(remote.read())
with open(JSON_NAME) as fp:
return json.load(fp)
with open(JSON) as fp:
return json.load(fp) # <3>
# END OSCONFEED

View File

@@ -1,19 +1,26 @@
"""
schedule1.py: traversing OSCON schedule data
# BEGIN SCHEDULE1_DEMO
>>> import shelve
>>> db = shelve.open(DB_NAME)
>>> if CONFERENCE not in db: load_db(db)
>>> event = db['event.33950']
>>> speaker = db['speaker.3471']
>>> speaker.name
>>> db = shelve.open(DB_NAME) # <1>
>>> if CONFERENCE not in db: # <2>
... load_db(db) # <3>
...
>>> speaker = db['speaker.3471'] # <4>
>>> type(speaker) # <5>
<class 'schedule1.Record'>
>>> speaker.name # <6>
'Anna Martelli Ravenscroft'
>>> speaker.twitter
'annaraven'
>>> db.close()
>>> db.close() # <7>
# END SCHEDULE1_DEMO
"""
# BEGIN SCHEDULE1
import warnings
import osconfeed
@@ -23,15 +30,18 @@ CONFERENCE = 'conference.115'
class Record:
def __init__(self, mapping):
self.__dict__.update(mapping)
def __init__(self, **kwargs):
self.__dict__.update(kwargs) # <1>
def load_db(db):
raw_data = osconfeed.load()
raw_data = osconfeed.load() # <2>
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items():
rec_type = collection[:-1]
for fields in rec_list:
key = '{}.{}'.format(rec_type, fields['serial'])
db[key] = Record(fields)
for collection, rec_list in raw_data['Schedule'].items(): # <3>
record_type = collection[:-1] # <4>
for record in rec_list:
key = '{}.{}'.format(record_type, record['serial']) # <5>
record['serial'] = key # <6>
db[key] = Record(**record) # <7>
# END SCHEDULE1

View File

@@ -4,82 +4,128 @@ schedule2.py: traversing OSCON schedule data
>>> import shelve
>>> db = shelve.open(DB_NAME)
>>> if CONFERENCE not in db: load_db(db)
>>> DbRecord.set_db(db)
>>> event = Event.get('event.33950')
>>> event
# BEGIN SCHEDULE2_DEMO
>>> DbRecord.set_db(db) # <1>
>>> event = DbRecord.fetch('event.33950') # <2>
>>> event # <3>
<Event 'There *Will* Be Bugs'>
>>> event.speakers[0].name
'Anna Martelli Ravenscroft'
>>> event.venue # <4>
<DbRecord serial='venue.1449'>
>>> event.venue.name # <5>
'Portland 251'
>>> for spkr in event.speakers: # <6>
... print('{0.serial}: {0.name}'.format(spkr))
...
speaker.3471: Anna Martelli Ravenscroft
speaker.5199: Alex Martelli
# END SCHEDULE2_DEMO
>>> db.close()
"""
# BEGIN SCHEDULE2_RECORD
import warnings
import inspect
import inspect # <1>
import osconfeed
DB_NAME = 'data/schedule2_db'
DB_NAME = 'data/schedule2_db' # <2>
CONFERENCE = 'conference.115'
class Record:
def __init__(self, mapping):
self.__dict__.update(mapping)
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __eq__(self, other):
def __eq__(self, other): # <3>
if isinstance(other, Record):
return self.__dict__ == other.__dict__
else:
return NotImplemented
# END SCHEDULE2_RECORD
# BEGIN SCHEDULE2_DBRECORD
class MissingDatabaseError(RuntimeError):
"""Raised when a database is required but was not set.""" # <1>
class DbRecord(Record): # <2>
_db = None # <3>
@staticmethod # <4>
def set_db(db):
DbRecord._db = db # <5>
@staticmethod # <6>
def get_db():
return DbRecord._db
@classmethod # <7>
def fetch(cls, ident):
db = cls.get_db()
try:
return db[ident] # <8>
except TypeError:
if db is None: # <9>
msg = "database not set; call '{}.set_db(my_db)'"
raise MissingDatabaseError(msg.format(cls.__name__))
else: # <10>
raise
def __repr__(self):
if hasattr(self, 'name'):
ident = repr(self.name)
if hasattr(self, 'serial'): # <11>
cls_name = self.__class__.__name__
return '<{} serial={!r}>'.format(cls_name, self.serial)
else:
ident = 'object at ' + hex(id(self))
cls_name = self.__class__.__name__
return '<{} {}>'.format(cls_name, ident)
return super().__repr__() # <12>
# END SCHEDULE2_DBRECORD
class DbRecord(Record):
@classmethod
def set_db(cls, db):
cls._db = db
@classmethod
def get(cls, ident):
return cls._db[ident]
class Event(DbRecord):
# BEGIN SCHEDULE2_EVENT
class Event(DbRecord): # <1>
@property
def venue(self):
key = self.venue_serial
return self._db['venue.{}'.format(key)]
key = 'venue.{}'.format(self.venue_serial)
return self.fetch(key) # <2>
@property
def speakers(self):
spkr_serials = self.__dict__['speakers']
if not hasattr(self, '_speaker_refs'):
self._speaker_refs = [self._db['speaker.{}'.format(key)]
for key in spkr_serials]
return self._speaker_refs
if not hasattr(self, '_speaker_objs'): # <3>
spkr_serials = self.__dict__['speakers'] # <4>
self._speaker_objs = [self.fetch('speaker.{}'.format(key))
for key in spkr_serials] # <5>
return self._speaker_objs # <6>
def __repr__(self):
if hasattr(self, 'name'): # <7>
cls_name = self.__class__.__name__
return '<{} {!r}>'.format(cls_name, self.name)
else:
return super().__repr__() # <8>
# END SCHEDULE2_EVENT
# BEGIN SCHEDULE2_LOAD
def load_db(db):
raw_data = osconfeed.load()
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items():
rec_type = collection[:-1]
for fields in rec_list:
cls_name = rec_type.capitalize()
cls = globals().get(cls_name, Record)
if inspect.isclass(cls) and issubclass(cls, Record):
record = cls(fields)
else:
Record(fields)
key = '{}.{}'.format(rec_type, fields['serial'])
db[key] = record
record_type = collection[:-1] # <1>
cls_name = record_type.capitalize() # <2>
cls = globals().get(cls_name, DbRecord) # <3>
if inspect.isclass(cls) and issubclass(cls, DbRecord): # <4>
factory = cls # <5>
else:
factory = DbRecord # <6>
for record in rec_list: # <7>
key = '{}.{}'.format(record_type, record['serial'])
record['serial'] = key
db[key] = factory(**record) # <>
# END SCHEDULE2_LOAD

View File

@@ -13,7 +13,7 @@ def db():
def test_record_class():
rec = schedule.Record({'spam': 99, 'eggs': 12})
rec = schedule.Record(spam=99, eggs=12)
assert rec.spam == 99
assert rec.eggs == 12

View File

@@ -13,16 +13,16 @@ def db():
def test_record_attr_access():
rec = schedule.Record({'spam': 99, 'eggs': 12})
rec = schedule.Record(spam=99, eggs=12)
assert rec.spam == 99
assert rec.eggs == 12
def test_record_repr():
rec = schedule.Record({'spam': 99, 'eggs': 12})
assert repr(rec).startswith('<Record object at 0x')
rec2 = schedule.Record({'name': 'Fido'})
assert repr(rec2) == "<Record 'Fido'>"
rec = schedule.DbRecord(spam=99, eggs=12)
assert 'DbRecord object at 0x' in repr(rec)
rec2 = schedule.DbRecord(serial=13)
assert repr(rec2) == "<DbRecord serial=13>"
def test_conference_record(db):
@@ -34,9 +34,14 @@ def test_speaker_record(db):
assert speaker.name == 'Anna Martelli Ravenscroft'
def test_missing_db_exception():
with pytest.raises(schedule.MissingDatabaseError):
schedule.DbRecord.fetch('venue.1585')
def test_dbrecord(db):
schedule.DbRecord.set_db(db)
venue = schedule.DbRecord.get('venue.1585')
venue = schedule.DbRecord.fetch('venue.1585')
assert venue.name == 'Exhibit Hall B'

View File

@@ -0,0 +1,10 @@
# pseudo-code for object construction
def object_maker(the_class, some_arg):
new_object = the_class.__new__(some_arg)
if isinstance(new_object, the_class):
the_class.__init__(new_object, some_arg)
return new_object
# the following statements are roughly equivalent
x = Foo('bar')
x = object_maker(Foo, 'bar')