updated from Atlas

This commit is contained in:
Luciano Ramalho
2015-04-15 04:48:25 -03:00
parent f4cdee2447
commit f86aee2785
78 changed files with 587 additions and 333 deletions

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

View File

@@ -0,0 +1,37 @@
"""
A line item for a bulk food order has description, weight and price fields.
A ``subtotal`` method gives the total price for that line item::
>>> raisins = LineItem('Golden raisins', 10, 6.95)
>>> raisins.weight, raisins.description, raisins.price
(10, 'Golden raisins', 6.95)
>>> raisins.subtotal()
69.5
But, without validation, these public attributes can cause trouble::
# BEGIN LINEITEM_PROBLEM_V1
>>> raisins = LineItem('Golden raisins', 10, 6.95)
>>> raisins.subtotal()
69.5
>>> raisins.weight = -20 # garbage in...
>>> raisins.subtotal() # garbage out...
-139.0
# END LINEITEM_PROBLEM_V1
"""
# BEGIN LINEITEM_V1
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
# END LINEITEM_V1

View File

@@ -0,0 +1,63 @@
"""
A line item for a bulk food order has description, weight and price fields::
>>> raisins = LineItem('Golden raisins', 10, 6.95)
>>> raisins.weight, raisins.description, raisins.price
(10, 'Golden raisins', 6.95)
A ``subtotal`` method gives the total price for that line item::
>>> raisins.subtotal()
69.5
The weight of a ``LineItem`` must be greater than 0::
>>> raisins.weight = -20
Traceback (most recent call last):
...
ValueError: value must be > 0
No change was made::
>>> raisins.weight
10
The check is also performed on instantiation::
>>> walnuts = LineItem('walnuts', 0, 10.00)
Traceback (most recent call last):
...
ValueError: value must be > 0
The proteced attribute can still be accessed if needed for some reason, such as
white box testing)::
>>> raisins._LineItem__weight
10
"""
# BEGIN LINEITEM_V2
class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight # <1>
self.price = price
def subtotal(self):
return self.weight * self.price
@property # <2>
def weight(self): # <3>
return self.__weight # <4>
@weight.setter # <5>
def weight(self, value):
if value > 0:
self.__weight = value # <6>
else:
raise ValueError('value must be > 0') # <7>
# END LINEITEM_V2

View File

@@ -0,0 +1,64 @@
"""
A line item for a bulk food order has description, weight and price fields::
>>> raisins = LineItem('Golden raisins', 10, 6.95)
>>> raisins.weight, raisins.description, raisins.price
(10, 'Golden raisins', 6.95)
A ``subtotal`` method gives the total price for that line item::
>>> raisins.subtotal()
69.5
The weight of a ``LineItem`` must be greater than 0::
>>> raisins.weight = -20
Traceback (most recent call last):
...
ValueError: value must be > 0
No change was made::
>>> raisins.weight
10
The check is also performed on instantiation::
>>> walnuts = LineItem('walnuts', 0, 10.00)
Traceback (most recent call last):
...
ValueError: value must be > 0
The proteced attribute can still be accessed if needed for some reason, such as
white box testing)::
>>> raisins._LineItem__weight
10
"""
# BEGIN LINEITEM_V2B
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): # <1>
return self.__weight
def set_weight(self, value): # <2>
if value > 0:
self.__weight = value
else:
raise ValueError('value must be > 0')
weight = property(get_weight, set_weight) # <3>
# END LINEITEM_V2B

View File

