From dba09573809b5c43dd5630bf6c00cd84ebfcb106 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Mon, 3 Sep 2012 20:14:21 +0200 Subject: [PATCH 1/4] Added possibility for custom _autocast functions --- eveapi.py | 54 +++++++++++++++++++++++++++++++++--------------------- setup.py | 3 +-- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/eveapi.py b/eveapi.py index 0733f51..7351d5e 100644 --- a/eveapi.py +++ b/eveapi.py @@ -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. # @@ -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: @@ -207,7 +212,7 @@ def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, pro 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 @@ -216,22 +221,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") @@ -272,9 +277,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() @@ -295,7 +301,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: @@ -317,18 +323,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): @@ -388,7 +394,7 @@ 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: @@ -396,7 +402,7 @@ def __call__(self, path, **kw): 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 @@ -430,7 +436,13 @@ def _autocast(key, value): class _Parser(object): - + + def __init__(self, autocast=None): + self.autocast = autocast or _autocast + + def cast(self, key, value): + return apply(self.autocast, (key, value)) + def Parse(self, data, isStream=False): self.container = self.root = None self._cdata = False @@ -524,7 +536,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.cast(col, attributes[row_idx+1])) row_idx += 2 else: fixed.append(None) @@ -534,7 +546,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.cast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]) # this._isrow = True @@ -557,7 +569,7 @@ def tag_cdata(self, data): return this = self.container - data = _autocast(this._name, data) + data = self.cast(this._name, data) if this._isrow: # sigh. anonymous data inside rows makes Entity cry. @@ -637,7 +649,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.cast(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): @@ -646,7 +658,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.cast(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) @@ -659,7 +671,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.cast(attributes[i], attributes[i+1]) return diff --git a/setup.py b/setup.py index 7be3cad..65bdd57 100755 --- a/setup.py +++ b/setup.py @@ -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 = 'jamie@hlekkir.com', @@ -31,6 +31,5 @@ 'Topic :: Games/Entertainment', ), # CONTENTS - zip_safe = True, py_modules = ['eveapi',], ) From 5080c39d7d6412e898850c80c0ffe078b7780fd6 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Mon, 3 Sep 2012 20:34:31 +0200 Subject: [PATCH 2/4] Added comment for autocast_func in EVEAPIConnection --- eveapi.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/eveapi.py b/eveapi.py index 7351d5e..07c1a69 100644 --- a/eveapi.py +++ b/eveapi.py @@ -206,6 +206,18 @@ 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 From 0e30ca0d035058050b6cecf41aa79063ee5a9584 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Tue, 4 Sep 2012 21:51:51 +0200 Subject: [PATCH 3/4] Replaced apply() by __call__() for performance (saves 2 function calls) --- eveapi.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/eveapi.py b/eveapi.py index 07c1a69..fde97d8 100644 --- a/eveapi.py +++ b/eveapi.py @@ -452,9 +452,6 @@ class _Parser(object): def __init__(self, autocast=None): self.autocast = autocast or _autocast - def cast(self, key, value): - return apply(self.autocast, (key, value)) - def Parse(self, data, isStream=False): self.container = self.root = None self._cdata = False @@ -548,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(self.cast(col, attributes[row_idx+1])) + fixed.append(self.autocast.__call__(col, attributes[row_idx+1])) row_idx += 2 else: fixed.append(None) @@ -558,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([self.cast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]) + self.container.append([self.autocast.__call__(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]) # this._isrow = True @@ -581,7 +578,7 @@ def tag_cdata(self, data): return this = self.container - data = self.cast(this._name, data) + data = self.autocast.__call__(this._name, data) if this._isrow: # sigh. anonymous data inside rows makes Entity cry. @@ -661,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 = [self.cast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)] + row = [self.autocast.__call__(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): @@ -670,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 = [self.cast(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]+[getattr(this, col) for col in attributes2] + row = [self.autocast.__call__(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) @@ -683,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]] = self.cast(attributes[i], attributes[i+1]) + this.__dict__[attributes[i]] = self.autocast.__call__(attributes[i], attributes[i+1]) return From c0130b685626de798fd2ff6c2bd310981e54bbe2 Mon Sep 17 00:00:00 2001 From: Robin Jarry Date: Wed, 5 Sep 2012 00:15:23 +0200 Subject: [PATCH 4/4] No need for __call__ --- eveapi.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/eveapi.py b/eveapi.py index fde97d8..6167ee4 100644 --- a/eveapi.py +++ b/eveapi.py @@ -545,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(self.autocast.__call__(col, attributes[row_idx+1])) + fixed.append(self.autocast(col, attributes[row_idx+1])) row_idx += 2 else: fixed.append(None) @@ -555,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([self.autocast.__call__(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)]) # this._isrow = True @@ -578,7 +578,7 @@ def tag_cdata(self, data): return this = self.container - data = self.autocast.__call__(this._name, data) + data = self.autocast(this._name, data) if this._isrow: # sigh. anonymous data inside rows makes Entity cry. @@ -658,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 = [self.autocast.__call__(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): @@ -667,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 = [self.autocast.__call__(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) @@ -680,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]] = self.autocast.__call__(attributes[i], attributes[i+1]) + this.__dict__[attributes[i]] = self.autocast(attributes[i], attributes[i+1]) return