Allow getting venue info through Event.get_venue() as a workaround until the Venue.getInfo API call is added to Last.fm

This commit is contained in:
Andrew Moss 2013-12-26 23:11:23 +00:00
parent 78aac5c437
commit 88f5b5e6b9

176
pylast.py
View file

@ -47,7 +47,7 @@ if sys.version_info[0] == 3:
elif sys.version_info[0] == 2: elif sys.version_info[0] == 2:
from httplib import HTTPConnection from httplib import HTTPConnection
import htmlentitydefs import htmlentitydefs
from urllib import splithost as url_split_host from urllib import splithost as url_split_host
from urllib import quote_plus as url_quote_plus from urllib import quote_plus as url_quote_plus
@ -143,7 +143,7 @@ class _Network(object):
""" """
self.name = name self.name = name
self.homepage = homepage self.homepage = homepage
self.ws_server = ws_server self.ws_server = ws_server
self.api_key = api_key self.api_key = api_key
self.api_secret = api_secret self.api_secret = api_secret
@ -241,13 +241,13 @@ class _Network(object):
Quote from http://www.last.fm/api/submissions: Quote from http://www.last.fm/api/submissions:
======== ========
Client identifiers are used to provide a centrally managed database of Client identifiers are used to provide a centrally managed database of
the client versions, allowing clients to be banned if they are found to the client versions, allowing clients to be banned if they are found to
be behaving undesirably. The client ID is associated with a version be behaving undesirably. The client ID is associated with a version
number on the server, however these are only incremented if a client is number on the server, however these are only incremented if a client is
banned and do not have to reflect the version of the actual client application. banned and do not have to reflect the version of the actual client application.
During development, clients which have not been allocated an identifier should During development, clients which have not been allocated an identifier should
use the identifier tst, with a version number of 1.0. Do not distribute code or use the identifier tst, with a version number of 1.0. Do not distribute code or
client implementations which use this test identifier. Do not use the identifiers client implementations which use this test identifier. Do not use the identifiers
used by other clients. used by other clients.
@ -257,7 +257,7 @@ class _Network(object):
* Last.fm: submissions@last.fm * Last.fm: submissions@last.fm
* # TODO: list others * # TODO: list others
...and provide us with the name of your client and its homepage address. ...and provide us with the name of your client and its homepage address.
""" """
_deprecation_warning("Use _Network.scrobble(...), _Network.scrobble_many(...), and Netowrk.update_now_playing(...) instead") _deprecation_warning("Use _Network.scrobble(...), _Network.scrobble_many(...), and Netowrk.update_now_playing(...) instead")
@ -353,7 +353,7 @@ class _Network(object):
def enable_caching(self, file_path = None): def enable_caching(self, file_path = None):
"""Enables caching request-wide for all cachable calls. """Enables caching request-wide for all cachable calls.
* file_path: A file path for the backend storage file. If * file_path: A file path for the backend storage file. If
None set, a temp file would probably be created, according the backend. None set, a temp file would probably be created, according the backend.
""" """
@ -435,10 +435,10 @@ class _Network(object):
return Album(_extract(doc, "artist"), _extract(doc, "name"), self) return Album(_extract(doc, "artist"), _extract(doc, "name"), self)
def update_now_playing(self, artist, title, album = None, album_artist = None, def update_now_playing(self, artist, title, album = None, album_artist = None,
duration = None, track_number = None, mbid = None, context = None): duration = None, track_number = None, mbid = None, context = None):
""" """
Used to notify Last.fm that a user has started listening to a track. Used to notify Last.fm that a user has started listening to a track.
Parameters: Parameters:
artist (Required) : The artist name artist (Required) : The artist name
@ -462,7 +462,7 @@ class _Network(object):
_Request(self, "track.updateNowPlaying", params).execute() _Request(self, "track.updateNowPlaying", params).execute()
def scrobble(self, artist, title, timestamp, album = None, album_artist = None, track_number = None, def scrobble(self, artist, title, timestamp, album = None, album_artist = None, track_number = None,
duration = None, stream_id = None, context = None, mbid = None): duration = None, stream_id = None, context = None, mbid = None):
"""Used to add a track-play to a user's profile. """Used to add a track-play to a user's profile.
@ -578,7 +578,7 @@ class LastFMNetwork(_Network):
) )
def __repr__(self): def __repr__(self):
return "pylast.LastFMNetwork(%s)" %(", ".join(("'%s'" %self.api_key, "'%s'" %self.api_secret, "'%s'" %self.session_key, return "pylast.LastFMNetwork(%s)" %(", ".join(("'%s'" %self.api_key, "'%s'" %self.api_secret, "'%s'" %self.session_key,
"'%s'" %self.username, "'%s'" %self.password_hash))) "'%s'" %self.username, "'%s'" %self.password_hash)))
def __str__(self): def __str__(self):
@ -661,7 +661,7 @@ class LibreFMNetwork(_Network):
) )
def __repr__(self): def __repr__(self):
return "pylast.LibreFMNetwork(%s)" %(", ".join(("'%s'" %self.api_key, "'%s'" %self.api_secret, "'%s'" %self.session_key, return "pylast.LibreFMNetwork(%s)" %(", ".join(("'%s'" %self.api_key, "'%s'" %self.api_secret, "'%s'" %self.session_key,
"'%s'" %self.username, "'%s'" %self.password_hash))) "'%s'" %self.username, "'%s'" %self.password_hash)))
def __str__(self): def __str__(self):
@ -788,7 +788,7 @@ class _Request(object):
"Content-type": "application/x-www-form-urlencoded", "Content-type": "application/x-www-form-urlencoded",
'Accept-Charset': 'utf-8', 'Accept-Charset': 'utf-8',
'User-Agent': "pylast" + '/' + __version__ 'User-Agent': "pylast" + '/' + __version__
} }
(HOST_NAME, HOST_SUBDIR) = self.network.ws_server (HOST_NAME, HOST_SUBDIR) = self.network.ws_server
@ -796,7 +796,7 @@ class _Request(object):
conn = HTTPConnection(host = self._get_proxy()[0], port = self._get_proxy()[1]) conn = HTTPConnection(host = self._get_proxy()[0], port = self._get_proxy()[1])
try: try:
conn.request(method='POST', url="http://" + HOST_NAME + HOST_SUBDIR, conn.request(method='POST', url="http://" + HOST_NAME + HOST_SUBDIR,
body=data, headers=headers) body=data, headers=headers)
except Exception as e: except Exception as e:
raise NetworkError(self.network, e) raise NetworkError(self.network, e)
@ -864,7 +864,7 @@ class SessionKeyGenerator(object):
manually, unless you want to. manually, unless you want to.
""" """
def __init__(self, network): def __init__(self, network):
self.network = network self.network = network
self.web_auth_tokens = {} self.web_auth_tokens = {}
@ -1272,7 +1272,7 @@ class Album(_BaseObject, _Taggable):
return _extract(self._request("album.getInfo", cacheable = True), "mbid") return _extract(self._request("album.getInfo", cacheable = True), "mbid")
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the album page on the network. """Returns the url of the album page on the network.
# Parameters: # Parameters:
* domain_name str: The network's language domain. Possible values: * domain_name str: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
@ -1537,10 +1537,10 @@ class Artist(_BaseObject, _Taggable):
return seq return seq
def share(self, users, message = None): def share(self, users, message = None):
"""Shares this artist (sends out recommendations). """Shares this artist (sends out recommendations).
# Parameters: # Parameters:
* users [User|str,]: A list that can contain usernames, emails, User objects, or all of them. * users [User|str,]: A list that can contain usernames, emails, User objects, or all of them.
* message str: A message to include in the recommendation message. * message str: A message to include in the recommendation message.
""" """
#last.fm currently accepts a max of 10 recipient at a time #last.fm currently accepts a max of 10 recipient at a time
@ -1565,7 +1565,7 @@ class Artist(_BaseObject, _Taggable):
self._request('artist.share', False, params) self._request('artist.share', False, params)
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the artist page on the network. """Returns the url of the artist page on the network.
# Parameters: # Parameters:
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
@ -1579,7 +1579,7 @@ class Artist(_BaseObject, _Taggable):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
artist = _url_safe(self.get_name()) artist = _url_safe(self.get_name())
@ -1675,7 +1675,7 @@ class Event(_BaseObject):
* attending_status: The attending status. Possible values: * attending_status: The attending status. Possible values:
o EVENT_ATTENDING o EVENT_ATTENDING
o EVENT_MAYBE_ATTENDING o EVENT_MAYBE_ATTENDING
o EVENT_NOT_ATTENDING o EVENT_NOT_ATTENDING
""" """
params = self._get_params() params = self._get_params()
@ -1735,7 +1735,7 @@ class Event(_BaseObject):
v = doc.getElementsByTagName("venue")[0] v = doc.getElementsByTagName("venue")[0]
venue_id = _number(_extract(v, "id")) venue_id = _number(_extract(v, "id"))
return Venue(venue_id, self.network) return Venue(venue_id, self.network, venue_element=v)
def get_start_date(self): def get_start_date(self):
"""Returns the date when the event starts.""" """Returns the date when the event starts."""
@ -1781,7 +1781,7 @@ class Event(_BaseObject):
return _number(_extract(doc, "reviews")) return _number(_extract(doc, "reviews"))
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the event page on the network. """Returns the url of the event page on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -1794,15 +1794,15 @@ class Event(_BaseObject):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
return self.network._get_url(domain_name, "event") %{'id': self.get_id()} return self.network._get_url(domain_name, "event") %{'id': self.get_id()}
def share(self, users, message = None): def share(self, users, message = None):
"""Shares this event (sends out recommendations). """Shares this event (sends out recommendations).
* users: A list that can contain usernames, emails, User objects, or all of them. * users: A list that can contain usernames, emails, User objects, or all of them.
* message: A message to include in the recommendation message. * message: A message to include in the recommendation message.
""" """
#last.fm currently accepts a max of 10 recipient at a time #last.fm currently accepts a max of 10 recipient at a time
@ -1919,7 +1919,7 @@ class Country(_BaseObject):
return seq return seq
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the event page on the network. """Returns the url of the event page on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -1932,7 +1932,7 @@ class Country(_BaseObject):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
country_name = _url_safe(self.get_name()) country_name = _url_safe(self.get_name())
@ -2173,7 +2173,7 @@ class Playlist(_BaseObject):
return _extract(self._get_info_node(), "image")[size] return _extract(self._get_info_node(), "image")[size]
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the playlist on the network. """Returns the url of the playlist on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -2186,7 +2186,7 @@ class Playlist(_BaseObject):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
english_url = _extract(self._get_info_node(), "url") english_url = _extract(self._get_info_node(), "url")
@ -2319,7 +2319,7 @@ class Tag(_BaseObject):
return seq return seq
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the tag page on the network. """Returns the url of the tag page on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -2332,7 +2332,7 @@ class Tag(_BaseObject):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
name = _url_safe(self.get_name()) name = _url_safe(self.get_name())
@ -2548,9 +2548,9 @@ class Track(_BaseObject, _Taggable):
return seq return seq
def share(self, users, message = None): def share(self, users, message = None):
"""Shares this track (sends out recommendations). """Shares this track (sends out recommendations).
* users: A list that can contain usernames, emails, User objects, or all of them. * users: A list that can contain usernames, emails, User objects, or all of them.
* message: A message to include in the recommendation message. * message: A message to include in the recommendation message.
""" """
#last.fm currently accepts a max of 10 recipient at a time #last.fm currently accepts a max of 10 recipient at a time
@ -2575,7 +2575,7 @@ class Track(_BaseObject, _Taggable):
self._request('track.share', False, params) self._request('track.share', False, params)
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the track page on the network. """Returns the url of the track page on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -2588,7 +2588,7 @@ class Track(_BaseObject, _Taggable):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
artist = _url_safe(self.get_artist().get_name()) artist = _url_safe(self.get_artist().get_name())
@ -2707,7 +2707,7 @@ class Group(_BaseObject):
return seq return seq
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the group page on the network. """Returns the url of the group page on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -2720,7 +2720,7 @@ class Group(_BaseObject):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
name = _url_safe(self.get_name()) name = _url_safe(self.get_name())
@ -2832,13 +2832,13 @@ class User(_BaseObject):
for e_id in ids: for e_id in ids:
events.append(Event(e_id, self.network)) events.append(Event(e_id, self.network))
return events return events
def get_artist_tracks(self, artist): def get_artist_tracks(self, artist):
"""Get a list of tracks by a given artist scrobbled by this user, including scrobble time.""" """Get a list of tracks by a given artist scrobbled by this user, including scrobble time."""
# Not implemented: "Can be limited to specific timeranges, defaults to all time." # Not implemented: "Can be limited to specific timeranges, defaults to all time."
params = self._get_params() params = self._get_params()
params['artist'] = artist params['artist'] = artist
@ -2848,14 +2848,14 @@ class User(_BaseObject):
artist = _extract(track, "artist") artist = _extract(track, "artist")
date = _extract(track, "date") date = _extract(track, "date")
timestamp = track.getElementsByTagName("date")[0].getAttribute("uts") timestamp = track.getElementsByTagName("date")[0].getAttribute("uts")
seq.append(PlayedTrack(Track(artist, title, self.network), date, timestamp)) seq.append(PlayedTrack(Track(artist, title, self.network), date, timestamp))
return seq return seq
def get_friends(self, limit = 50): def get_friends(self, limit = 50):
"""Returns a list of the user's friends. """ """Returns a list of the user's friends. """
seq = [] seq = []
for node in _collect_nodes(limit, self, "user.getFriends", False): for node in _collect_nodes(limit, self, "user.getFriends", False):
seq.append(User(_extract(node, "name"), self.network)) seq.append(User(_extract(node, "name"), self.network))
@ -3037,13 +3037,13 @@ class User(_BaseObject):
return _number(_extract(doc, "playcount")) return _number(_extract(doc, "playcount"))
def get_top_albums(self, period = PERIOD_OVERALL): def get_top_albums(self, period = PERIOD_OVERALL):
"""Returns the top albums played by a user. """Returns the top albums played by a user.
* period: The period of time. Possible values: * period: The period of time. Possible values:
o PERIOD_OVERALL o PERIOD_OVERALL
o PERIOD_7DAYS o PERIOD_7DAYS
o PERIOD_3MONTHS o PERIOD_3MONTHS
o PERIOD_6MONTHS o PERIOD_6MONTHS
o PERIOD_12MONTHS o PERIOD_12MONTHS
""" """
params = self._get_params() params = self._get_params()
@ -3062,13 +3062,13 @@ class User(_BaseObject):
return seq return seq
def get_top_artists(self, period = PERIOD_OVERALL): def get_top_artists(self, period = PERIOD_OVERALL):
"""Returns the top artists played by a user. """Returns the top artists played by a user.
* period: The period of time. Possible values: * period: The period of time. Possible values:
o PERIOD_OVERALL o PERIOD_OVERALL
o PERIOD_7DAYS o PERIOD_7DAYS
o PERIOD_3MONTHS o PERIOD_3MONTHS
o PERIOD_6MONTHS o PERIOD_6MONTHS
o PERIOD_12MONTHS o PERIOD_12MONTHS
""" """
params = self._get_params() params = self._get_params()
@ -3086,8 +3086,8 @@ class User(_BaseObject):
return seq return seq
def get_top_tags(self, limit=None): def get_top_tags(self, limit=None):
"""Returns a sequence of the top tags used by this user with their counts as TopItem objects. """Returns a sequence of the top tags used by this user with their counts as TopItem objects.
* limit: The limit of how many tags to return. * limit: The limit of how many tags to return.
""" """
doc = self._request("user.getTopTags", True) doc = self._request("user.getTopTags", True)
@ -3102,13 +3102,13 @@ class User(_BaseObject):
return seq return seq
def get_top_tracks(self, period = PERIOD_OVERALL): def get_top_tracks(self, period = PERIOD_OVERALL):
"""Returns the top tracks played by a user. """Returns the top tracks played by a user.
* period: The period of time. Possible values: * period: The period of time. Possible values:
o PERIOD_OVERALL o PERIOD_OVERALL
o PERIOD_7DAYS o PERIOD_7DAYS
o PERIOD_3MONTHS o PERIOD_3MONTHS
o PERIOD_6MONTHS o PERIOD_6MONTHS
o PERIOD_12MONTHS o PERIOD_12MONTHS
""" """
params = self._get_params() params = self._get_params()
@ -3230,7 +3230,7 @@ class User(_BaseObject):
return _extract(doc, "image") return _extract(doc, "image")
def get_url(self, domain_name = DOMAIN_ENGLISH): def get_url(self, domain_name = DOMAIN_ENGLISH):
"""Returns the url of the user page on the network. """Returns the url of the user page on the network.
* domain_name: The network's language domain. Possible values: * domain_name: The network's language domain. Possible values:
o DOMAIN_ENGLISH o DOMAIN_ENGLISH
o DOMAIN_GERMAN o DOMAIN_GERMAN
@ -3243,7 +3243,7 @@ class User(_BaseObject):
o DOMAIN_TURKISH o DOMAIN_TURKISH
o DOMAIN_RUSSIAN o DOMAIN_RUSSIAN
o DOMAIN_JAPANESE o DOMAIN_JAPANESE
o DOMAIN_CHINESE o DOMAIN_CHINESE
""" """
name = _url_safe(self.get_name()) name = _url_safe(self.get_name())
@ -3459,13 +3459,25 @@ class Venue(_BaseObject):
"""A venue where events are held.""" """A venue where events are held."""
# TODO: waiting for a venue.getInfo web service to use. # TODO: waiting for a venue.getInfo web service to use.
# TODO: As an intermediate use case, can pass the venue DOM element when using
# Event.get_venue() to populate the venue info, if the venue.getInfo API
# call becomes available this workaround should be removed
id = None id = None
info = None
name = None
location = None
url = None
def __init__(self, id, network): def __init__(self, id, network, venue_element=None):
_BaseObject.__init__(self, network) _BaseObject.__init__(self, network)
self.id = _number(id) self.id = _number(id)
if venue_element is not None:
self.info = _extract_element_tree(venue_element)
self.name = self.info.get('name')
self.url = self.info.get('url')
self.location = self.info.get('location')
def __repr__(self): def __repr__(self):
return "pylast.Venue(%s, %s)" %(repr(self.id), repr(self.network)) return "pylast.Venue(%s, %s)" %(repr(self.id), repr(self.network))
@ -3485,6 +3497,21 @@ class Venue(_BaseObject):
return self.id return self.id
def get_name(self):
"""Returns the name of the venue."""
return self.name
def get_url(self):
"""Returns the URL of the venue page."""
return self.url
def get_location(self):
"""Returns the location of the venue (dictionary)."""
return self.location
def get_upcoming_events(self): def get_upcoming_events(self):
"""Returns the upcoming events in this venue.""" """Returns the upcoming events in this venue."""
@ -3575,14 +3602,14 @@ def _collect_nodes(limit, sender, method_name, cacheable, params=None):
total_pages = _number(main.getAttribute("totalpages")) total_pages = _number(main.getAttribute("totalpages"))
else: else:
raise Exception("No total pages attribute") raise Exception("No total pages attribute")
for node in main.childNodes: for node in main.childNodes:
if not node.nodeType == xml.dom.Node.TEXT_NODE and (not limit or (len(nodes) < limit)): if not node.nodeType == xml.dom.Node.TEXT_NODE and (not limit or (len(nodes) < limit)):
nodes.append(node) nodes.append(node)
if page >= total_pages: if page >= total_pages:
end_of_pages = True end_of_pages = True
page += 1 page += 1
return nodes return nodes
@ -3598,6 +3625,35 @@ def _extract(node, name, index = 0):
else: else:
return None return None
def _extract_element_tree(node, index = 0):
"""Extract an element tree into a multi-level dictionary
NB: If any elements have text nodes as well as nested
elements this will ignore the text nodes"""
def _recurse_build_tree(rootNode, targetDict):
"""Recursively build a multi-level dict"""
def _has_child_elements(rootNode):
"""Check if an element has any nested (child) elements"""
for node in rootNode.childNodes:
if node.nodeType == node.ELEMENT_NODE:
return True
return False
for node in rootNode.childNodes:
if node.nodeType == node.ELEMENT_NODE:
if _has_child_elements(node):
targetDict[node.tagName] = {}
_recurse_build_tree(node, targetDict[node.tagName])
else:
targetDict[node.tagName] = _unescape_htmlentity(node.firstChild.data.strip())
return targetDict
return _recurse_build_tree(node, {})
def _extract_all(node, name, limit_count = None): def _extract_all(node, name, limit_count = None):
"""Extracts all the values from the xml string. returning a list.""" """Extracts all the values from the xml string. returning a list."""
@ -3633,7 +3689,7 @@ def _number(string):
def _unescape_htmlentity(string): def _unescape_htmlentity(string):
#string = _unicode(string) #string = _unicode(string)
mapping = htmlentitydefs.name2codepoint mapping = htmlentitydefs.name2codepoint
for key in mapping: for key in mapping: