Skip to content

Custom _autocast function #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 42 additions & 21 deletions eveapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
# OTHER DEALINGS IN THE SOFTWARE
#
#-----------------------------------------------------------------------------
# Version: 1.2.8 - 3 September 2012
# - Added a "autocast_func" parameter to the EVEAPIConnection class.
# If this parameter is not None, it will be used in replacement
# of the default "_autocast" function.
#
# Version: 1.2.7 - 3 September 2012
# - Added get() method to Row object.
#
Expand Down Expand Up @@ -164,7 +169,7 @@ class ServerError(Error):
pass


def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, proxySSL=False):
def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, proxySSL=False, autocast_func=None):
# Creates an API object through which you can call remote functions.
#
# The following optional arguments may be provided:
Expand Down Expand Up @@ -201,13 +206,25 @@ def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, pro
# will only be called if you returned None in the retrieve() for
# this object.
#
# autocast_func - plain function that will be used to cast XML values to the
# most probable type. The function must accept 2 parameters
# and return a casted value. It shouldn't raise exceptions.
#
# example:
#
# def my_autocast(key, value):
# ....
# blah blah
# ....
# return casted_value
#

if not url.startswith("http"):
url = "https://" + url
p = urlparse.urlparse(url, "https")
if p.path and p.path[-1] == "/":
p.path = p.path[:-1]
ctx = _RootContext(None, p.path, {}, {})
ctx = _RootContext(None, p.path, {}, {}, autocast_func)
ctx._handler = cacheHandler
ctx._scheme = p.scheme
ctx._host = p.netloc
Expand All @@ -216,22 +233,22 @@ def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, pro
return ctx


def ParseXML(file_or_string):
def ParseXML(file_or_string, autocast=None):
try:
return _ParseXML(file_or_string, False, None)
return _ParseXML(file_or_string, False, None, autocast=autocast)
except TypeError:
raise TypeError("XML data must be provided as string or file-like object")


def _ParseXML(response, fromContext, storeFunc):
def _ParseXML(response, fromContext, storeFunc, autocast=None):
# pre/post-process XML or Element data

if fromContext and isinstance(response, Element):
obj = response
elif type(response) in (str, unicode):
obj = _Parser().Parse(response, False)
obj = _Parser(autocast).Parse(response, False)
elif hasattr(response, "read"):
obj = _Parser().Parse(response, True)
obj = _Parser(autocast).Parse(response, True)
else:
raise TypeError("retrieve method must return None, string, file-like object or an Element instance")

Expand Down Expand Up @@ -272,9 +289,10 @@ def _ParseXML(response, fromContext, storeFunc):

class _Context(object):

def __init__(self, root, path, parentDict, newKeywords=None):
def __init__(self, root, path, parentDict, newKeywords=None, autocast=None):
self._root = root or self
self._path = path
self.autocast = autocast
if newKeywords:
if parentDict:
self.parameters = parentDict.copy()
Expand All @@ -295,7 +313,7 @@ def context(self, *args, **kw):

def __getattr__(self, this):
# perform arcane attribute majick trick
return _Context(self._root, self._path + "/" + this, self.parameters)
return _Context(self._root, self._path + "/" + this, self.parameters, autocast=self.autocast)

def __call__(self, **kw):
if kw:
Expand All @@ -317,18 +335,18 @@ def character(self, characterID):
# returns a copy of this connection object but for every call made
# through it, it will add the folder "/char" to the url, and the
# characterID to the parameters passed.
return _Context(self._root, self._path + "/char", self.parameters, {"characterID":characterID})
return _Context(self._root, self._path + "/char", self.parameters, {"characterID":characterID}, autocast=self.autocast)

def corporation(self, characterID):
# same as character except for the folder "/corp"
return _Context(self._root, self._path + "/corp", self.parameters, {"characterID":characterID})
return _Context(self._root, self._path + "/corp", self.parameters, {"characterID":characterID}, autocast=self.autocast)


class _RootContext(_Context):

def auth(self, **kw):
if len(kw) == 2 and (("keyID" in kw and "vCode" in kw) or ("userID" in kw and "apiKey" in kw)):
return _AuthContext(self._root, self._path, self.parameters, kw)
return _AuthContext(self._root, self._path, self.parameters, kw, autocast=self.autocast)
raise ValueError("Must specify keyID and vCode")

def setcachehandler(self, handler):
Expand Down Expand Up @@ -388,15 +406,15 @@ def __call__(self, path, **kw):
if retrieve_fallback:
# implementor is handling fallbacks...
try:
return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj)))
return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj)), autocast=self.autocast)
except Error, e:
response = retrieve_fallback(self._host, path, kw, reason=e)
if response is not None:
return response
raise
else:
# implementor is not handling fallbacks...
return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj)))
return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj)), autocast=self.autocast)

#-----------------------------------------------------------------------------
# XML Parser
Expand Down Expand Up @@ -430,7 +448,10 @@ def _autocast(key, value):


class _Parser(object):


def __init__(self, autocast=None):
self.autocast = autocast or _autocast

def Parse(self, data, isStream=False):
self.container = self.root = None
self._cdata = False
Expand Down Expand Up @@ -524,7 +545,7 @@ def tag_start(self, name, attributes):
row_idx = 0; hdr_idx = 0; numAttr*=2
for col in self.container._cols:
if col == attributes[row_idx]:
fixed.append(_autocast(col, attributes[row_idx+1]))
fixed.append(self.autocast(col, attributes[row_idx+1]))
row_idx += 2
else:
fixed.append(None)
Expand All @@ -534,7 +555,7 @@ def tag_start(self, name, attributes):
if not self.container._cols or (numAttr > numCols):
# the row data contains more attributes than were defined.
self.container._cols = attributes[0::2]
self.container.append([_autocast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)])
self.container.append([self.autocast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)])
# </hack>

this._isrow = True
Expand All @@ -557,7 +578,7 @@ def tag_cdata(self, data):
return

this = self.container
data = _autocast(this._name, data)
data = self.autocast(this._name, data)

if this._isrow:
# sigh. anonymous data inside rows makes Entity cry.
Expand Down Expand Up @@ -637,7 +658,7 @@ def tag_end(self, name):
# multiples of some tag or attribute. Code below handles this case.
elif isinstance(sibling, Rowset):
# its doppelganger is a rowset, append this as a row to that.
row = [_autocast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]
row = [self.autocast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]
row.extend([getattr(this, col) for col in attributes2])
sibling.append(row)
elif isinstance(sibling, Element):
Expand All @@ -646,7 +667,7 @@ def tag_end(self, name):
# into a Rowset, adding the sibling element and this one.
rs = Rowset()
rs.__catch = rs._name = this._name
row = [_autocast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]+[getattr(this, col) for col in attributes2]
row = [self.autocast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]+[getattr(this, col) for col in attributes2]
rs.append(row)
row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)]+[getattr(sibling, col) for col in attributes2]
rs.append(row)
Expand All @@ -659,7 +680,7 @@ def tag_end(self, name):

# Now fix up the attributes and be done with it.
for i in xrange(0, len(attributes), 2):
this.__dict__[attributes[i]] = _autocast(attributes[i], attributes[i+1])
this.__dict__[attributes[i]] = self.autocast(attributes[i], attributes[i+1])

return

Expand Down
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
setup(
# GENERAL INFO
name = 'eveapi',
version = '1.2.6',
version = '1.2.8',
description = 'Python library for accessing the EVE Online API.',
author = 'Jamie van den Berge',
author_email = '[email protected]',
Expand All @@ -31,6 +31,5 @@
'Topic :: Games/Entertainment',
),
# CONTENTS
zip_safe = True,
py_modules = ['eveapi',],
)