@@ -0,0 +1,69 @@
"""
A line item for a bulk food order has description, weight and price fields::
>>> raisins = LineItem('Golden raisins', 10, 6.95)
>>> raisins.weight, raisins.description, raisins.price
(10, 'Golden raisins', 6.95)
A ``subtotal`` method gives the total price for that line item::
>>> raisins.subtotal()
69.5
The weight of a ``LineItem`` must be greater than 0::
>>> raisins.weight = -20
Traceback (most recent call last):
...
ValueError: value must be > 0
No change was made::
>>> raisins.weight
10
The value of the attributes managed by the properties are stored in
instance attributes, created in each ``LineItem`` instance::
# BEGIN LINEITEM_V2_PROP_DEMO
>>> nutmeg = LineItem('Moluccan nutmeg', 8, 13.95)
>>> nutmeg.weight, nutmeg.price # <1>
(8, 13.95)
>>> sorted(vars(nutmeg).items()) # <2>
[('description', 'Moluccan nutmeg'), ('price', 13.95), ('weight', 8)]
# END LINEITEM_V2_PROP_DEMO
"""
# BEGIN LINEITEM_V2_PROP_FACTORY_FUNCTION
def quantity(storage_name): # <1>
def qty_getter(instance): # <2>
return instance.__dict__[storage_name] # <3>
def qty_setter(instance, value): # <4>
if value > 0:
instance.__dict__[storage_name] = value # <5>
else:
raise ValueError('value must be > 0')
return property(qty_getter, qty_setter) # <6>
# END LINEITEM_V2_PROP_FACTORY_FUNCTION
# BEGIN LINEITEM_V2_PROP_CLASS
class LineItem:
weight = quantity('weight') # <1>
price = quantity('price') # <2>
def __init__(self, description, weight, price):
self.description = description
self.weight = weight # <3>
self.price = price
def subtotal(self):
return self.weight * self.price # <4>
# END LINEITEM_V2_PROP_CLASS

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
"""
explore0.py: Script to explore the OSCON schedule feed
# BEGIN EXPLORE0_DEMO
>>> from osconfeed import load
>>> raw_feed = load()
>>> 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()): # <4>
... print('{:3} {}'.format(len(value), key))
...
1 conferences
484 events
357 speakers
53 venues
>>> feed.Schedule.speakers[-1].name # <5>
'Carina C. Zona'
>>> talk = feed.Schedule.events[40]
>>> type(talk) # <6>
<class 'explore0.FrozenJSON'>
>>> talk.name
'There *Will* Be Bugs'
>>> talk.speakers # <7>
[3471, 5199]
>>> talk.flavor # <8>
Traceback (most recent call last):
...
KeyError: 'flavor'
# END EXPLORE0_DEMO
"""
# BEGIN EXPLORE0
from collections import abc
class FrozenJSON:
"""A read-only façade 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
# END EXPLORE0

View File

@@ -0,0 +1,78 @@
"""
explore1.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'
>>> talk = feed.Schedule.events[40]
>>> type(talk)
<class 'explore1.FrozenJSON'>
>>> talk.name
'There *Will* Be Bugs'
>>> talk.speakers
[3471, 5199]
>>> talk.flavor
Traceback (most recent call last):
...
KeyError: 'flavor'
Handle keywords by appending a `_`.
# BEGIN EXPLORE1_DEMO
>>> grad = FrozenJSON({'name': 'Jim Bo', 'class': 1982})
>>> grad.name
'Jim Bo'
>>> grad.class_
1991
# END EXPLORE1_DEMO
"""
from collections import abc
import keyword
class FrozenJSON:
"""A read-only façade for navigating a JSON-like object
using attribute notation
"""
# BEGIN EXPLORE1
def __init__(self, mapping):
self.__data = {}
for key, value in mapping.items():
if keyword.iskeyword(key): # <1>
key += '_'
self.__data[key] = value
# END EXPLORE1
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: # <8>
return obj

View File

@@ -0,0 +1,54 @@
"""
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']
>>> 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'
"""
# BEGIN EXPLORE2
from collections import abc
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 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

View File

@@ -0,0 +1,32 @@
{ "Schedule":
{ "conferences": [{"serial": 115 }],
"events": [
{ "serial": 34505,
"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...",
"website_url": "http://oscon.com/oscon2014/public/schedule/detail/34505",
"speakers": [157509],
"categories": ["Education"] }
],
"speakers": [
{ "serial": 157509,
"name": "Robert Lefkowitz",
"photo": null,
"url": "http://sharewave.com/",
"position": "CTO",
"affiliation": "Sharewave",
"twitter": "sharewaveteam",
"bio": "Robert ´r0ml´ Lefkowitz is the CTO at Sharewave, a startup..." }
],
"venues": [
{ "serial": 1462,
"name": "F151",
"category": "Conference Venues" }
]
}
}

View File

@@ -0,0 +1,49 @@
"""
osconfeed.py: Script to download the OSCON schedule feed
# 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)) # <3>
...
1 conferences
484 events
357 speakers
53 venues
>>> feed['Schedule']['speakers'][-1]['name'] # <4>
'Carina C. Zona'
>>> feed['Schedule']['speakers'][-1]['serial'] # <5>
141590
>>> feed['Schedule']['events'][40]['name']
'There *Will* Be Bugs'
>>> 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 = 'data/osconfeed.json'
def load():
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) as fp:
return json.load(fp) # <3>
# END OSCONFEED

View File

@@ -0,0 +1,45 @@
"""
schedule1.py: traversing OSCON schedule data
# BEGIN SCHEDULE1_DEMO
>>> import shelve
>>> 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, speaker.twitter # <6>
('Anna Martelli Ravenscroft', 'annaraven')
>>> db.close() # <7>
# END SCHEDULE1_DEMO
"""
# BEGIN SCHEDULE1
import warnings
import osconfeed # <1>
DB_NAME = 'data/schedule1_db'
CONFERENCE = 'conference.115'
class Record:
def __init__(self, **kwargs):
self.__dict__.update(kwargs) # <2>
def load_db(db):
raw_data = osconfeed.load() # <3>
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['Schedule'].items(): # <4>
record_type = collection[:-1] # <5>
for record in rec_list:
key = '{}.{}'.format(record_type, record['serial']) # <6>
record['serial'] = key # <7>
db[key] = Record(**record) # <8>
# END SCHEDULE1

View File

@@ -0,0 +1,132 @@
"""
schedule2.py: traversing OSCON schedule data
>>> import shelve
>>> db = shelve.open(DB_NAME)
>>> if CONFERENCE not in db: load_db(db)
# BEGIN SCHEDULE2_DEMO
>>> DbRecord.set_db(db) # <1>
>>> event = DbRecord.fetch('event.33950') # <2>
>>> event # <3>
<Event 'There *Will* Be Bugs'>
>>> 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 # <1>
import osconfeed
DB_NAME = 'data/schedule2_db' # <2>
CONFERENCE = 'conference.115'
class Record:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
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, 'serial'): # <11>
cls_name = self.__class__.__name__
return '<{} serial={!r}>'.format(cls_name, self.serial)
else:
return super().__repr__() # <12>
# END SCHEDULE2_DBRECORD
# BEGIN SCHEDULE2_EVENT
class Event(DbRecord): # <1>
@property
def venue(self):
key = 'venue.{}'.format(self.venue_serial)
return self.__class__.fetch(key) # <2>
@property
def speakers(self):
if not hasattr(self, '_speaker_objs'): # <3>
spkr_serials = self.__dict__['speakers'] # <4>
fetch = self.__class__.fetch # <5>
self._speaker_objs = [fetch('speaker.{}'.format(key))
for key in spkr_serials] # <6>
return self._speaker_objs # <7>
def __repr__(self):
if hasattr(self, 'name'): # <8>
cls_name = self.__class__.__name__
return '<{} {!r}>'.format(cls_name, self.name)
else:
return super().__repr__() # <9>
# 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():
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) # <8>
# END SCHEDULE2_LOAD