capabilities objets inherit from CapBaseObject (refs #369)
This commit is contained in:
parent
33d1574878
commit
e980e040ba
20 changed files with 126 additions and 102 deletions
|
|
@ -35,14 +35,14 @@ class Boobank(ConsoleApplication):
|
|||
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
|
||||
|
||||
def main(self, argv):
|
||||
self.load_configured_backends(ICapBank)
|
||||
return self.process_command(*argv[1:])
|
||||
|
||||
@ConsoleApplication.command('List every available accounts')
|
||||
def command_list(self):
|
||||
self.load_configured_backends(ICapBank)
|
||||
try:
|
||||
for backend, account in self.do('iter_accounts'):
|
||||
self.format(account, backend.name)
|
||||
self.format(account)
|
||||
except weboob.core.CallErrors, errors:
|
||||
for backend, error, backtrace in errors:
|
||||
if isinstance(error, weboob.tools.browser.BrowserIncorrectPassword):
|
||||
|
|
@ -52,19 +52,13 @@ class Boobank(ConsoleApplication):
|
|||
|
||||
@ConsoleApplication.command('Display all future operations')
|
||||
def command_coming(self, id):
|
||||
total = 0.0
|
||||
id, backend_name = self.parse_id(id)
|
||||
names = (backend_name,) if backend_name is not None else None
|
||||
self.load_configured_backends(ICapBank, names=names)
|
||||
|
||||
def do(backend):
|
||||
account = backend.get_account(id)
|
||||
return backend.iter_operations(account)
|
||||
|
||||
try:
|
||||
for backend, operation in self.do(do):
|
||||
self.format(operation, backend.name)
|
||||
total += operation.amount
|
||||
except weboob.core.CallErrors, errors:
|
||||
for backend, error, backtrace in errors:
|
||||
if isinstance(error, AccountNotFound):
|
||||
logging.error(u'Error: account %s not found' % id)
|
||||
else:
|
||||
logging.error(u'Error[%s]: %s\n%s' % (backend.name, error, backtrace))
|
||||
for backend, operation in self.do(do):
|
||||
self.format(operation)
|
||||
|
|
|
|||
|
|
@ -47,12 +47,12 @@ class Chatoob(ConsoleApplication):
|
|||
@ConsoleApplication.command('list online contacts')
|
||||
def command_list(self):
|
||||
for backend, contact in self.do('iter_contacts', status=Contact.STATUS_ONLINE, caps=ICapContact):
|
||||
self.format(contact, backend.name)
|
||||
self.format(contact)
|
||||
|
||||
@ConsoleApplication.command('get messages')
|
||||
def command_messages(self):
|
||||
for backend, message in self.do('iter_chat_messages'):
|
||||
self.format(message, backend.name)
|
||||
self.format(message)
|
||||
|
||||
@ConsoleApplication.command('send message to contact')
|
||||
def command_send(self, _id, message):
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ class Geolooc(ConsoleApplication):
|
|||
|
||||
self.load_configured_backends(ICapGeolocIp)
|
||||
for backend, location in self.do('get_location', argv[1]):
|
||||
self.format(location, backend.name)
|
||||
self.format(location)
|
||||
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Traveloob(ConsoleApplication):
|
|||
def command_stations(self, pattern):
|
||||
self.load_backends(ICapTravel)
|
||||
for backend, station in self.do('iter_station_search', pattern):
|
||||
self.format(station, backend.name)
|
||||
self.format(station)
|
||||
|
||||
@ConsoleApplication.command('List all departures for a given station')
|
||||
def command_departures(self, station, arrival=None):
|
||||
|
|
@ -59,4 +59,4 @@ class Traveloob(ConsoleApplication):
|
|||
|
||||
self.load_backends(ICapTravel, names=backends)
|
||||
for backend, departure in self.do('iter_station_departures', station_id, arrival_id):
|
||||
self.format(departure, backend.name)
|
||||
self.format(departure)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class Videoob(ConsoleApplication):
|
|||
for backend, video in self.do('get_video', _id):
|
||||
if video is None:
|
||||
continue
|
||||
self.format(video, backend.name)
|
||||
self.format(video)
|
||||
|
||||
@ConsoleApplication.command('Search for videos')
|
||||
def command_search(self, pattern=None):
|
||||
|
|
@ -53,4 +53,4 @@ class Videoob(ConsoleApplication):
|
|||
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Latest videos')
|
||||
for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw,
|
||||
max_results=self.options.count):
|
||||
self.format(video, backend.name)
|
||||
self.format(video)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,6 @@ class WeboobCli(ConsoleApplication):
|
|||
self.load_backends(cap_s)
|
||||
|
||||
for backend, obj in self.do(cmd, *args):
|
||||
self.format(obj, backend.name)
|
||||
self.format(obj)
|
||||
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Weboorrents(ConsoleApplication):
|
|||
found = 0
|
||||
for backend, torrent in self.do('get_torrent', _id, backends=backend_name):
|
||||
if torrent:
|
||||
self.format(torrent, backend.name)
|
||||
self.format(torrent)
|
||||
found = 1
|
||||
|
||||
if not found:
|
||||
|
|
@ -67,4 +67,4 @@ class Weboorrents(ConsoleApplication):
|
|||
def command_search(self, pattern=None):
|
||||
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Latest torrents')
|
||||
for backend, torrent in self.do('iter_torrents', pattern=pattern):
|
||||
self.format(torrent, backend.name)
|
||||
self.format(torrent)
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ class WetBoobs(ConsoleApplication):
|
|||
@ConsoleApplication.command('search cities')
|
||||
def command_search(self, pattern):
|
||||
for backend, city in self.do('iter_city_search', pattern):
|
||||
self.format(city, backend.name)
|
||||
self.format(city)
|
||||
|
||||
@ConsoleApplication.command('get current weather')
|
||||
def command_current(self, city):
|
||||
try:
|
||||
for backend, current in self.do('get_current', city):
|
||||
self.format(current, backend.name)
|
||||
self.format(current)
|
||||
except CallErrors, e:
|
||||
for error in e:
|
||||
if isinstance(error, CityNotFound):
|
||||
|
|
@ -57,7 +57,7 @@ class WetBoobs(ConsoleApplication):
|
|||
def command_forecasts(self, city):
|
||||
try:
|
||||
for backend, forecast in self.do('iter_forecast', city):
|
||||
self.format(forecast, backend.name)
|
||||
self.format(forecast)
|
||||
except CallErrors, e:
|
||||
for error in e:
|
||||
if isinstance(error, CityNotFound):
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ if sys.version_info[:2] <= (2, 5):
|
|||
from weboob.tools.property import property
|
||||
|
||||
|
||||
from .base import IBaseCap
|
||||
from .base import IBaseCap, CapBaseObject
|
||||
|
||||
|
||||
__all__ = ['Account', 'AccountNotFound', 'ICapBank', 'Operation']
|
||||
|
|
@ -31,9 +31,10 @@ class AccountNotFound(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class Account(object):
|
||||
class Account(CapBaseObject):
|
||||
FIELDS = ('label', 'balance', 'coming')
|
||||
def __init__(self):
|
||||
self.id = 0
|
||||
CapBaseObject.__init__(self, 0)
|
||||
self.label = ''
|
||||
self._balance = 0.0
|
||||
self._coming = 0.0
|
||||
|
|
@ -59,8 +60,10 @@ class Account(object):
|
|||
return u"<Account id='%s' label='%s'>" % (self.id, self.label)
|
||||
|
||||
|
||||
class Operation(object):
|
||||
class Operation(CapBaseObject):
|
||||
FIELDS = ('date', 'label', 'amount')
|
||||
def __init__(self):
|
||||
CapBaseObject(self, 0)
|
||||
self.date = None
|
||||
self._label = u''
|
||||
self._amount = 0.0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright(C) 2010 Christophe Benz
|
||||
# Copyright(C) 2010 Christophe Benz, Romain Bignon
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -16,7 +16,9 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
__all__ = ['IBaseCap', 'NotLoaded', 'LoadingError']
|
||||
from weboob.tools.misc import iter_fields
|
||||
|
||||
__all__ = ['IBaseCap', 'NotLoaded', 'LoadingError', 'CapBaseObject']
|
||||
|
||||
|
||||
class NotLoadedMeta(type):
|
||||
|
|
@ -45,3 +47,20 @@ class LoadingError(object):
|
|||
|
||||
class IBaseCap(object):
|
||||
pass
|
||||
|
||||
class CapBaseObject(object):
|
||||
FIELDS = None
|
||||
|
||||
def __init__(self, id, backend=None):
|
||||
self.id = id
|
||||
self.backend = backend
|
||||
|
||||
def iter_fields(self):
|
||||
if self.FIELDS is None:
|
||||
for key, value in iter_fields(self):
|
||||
if key != 'backend':
|
||||
yield key, value
|
||||
else:
|
||||
yield 'id', self.id
|
||||
for attrstr in self.FIELDS:
|
||||
yield attrstr, getattr(self, attrstr)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import datetime
|
||||
|
||||
from .base import IBaseCap
|
||||
from .base import IBaseCap, CapBaseObject
|
||||
|
||||
|
||||
__all__ = ['ChatException', 'ICapChat']
|
||||
|
|
@ -28,8 +28,11 @@ class ChatException(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class ChatMessage(object):
|
||||
def __init__(self, id_from, id_to, message, date=None):
|
||||
class ChatMessage(CapBaseObject):
|
||||
FIELDS = ('id_from', 'id_to', 'date', 'message')
|
||||
|
||||
def __init__(self, id_from, id_to, message, date=None):
|
||||
CapBaseObject.__init__(self, '%s.%s' % (id_from, id_to))
|
||||
self.id_from = id_from
|
||||
self.id_to = id_to
|
||||
self.message = message
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
from .base import IBaseCap
|
||||
from .base import IBaseCap, CapBaseObject
|
||||
from weboob.tools.ordereddict import OrderedDict
|
||||
|
||||
|
||||
|
|
@ -50,14 +50,16 @@ class ContactPhoto(object):
|
|||
def __repr__(self):
|
||||
return u'<ContactPhoto "%s" data=%do tndata=%do>' % (self.name, len(self.data), len(self.thumbnail_data))
|
||||
|
||||
class Contact(object):
|
||||
class Contact(CapBaseObject):
|
||||
FIELDS = ('name', 'status', 'status_msg', 'summary', 'avatar', 'photos', 'profile')
|
||||
|
||||
STATUS_ONLINE = 0x001
|
||||
STATUS_AWAY = 0x002
|
||||
STATUS_OFFLINE = 0x004
|
||||
STATUS_ALL = 0xfff
|
||||
|
||||
def __init__(self, id, name, status):
|
||||
self.id = id
|
||||
CapBaseObject.__init__(self, id)
|
||||
self.name = name
|
||||
self.status = status
|
||||
self.status_msg = u''
|
||||
|
|
@ -74,17 +76,6 @@ class Contact(object):
|
|||
for key, value in kwargs.iteritems():
|
||||
setattr(photo, key, value)
|
||||
|
||||
def iter_fields(self):
|
||||
return {'id': self.id,
|
||||
'name': self.name,
|
||||
'status': self.status,
|
||||
'status_msg': self.status_msg,
|
||||
'summary': self.summary,
|
||||
'avatar': self.avatar,
|
||||
'photos': self.photos,
|
||||
'profile': self.profile,
|
||||
}.iteritems()
|
||||
|
||||
class ICapContact(IBaseCap):
|
||||
def iter_contacts(self, status=Contact.STATUS_ALL, ids=None):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -16,13 +16,16 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
from .cap import ICap
|
||||
from .base import IBaseCap, CapBaseObject
|
||||
|
||||
|
||||
__all__ = ('IpLocation', 'ICapGeolocIp')
|
||||
|
||||
class IpLocation(object):
|
||||
class IpLocation(CapBaseObject):
|
||||
FIELDS = ('city', 'region', 'zipcode', 'country', 'lt', 'lg', 'host', 'tls', 'isp')
|
||||
def __init__(self, ipaddr):
|
||||
CapBaseObject.__init__(self, ipaddr)
|
||||
|
||||
self.ipaddr = ipaddr
|
||||
self.city = None
|
||||
self.region = None
|
||||
|
|
@ -34,6 +37,6 @@ class IpLocation(object):
|
|||
self.tld = None
|
||||
self.isp = None
|
||||
|
||||
class ICapGeolocIp(ICap):
|
||||
class ICapGeolocIp(IBaseCap):
|
||||
def get_location(self, ipaddr):
|
||||
raise NotImplementedError()
|
||||
|
|
|
|||
|
|
@ -16,15 +16,19 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
from .base import IBaseCap
|
||||
from .base import IBaseCap, CapBaseObject, NotLoaded
|
||||
|
||||
|
||||
__all__ = ['ICapTorrent', 'Torrent']
|
||||
|
||||
|
||||
class Torrent(object):
|
||||
def __init__(self, id, name, date=None, size=0.0, url=u'', seeders=0, leechers=0, files=[], description=u''):
|
||||
self.id = id
|
||||
class Torrent(CapBaseObject):
|
||||
FIELDS = ('name', 'size', 'date', 'url', 'seeders', 'leechers', 'files', 'description')
|
||||
|
||||
def __init__(self, id, name, date=NotLoaded, size=NotLoaded, url=NotLoaded,
|
||||
seeders=NotLoaded, leechers=NotLoaded, files=NotLoaded,
|
||||
description=NotLoaded):
|
||||
CapBaseObject.__init__(self, id)
|
||||
self.name = name
|
||||
self.date = date
|
||||
self.size = size
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
from datetime import time
|
||||
|
||||
from .base import IBaseCap
|
||||
from .base import IBaseCap, CapBaseObject
|
||||
|
||||
|
||||
__all__ = ['Departure', 'ICapTravel', 'Station']
|
||||
|
|
@ -45,18 +45,23 @@ class ICapTravel(IBaseCap):
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
class Station(object):
|
||||
def __init__(self, _id, name):
|
||||
self.id = _id
|
||||
class Station(CapBaseObject):
|
||||
FIELDS = ('name',)
|
||||
|
||||
def __init__(self, id, name):
|
||||
CapBaseObject.__init__(self, id)
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "<Station id='%s' name='%s'>" % (self.id, self.name)
|
||||
|
||||
|
||||
class Departure(object):
|
||||
def __init__(self, _id, _type, _time):
|
||||
self.id = _id
|
||||
class Departure(CapBaseObject):
|
||||
FIELDS = ('type', 'time', 'departure_station', 'arrival_station', 'late', 'information', 'plateform')
|
||||
|
||||
def __init__(self, id, _type, _time):
|
||||
CapBaseObject.__init__(self, id)
|
||||
|
||||
self.type = _type
|
||||
self.time = _time
|
||||
self.departure_station = u''
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
from .base import IBaseCap, LoadingError, NotLoaded
|
||||
from .base import IBaseCap, LoadingError, NotLoaded, CapBaseObject
|
||||
|
||||
|
||||
__all__ = ['BaseVideo', 'ICapVideo']
|
||||
|
|
@ -37,7 +37,9 @@ class VideoThumbnail(object):
|
|||
return self.data is not NotLoaded
|
||||
|
||||
|
||||
class BaseVideo(object):
|
||||
class BaseVideo(CapBaseObject):
|
||||
FIELDS = ('title', 'url', 'author', 'duration', 'date', 'rating', 'rating_max', 'thumbnail', 'nsfw')
|
||||
|
||||
def __init__(self, _id, title=NotLoaded, url=NotLoaded, author=NotLoaded, duration=NotLoaded, date=NotLoaded,
|
||||
rating=NotLoaded, rating_max=NotLoaded, thumbnail=NotLoaded, thumbnail_url=None, nsfw=False):
|
||||
self.id = unicode(_id)
|
||||
|
|
|
|||
|
|
@ -16,14 +16,15 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
from .base import IBaseCap
|
||||
from .base import IBaseCap, CapBaseObject
|
||||
|
||||
|
||||
__all__ = ['City', 'CityNotFound', 'Current', 'Forecast', 'ICapWeather']
|
||||
|
||||
|
||||
class Forecast(object):
|
||||
class Forecast(CapBaseObject):
|
||||
def __init__(self, date, low, high, text, unit):
|
||||
CapBaseObject.__init__(self, date)
|
||||
self.date = date
|
||||
self.low = low
|
||||
self.high = high
|
||||
|
|
@ -31,17 +32,18 @@ class Forecast(object):
|
|||
self.unit = unit
|
||||
|
||||
|
||||
class Current(object):
|
||||
class Current(CapBaseObject):
|
||||
def __init__(self, date, temp, text, unit):
|
||||
CapBaseObject.__init__(self, date)
|
||||
self.date = date
|
||||
self.temp = temp
|
||||
self.text = text
|
||||
self.unit = unit
|
||||
|
||||
|
||||
class City(object):
|
||||
def __init__(self, city_id, name):
|
||||
self.city_id = city_id
|
||||
class City(CapBaseObject):
|
||||
def __init__(self, id, name):
|
||||
self.id = id
|
||||
self.name = name
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ from copy import copy
|
|||
import logging
|
||||
from logging import debug
|
||||
from threading import Thread, Event, RLock, Timer
|
||||
|
||||
from weboob.capabilities.base import CapBaseObject
|
||||
from weboob.tools.misc import get_backtrace
|
||||
|
||||
|
||||
|
|
@ -80,6 +82,8 @@ class BackendsCall(object):
|
|||
|
||||
def _store_result(self, backend, result):
|
||||
with self.mutex:
|
||||
if isinstance(result, CapBaseObject):
|
||||
result.backend = backend.name
|
||||
self.responses.append((backend, result))
|
||||
self.response_event.set()
|
||||
|
||||
|
|
|
|||
|
|
@ -241,10 +241,9 @@ class ConsoleApplication(BaseApplication):
|
|||
def set_formatter_header(self, string):
|
||||
self.formatter.set_header(string)
|
||||
|
||||
def format(self, result, backend_name=None):
|
||||
def format(self, result):
|
||||
try:
|
||||
self.formatter.format(obj=result, backend_name=backend_name,
|
||||
selected_fields=self.selected_fields, condition=self.condition)
|
||||
self.formatter.format(obj=result, selected_fields=self.selected_fields, condition=self.condition)
|
||||
except FieldNotFound, e:
|
||||
logging.error(e)
|
||||
except ResultsConditionException, e:
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
from weboob.capabilities.base import CapBaseObject
|
||||
from weboob.tools.misc import iter_fields
|
||||
from weboob.tools.ordereddict import OrderedDict
|
||||
|
||||
|
|
@ -43,7 +44,7 @@ class IFormatter(object):
|
|||
def flush(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def format(self, obj, backend_name=None, selected_fields=None, condition=None):
|
||||
def format(self, obj, selected_fields=None, condition=None):
|
||||
"""
|
||||
Format an object to be human-readable.
|
||||
An object has fields which can be selected, and the objects
|
||||
|
|
@ -52,15 +53,17 @@ class IFormatter(object):
|
|||
call it. It can be used to specify the fields order.
|
||||
|
||||
@param obj [object] object to format
|
||||
@param backend_name [str] name of backend, used to create object ID
|
||||
@param selected_fields [tuple] fields to display. If None, all fields are selected
|
||||
@param condition [Condition] condition to objects to display
|
||||
@return a string of the formatted object
|
||||
"""
|
||||
assert isinstance(obj, (dict, CapBaseObject))
|
||||
|
||||
if isinstance(obj, dict):
|
||||
item = obj
|
||||
else:
|
||||
item = self.to_dict(obj, backend_name, condition, selected_fields)
|
||||
item = self.to_dict(obj, condition, selected_fields)
|
||||
|
||||
if item is None:
|
||||
return None
|
||||
formatted = self.format_dict(item=item)
|
||||
|
|
@ -82,33 +85,25 @@ class IFormatter(object):
|
|||
def set_header(self, string):
|
||||
raise NotImplementedError()
|
||||
|
||||
def to_dict(self, obj, backend_name=None, condition=None, selected_fields=None):
|
||||
def to_dict(self, obj, condition=None, selected_fields=None):
|
||||
def iter_select_and_decorate(d):
|
||||
if hasattr(obj, '__id__'):
|
||||
id_attr = getattr(obj, '__id__')
|
||||
if not isinstance(id_attr, (set, list, tuple)):
|
||||
id_attr = (id_attr,)
|
||||
id_fields = id_attr
|
||||
else:
|
||||
id_fields = ('id',)
|
||||
if selected_fields is None or '*' in selected_fields:
|
||||
for k, v in d:
|
||||
if k in id_fields and backend_name is not None:
|
||||
v = self.build_id(v, backend_name)
|
||||
yield k, v
|
||||
fields = d.iterkeys()
|
||||
else:
|
||||
d = dict(d)
|
||||
for selected_field in selected_fields:
|
||||
v = d[selected_field]
|
||||
if selected_field in id_fields and backend_name is not None:
|
||||
v = self.build_id(v, backend_name)
|
||||
try:
|
||||
yield selected_field, v
|
||||
except KeyError:
|
||||
raise FieldNotFound(selected_field)
|
||||
fields = selected_fields
|
||||
|
||||
fields_iterator = obj.iter_fields() if hasattr(obj, 'iter_fields') else iter_fields(obj)
|
||||
d = dict(fields_iterator)
|
||||
for key in fields:
|
||||
try:
|
||||
value = d[key]
|
||||
except KeyError:
|
||||
raise FieldNotFound(key)
|
||||
|
||||
if key == 'id' and obj.backend is not None:
|
||||
value = self.build_id(value, obj.backend)
|
||||
yield key, value
|
||||
|
||||
fields_iterator = obj.iter_fields()
|
||||
d = OrderedDict(fields_iterator)
|
||||
if condition is not None and not condition.is_valid(d):
|
||||
return None
|
||||
return OrderedDict([(k, v) for k, v in iter_select_and_decorate(d.iteritems())])
|
||||
return OrderedDict([(k, v) for k, v in iter_select_and_decorate(d)])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue