Update TMDB api
This commit is contained in:
+119
-82
@@ -10,13 +10,15 @@ from copy import copy
|
||||
from locales import get_locale
|
||||
from tmdb_auth import get_session
|
||||
|
||||
class NameRepr( object ):
|
||||
|
||||
class NameRepr(object):
|
||||
"""Mixin for __repr__ methods using 'name' attribute."""
|
||||
def __repr__(self):
|
||||
return u"<{0.__class__.__name__} '{0.name}'>"\
|
||||
.format(self).encode('utf-8')
|
||||
.format(self).encode('utf-8')
|
||||
|
||||
class SearchRepr( object ):
|
||||
|
||||
class SearchRepr(object):
|
||||
"""
|
||||
Mixin for __repr__ methods for classes with '_name' and
|
||||
'_request' attributes.
|
||||
@@ -25,10 +27,11 @@ class SearchRepr( object ):
|
||||
name = self._name if self._name else self._request._kwargs['query']
|
||||
return u"<Search Results: {0}>".format(name).encode('utf-8')
|
||||
|
||||
class Poller( object ):
|
||||
|
||||
class Poller(object):
|
||||
"""
|
||||
Wrapper for an optional callable to populate an Element derived class
|
||||
with raw data, or data from a Request.
|
||||
Wrapper for an optional callable to populate an Element derived
|
||||
class with raw data, or data from a Request.
|
||||
"""
|
||||
def __init__(self, func, lookup, inst=None):
|
||||
self.func = func
|
||||
@@ -60,7 +63,7 @@ class Poller( object ):
|
||||
if not callable(self.func):
|
||||
raise RuntimeError('Poller object called without a source function')
|
||||
req = self.func()
|
||||
if (('language' in req._kwargs) or ('country' in req._kwargs)) \
|
||||
if ('language' in req._kwargs) or ('country' in req._kwargs) \
|
||||
and self.inst._locale.fallthrough:
|
||||
# request specifies a locale filter, and fallthrough is enabled
|
||||
# run a first pass with specified filter
|
||||
@@ -79,7 +82,7 @@ class Poller( object ):
|
||||
def apply(self, data, set_nones=True):
|
||||
# apply data directly, bypassing callable function
|
||||
unfilled = False
|
||||
for k,v in self.lookup.items():
|
||||
for k, v in self.lookup.items():
|
||||
if (k in data) and \
|
||||
((data[k] is not None) if callable(self.func) else True):
|
||||
# argument received data, populate it
|
||||
@@ -100,32 +103,38 @@ class Poller( object ):
|
||||
unfilled = True
|
||||
return unfilled
|
||||
|
||||
class Data( object ):
|
||||
|
||||
class Data(object):
|
||||
"""
|
||||
Basic response definition class
|
||||
This maps to a single key in a JSON dictionary received from the API
|
||||
"""
|
||||
def __init__(self, field, initarg=None, handler=None, poller=None,
|
||||
raw=True, default=u'', lang=False):
|
||||
raw=True, default=u'', lang=None, passthrough={}):
|
||||
"""
|
||||
This defines how the dictionary value is to be processed by the poller
|
||||
field -- defines the dictionary key that filters what data this uses
|
||||
initarg -- (optional) specifies that this field must be supplied
|
||||
when creating a new instance of the Element class this
|
||||
definition is mapped to. Takes an integer for the order
|
||||
it should be used in the input arguments
|
||||
handler -- (optional) callable used to process the received value
|
||||
before being stored in the Element object.
|
||||
poller -- (optional) callable to be used if data is requested and
|
||||
this value has not yet been defined. the callable should
|
||||
return a dictionary of data from a JSON query. many
|
||||
definitions may share a single poller, which will be
|
||||
and the data used to populate all referenced definitions
|
||||
based off their defined field
|
||||
raw -- (optional) if the specified handler is an Element class,
|
||||
the data will be passed into it using the 'raw' keyword
|
||||
attribute. setting this to false will force the data to
|
||||
instead be passed in as the first argument
|
||||
This defines how the dictionary value is to be processed by the
|
||||
poller
|
||||
field -- defines the dictionary key that filters what data
|
||||
this uses
|
||||
initarg -- (optional) specifies that this field must be
|
||||
supplied when creating a new instance of the Element
|
||||
class this definition is mapped to. Takes an integer
|
||||
for the order it should be used in the input
|
||||
arguments
|
||||
handler -- (optional) callable used to process the received
|
||||
value before being stored in the Element object.
|
||||
poller -- (optional) callable to be used if data is requested
|
||||
and this value has not yet been defined. the
|
||||
callable should return a dictionary of data from a
|
||||
JSON query. many definitions may share a single
|
||||
poller, which will be and the data used to populate
|
||||
all referenced definitions based off their defined
|
||||
field
|
||||
raw -- (optional) if the specified handler is an Element
|
||||
class, the data will be passed into it using the
|
||||
'raw' keyword attribute. setting this to false
|
||||
will force the data to instead be passed in as the
|
||||
first argument
|
||||
"""
|
||||
self.field = field
|
||||
self.initarg = initarg
|
||||
@@ -133,6 +142,7 @@ class Data( object ):
|
||||
self.raw = raw
|
||||
self.default = default
|
||||
self.sethandler(handler)
|
||||
self.passthrough = passthrough
|
||||
|
||||
def __get__(self, inst, owner):
|
||||
if inst is None:
|
||||
@@ -151,6 +161,9 @@ class Data( object ):
|
||||
if isinstance(value, Element):
|
||||
value._locale = inst._locale
|
||||
value._session = inst._session
|
||||
|
||||
for source, dest in self.passthrough:
|
||||
setattr(value, dest, getattr(inst, source))
|
||||
inst._data[self.field] = value
|
||||
|
||||
def sethandler(self, handler):
|
||||
@@ -162,37 +175,44 @@ class Data( object ):
|
||||
else:
|
||||
self.handler = lambda x: handler(x)
|
||||
|
||||
class Datapoint( Data ):
|
||||
|
||||
class Datapoint(Data):
|
||||
pass
|
||||
|
||||
class Datalist( Data ):
|
||||
|
||||
class Datalist(Data):
|
||||
"""
|
||||
Response definition class for list data
|
||||
This maps to a key in a JSON dictionary storing a list of data
|
||||
"""
|
||||
def __init__(self, field, handler=None, poller=None, sort=None, raw=True):
|
||||
def __init__(self, field, handler=None, poller=None, sort=None, raw=True, passthrough={}):
|
||||
"""
|
||||
This defines how the dictionary value is to be processed by the poller
|
||||
field -- defines the dictionary key that filters what data this uses
|
||||
handler -- (optional) callable used to process the received value
|
||||
before being stored in the Element object.
|
||||
poller -- (optional) callable to be used if data is requested and
|
||||
this value has not yet been defined. the callable should
|
||||
return a dictionary of data from a JSON query. many
|
||||
definitions may share a single poller, which will be
|
||||
and the data used to populate all referenced definitions
|
||||
based off their defined field
|
||||
sort -- (optional) name of attribute in resultant data to be used
|
||||
to sort the list after processing. this effectively
|
||||
a handler be defined to process the data into something
|
||||
that has attributes
|
||||
raw -- (optional) if the specified handler is an Element class,
|
||||
the data will be passed into it using the 'raw' keyword
|
||||
attribute. setting this to false will force the data to
|
||||
instead be passed in as the first argument
|
||||
This defines how the dictionary value is to be processed by the
|
||||
poller
|
||||
field -- defines the dictionary key that filters what data
|
||||
this uses
|
||||
handler -- (optional) callable used to process the received
|
||||
value before being stored in the Element object.
|
||||
poller -- (optional) callable to be used if data is requested
|
||||
and this value has not yet been defined. the
|
||||
callable should return a dictionary of data from a
|
||||
JSON query. many definitions may share a single
|
||||
poller, which will be and the data used to populate
|
||||
all referenced definitions based off their defined
|
||||
field
|
||||
sort -- (optional) name of attribute in resultant data to be
|
||||
used to sort the list after processing. this
|
||||
effectively requires a handler be defined to process
|
||||
the data into something that has attributes
|
||||
raw -- (optional) if the specified handler is an Element
|
||||
class, the data will be passed into it using the
|
||||
'raw' keyword attribute. setting this to false will
|
||||
force the data to instead be passed in as the first
|
||||
argument
|
||||
"""
|
||||
super(Datalist, self).__init__(field, None, handler, poller, raw)
|
||||
super(Datalist, self).__init__(field, None, handler, poller, raw, passthrough=passthrough)
|
||||
self.sort = sort
|
||||
|
||||
def __set__(self, inst, value):
|
||||
data = []
|
||||
if value:
|
||||
@@ -201,6 +221,10 @@ class Datalist( Data ):
|
||||
if isinstance(val, Element):
|
||||
val._locale = inst._locale
|
||||
val._session = inst._session
|
||||
|
||||
for source, dest in self.passthrough.items():
|
||||
setattr(val, dest, getattr(inst, source))
|
||||
|
||||
data.append(val)
|
||||
if self.sort:
|
||||
if self.sort is True:
|
||||
@@ -209,45 +233,52 @@ class Datalist( Data ):
|
||||
data.sort(key=lambda x: getattr(x, self.sort))
|
||||
inst._data[self.field] = data
|
||||
|
||||
class Datadict( Data ):
|
||||
|
||||
class Datadict(Data):
|
||||
"""
|
||||
Response definition class for dictionary data
|
||||
This maps to a key in a JSON dictionary storing a dictionary of data
|
||||
"""
|
||||
def __init__(self, field, handler=None, poller=None, raw=True,
|
||||
key=None, attr=None):
|
||||
key=None, attr=None, passthrough={}):
|
||||
"""
|
||||
This defines how the dictionary value is to be processed by the poller
|
||||
field -- defines the dictionary key that filters what data this uses
|
||||
handler -- (optional) callable used to process the received value
|
||||
before being stored in the Element object.
|
||||
poller -- (optional) callable to be used if data is requested and
|
||||
this value has not yet been defined. the callable should
|
||||
return a dictionary of data from a JSON query. many
|
||||
definitions may share a single poller, which will be
|
||||
and the data used to populate all referenced definitions
|
||||
based off their defined field
|
||||
key -- (optional) name of key in resultant data to be used as
|
||||
the key in the stored dictionary. if this is not the
|
||||
field name from the source data is used instead
|
||||
attr -- (optional) name of attribute in resultant data to be used
|
||||
This defines how the dictionary value is to be processed by the
|
||||
poller
|
||||
field -- defines the dictionary key that filters what data
|
||||
this uses
|
||||
handler -- (optional) callable used to process the received
|
||||
value before being stored in the Element object.
|
||||
poller -- (optional) callable to be used if data is requested
|
||||
and this value has not yet been defined. the
|
||||
callable should return a dictionary of data from a
|
||||
JSON query. many definitions may share a single
|
||||
poller, which will be and the data used to populate
|
||||
all referenced definitions based off their defined
|
||||
field
|
||||
key -- (optional) name of key in resultant data to be used
|
||||
as the key in the stored dictionary. if this is not
|
||||
the field name from the source data is used instead
|
||||
raw -- (optional) if the specified handler is an Element class,
|
||||
the data will be passed into it using the 'raw' keyword
|
||||
attribute. setting this to false will force the data to
|
||||
instead be passed in as the first argument
|
||||
attr -- (optional) name of attribute in resultant data to be
|
||||
used as the key in the stored dictionary. if this is
|
||||
not the field name from the source data is used
|
||||
instead
|
||||
raw -- (optional) if the specified handler is an Element
|
||||
class, the data will be passed into it using the
|
||||
'raw' keyword attribute. setting this to false will
|
||||
force the data to instead be passed in as the first
|
||||
argument
|
||||
"""
|
||||
if key and attr:
|
||||
raise TypeError("`key` and `attr` cannot both be defined")
|
||||
super(Datadict, self).__init__(field, None, handler, poller, raw)
|
||||
super(Datadict, self).__init__(field, None, handler, poller, raw, passthrough=passthrough)
|
||||
if key:
|
||||
self.getkey = lambda x: x[key]
|
||||
elif attr:
|
||||
self.getkey = lambda x: getattr(x, attr)
|
||||
else:
|
||||
raise TypeError("Datadict requires `key` or `attr` be defined "+\
|
||||
raise TypeError("Datadict requires `key` or `attr` be defined " +
|
||||
"for populating the dictionary")
|
||||
|
||||
def __set__(self, inst, value):
|
||||
data = {}
|
||||
if value:
|
||||
@@ -256,6 +287,10 @@ class Datadict( Data ):
|
||||
if isinstance(val, Element):
|
||||
val._locale = inst._locale
|
||||
val._session = inst._session
|
||||
|
||||
for source, dest in self.passthrough.items():
|
||||
setattr(val, dest, getattr(inst, source))
|
||||
|
||||
data[self.getkey(val)] = val
|
||||
inst._data[self.field] = data
|
||||
|
||||
@@ -286,7 +321,7 @@ class ElementType( type ):
|
||||
# extract copies of each defined Poller function
|
||||
# from parent classes
|
||||
pollers[k] = attr.func
|
||||
for k,attr in attrs.items():
|
||||
for k, attr in attrs.items():
|
||||
if isinstance(attr, Data):
|
||||
data[k] = attr
|
||||
if '_populate' in attrs:
|
||||
@@ -295,9 +330,9 @@ class ElementType( type ):
|
||||
# process all defined Data attribues, testing for use as an initial
|
||||
# argument, and building a list of what Pollers are used to populate
|
||||
# which Data points
|
||||
pollermap = dict([(k,[]) for k in pollers])
|
||||
pollermap = dict([(k, []) for k in pollers])
|
||||
initargs = []
|
||||
for k,v in data.items():
|
||||
for k, v in data.items():
|
||||
v.name = k
|
||||
if v.initarg:
|
||||
initargs.append(v)
|
||||
@@ -313,7 +348,7 @@ class ElementType( type ):
|
||||
|
||||
# wrap each used poller function with a Poller class, and push into
|
||||
# the new class attributes
|
||||
for k,v in pollermap.items():
|
||||
for k, v in pollermap.items():
|
||||
if len(v) == 0:
|
||||
continue
|
||||
lookup = dict([(attr.field, attr.name) for attr in v])
|
||||
@@ -326,8 +361,8 @@ class ElementType( type ):
|
||||
attrs[attr.name] = attr
|
||||
|
||||
# build sorted list of arguments used for intialization
|
||||
attrs['_InitArgs'] = tuple([a.name for a in \
|
||||
sorted(initargs, key=lambda x: x.initarg)])
|
||||
attrs['_InitArgs'] = tuple(
|
||||
[a.name for a in sorted(initargs, key=lambda x: x.initarg)])
|
||||
return type.__new__(mcs, name, bases, attrs)
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
@@ -346,21 +381,23 @@ class ElementType( type ):
|
||||
if 'raw' in kwargs:
|
||||
# if 'raw' keyword is supplied, create populate object manually
|
||||
if len(args) != 0:
|
||||
raise TypeError('__init__() takes exactly 2 arguments (1 given)')
|
||||
raise TypeError(
|
||||
'__init__() takes exactly 2 arguments (1 given)')
|
||||
obj._populate.apply(kwargs['raw'], False)
|
||||
else:
|
||||
# if not, the number of input arguments must exactly match that
|
||||
# defined by the Data definitions
|
||||
if len(args) != len(cls._InitArgs):
|
||||
raise TypeError('__init__() takes exactly {0} arguments ({1} given)'\
|
||||
raise TypeError(
|
||||
'__init__() takes exactly {0} arguments ({1} given)'\
|
||||
.format(len(cls._InitArgs)+1, len(args)+1))
|
||||
for a,v in zip(cls._InitArgs, args):
|
||||
for a, v in zip(cls._InitArgs, args):
|
||||
setattr(obj, a, v)
|
||||
|
||||
obj.__init__()
|
||||
return obj
|
||||
|
||||
|
||||
class Element( object ):
|
||||
__metaclass__ = ElementType
|
||||
_lang = 'en'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user