These forums have been archived and are now read-only.

The new forums are live and can be found at https://forums.eveonline.com/

EVE Technology Lab

 
  • Topic is locked indefinitely.
12Next page
 

PyCrest - Python CREST Client

First post First post
Author
Dreae
Spaalonebabuguuscooties
#1 - 2015-01-17 00:47:42 UTC
Here is a little Python client library I've been working on for accessing the CREST API.

It supports all of the features I know about the CREST API.

GitHub:
https://github.com/Dreae/PyCrest

Docs:
http://pycrest.readthedocs.org/en/latest/

The name is supposed to sound like pie crust, because that amused me...
Ivan Koratril
Tiganic Operations Ltd
#2 - 2015-01-17 01:48:48 UTC
Very nice! I've been trying to find a way to hook python up to Authed CREST; this will be useful.

One question (warning: from a clueless newbie): if I'm writing a python app rather than a web app, what should I put in for the redirect_uri? As I understand the Oauth flow, the authorization server will be returning the access token back through the redirect UI; does that mean I need to be running a web server to use the interface? I'm wanting to just write a simple python program to read and evaluate market data.
Dreae
Spaalonebabuguuscooties
#3 - 2015-01-17 02:06:12 UTC
Ivan Koratril wrote:
Very nice! I've been trying to find a way to hook python up to Authed CREST; this will be useful.

One question (warning: from a clueless newbie): if I'm writing a python app rather than a web app, what should I put in for the redirect_uri? As I understand the Oauth flow, the authorization server will be returning the access token back through the redirect UI; does that mean I need to be running a web server to use the interface? I'm wanting to just write a simple python program to read and evaluate market data.


As far as I know there is no alternative to using the redirect_uri in the OAuth specification, however, you can setup a web server running in your program listening on localhost to receive the authorization token. This is one of the methods used by Google for using OAuth with installed applications.

The alternative that Google uses is to provide a redirect_uri that notifies the OAuth provider that instead of redirecting they should render a webpage with further instructions to the user. This would be nice, but would obviously require CCP's support.
Ivan Koratril
Tiganic Operations Ltd
#4 - 2015-01-17 02:59:26 UTC
Cool; thank you. I'll play around and see if I can get that to work.
CCP FoxFour
C C P
C C P Alliance
#5 - 2015-01-17 05:20:46 UTC
"By default resources are cached for 10 minutes"

Are you aware all returns have a header containing their cache time?

@CCP_FoxFour // Technical Designer // Team Tech Co

Third-party developer? Check out the official developers site for dev blogs, resources, and more.

Nuke Cherenkov
The Scope
Gallente Federation
#6 - 2015-01-26 04:00:47 UTC  |  Edited by: Nuke Cherenkov
I'm getting an error in requests trying to use PyCrest:

requests.exceptions.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:581)

My code:

import pycrest

eve = pycrest.EVE()
eve()


This doesn't have anything to do with PyCrest but hoping there is some request/networking expertise out there.. This code has the same problem as the above (created in a fresh virtualenv that only has requests installed in it):

import requests

params = None
res = requests.get('https://public-crest.eveonline.com/', headers={'Accept': 'application/json', 'User-Agent': 'PyCrest - v 0.0.1 testing'}, params=params if params else {})


I'm using Python 2.7.9 (32 bit) on Win 7 64. The code is running in a virtualenv with requests 2.5.1 (latest).

I'm also using the Wing Pro IDE and I set breakpoints down into ssl.py where the error is raised but I don't see anything that indicates a problem to me (but then I'm more of a hardware guy). I found a post on how to force requests to use a specific SSL version, https://lukasa.co.uk/2013/01/Choosing_SSL_Version_In_Requests/ , selected PROTOCOL_TLSv1, but get the same error. I checked with https://www.digicert.com/help/ and it shows that https://public-crest.eveonline.com/ uses TLS 1.0, SSL 3.0 (and notes that 3.0 has a bad vulnerability (POODLE Bug attack)). Note that I also ran the code outside the IDE and got the same error.

Looking on Stackoverlfow I found:

Quote:
RC4-MD5 also is the reason for not working with python 3.4. Contrary to python 2.7 there is a more secure default cipher set in python 3.4 which includes "..:!MD5". This means the python 3.4 client will not offer RC4-MD5 as cipher and thus the handshake will fail because of no shared ciphers.


This page: https://sslanalyzer.comodoca.com/?url=https%3A%2F%2Fpublic-crest.eveonline.com%2F says the crest server uses TLS_RSA_WITH_RC4_128_MD5. One of the main reasons for Python 2.7.9 was to update SSL:
Quote:
The entirety of Python 3.4's ssl module has been backported for Python 2.7.9. See PEP 466 for justification.


Is this a server and latest version of Python issue or?

Edit 1/26
Found another PC with Python 2.7.8, installed requests 2.5.1 (1.2.3 worked too) and the second code example runs fine. Looks like CREST and the latest versions of Python won't play nicely with each other?
Steve Ronuken
Fuzzwork Enterprises
Vote Steve Ronuken for CSM
#7 - 2015-01-26 12:29:01 UTC
It should be noted, POODLE isn't a /huge/ concern, if you're using a network you trust.

They need to be between you and CCP, which isn't too likely, unless you're using public wifi. (still, should be disabled)


Anyway, you have a number of options to get the application working. One which works is use something like POSTMAN for the initial handshake, which gets you a refresh token, along with an access token. Once you have a refresh token, that can be used to reauthenticate without using a password (it's how I'm using things with my applications.)


Woo! CSM XI!

Fuzzwork Enterprises

Twitter: @fuzzysteve on Twitter

Paxswill
Center for Advanced Studies
Gallente Federation
#8 - 2015-01-26 22:47:53 UTC
Nuke Cherenkov wrote:
I'm getting an error in requests trying to use PyCrest:

requests.exceptions.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:581)

*snip*

Is this a server and latest version of Python issue or?


Yes, this is a problem because the public CREST serveronly supports TLS_RSA_WITH_RC4_128_MD5, and ciphers using MD5 were explicitly disabled in the latest Python releases. To get around this, a custom transport adapter should be used with the requests library, so this is something that should either be handled by Dreae or by CCP changing their server configuration to allow other ciphers (CCPlease?). I've submitted a pull request with that and a few other fixes/changes, so there might be a fix soon-ish.
Nuke Cherenkov
The Scope
Gallente Federation
#9 - 2015-01-27 06:40:17 UTC
For those whose expertise, like mine, lags the previous commentators:

pip install https://github.com/paxswill/PyCrest/archive/weak-ciphers.zip


gets you Paxswill's version to play with while Dreae sorts out the pull requests.
Dreae
Spaalonebabuguuscooties
#10 - 2015-01-28 00:40:24 UTC
I've merged Paxswill's pull request, and I've updated the package on pip so it should work now.

CCP FoxFour wrote:
"By default resources are cached for 10 minutes"

Are you aware all returns have a header containing their cache time?



I was not, I'll update the library to respect the Cache-Control header when I get the chance
Dreae
Spaalonebabuguuscooties
#11 - 2015-01-29 21:24:23 UTC
The library has been updated to respect the Cache-Control header in API returns.
Nuke Cherenkov
The Scope
Gallente Federation
#12 - 2015-02-01 20:01:46 UTC
Having trouble connecting to auth CREST from a desktop app. I'm pretty sure I can negotiate the authorization process, auth_conn.whoami() looks like it returns good data but region().marketSellOrders(type=item) fails with a 404 (pycrest version 0.0.3).

I've put the main code below, the server code is at https://gist.github.com/EricE/fab125c74082ea337faa (my first code at GitHub Smile), I think it is working ok.

Tracing down into eve.py, the problem is here (line 49):

res = self._session.get(resource, params=prms)

resource is u'https://public-crest.eveonline.com/market/10000067/orders/sell/'
prms is {'type': u'https://public-crest.eveonline.com/types/34/'}

It's trying to use public CREST for an auth CREST resource? I can't figure out how to tell pycrest to use https://crest-tq.eveonline.com/ instead. I tried setting a breakpoint and manually changing resource to crest-tq but then I get a 401, I assume it's because I wasn't sending the auth info (I'm in over my head at this point, very limited web development background). I'd appreciate some pointers, feel free to point out if I could be doing this a better way...

import multiprocessing
import urlparse
import uuid
import webbrowser

import pycrest
import server

#----------------------------------------------------------------------
def main():
    # Launch the server in a separate process, pass back the code and state in a queue
    queue = multiprocessing.Queue()

    eve_auth = pycrest.EVE(client_id="client id goes here",
                           api_key="api key goes here",
                           redirect_uri="http://localhost:12345/callback/")

    state_str = uuid.uuid4().get_hex()
    webbrowser.open(eve_auth.auth_uri(state=state_str))

    p = multiprocessing.Process(target=server.start_server, args=(queue,))
    p.start()

    code_raw = queue.get()

    queue.close()
    queue.join_thread()

    p.join()

    query_str = dict(urlparse.parse_qsl(code_raw))

    eve_auth()

    auth_conn = eve_auth.authorize(query_str['code'])

    region = getByAttrVal(eve_auth.regions().items, 'name', 'Catch')
    item = getByAttrVal(getAllItems(eve_auth.itemTypes), 'name', 'Tritanium').href
    
    print getAllItems(region().marketSellOrders(type=item))
        
# Below lifted from http://pycrest.readthedocs.org/en/latest/
def getByAttrVal(objlist, attr, val):
    ''' Searches list of dicts for a dict with dict[attr] == val '''
    matches = [getattr(obj, attr) == val for obj in objlist]
    index = matches.index(True)  # find first match, raise ValueError if not found
    return objlist[index]

def getAllItems(page):
    ''' Fetch data from all pages '''
    ret = page().items
    while hasattr(page(), 'next'):
        page = page().next()
        ret.extend(page().items)
    return ret

if __name__ == '__main__':
    main()
Dreae
Spaalonebabuguuscooties
#13 - 2015-02-02 04:59:18 UTC
Nuke Cherenkov wrote:
Having trouble connecting to auth CREST from a desktop app.
--snip--


Nearly there, looks like you just need to change 37
region = getByAttrVal(eve_auth.regions().items, 'name', 'Catch')


to use your newly created authorized connection
region = getByAttrVal(auth_conn().regions().items, 'name', 'Catch')
Nuke Cherenkov
The Scope
Gallente Federation
#14 - 2015-02-02 07:09:08 UTC
Thanks for the quick reply! Made the change:

region = getByAttrVal(auth_conn().regions().items, 'name', 'Catch')
item = getByAttrVal(getAllItems(eve_auth.itemTypes), 'name', 'Tritanium').href
print getAllItems(region().marketSellOrders(type=item))


However, getting a 401 from this line:
print getAllItems(region().marketSellOrders(type=item))


I set a conditional breakpoint in APIConnection :

def get(self, resource, params=None):
    logger.debug('Getting resource %s', resource)
    if params is None:  # <-- breakpoint here, condition is 'orders/sell/' in resource
        params = {}


resource == u'https://crest-tq.eveonline.com/market/10000014/orders/sell/'
params == {'type': u'https://public-crest.eveonline.com/types/34/'}

Going into the cache check have:

prms == {'type': u'https://public-crest.eveonline.com/types/34/'}

I noticed the necessary info seems to be present in key after the cache check:

# check cache
key = (resource, frozenset(self._session.headers.items()), frozenset(prms.items()))
cached = self.cache.get(key)  # cached == None


key is set to:
(u'https://crest-tq.eveonline.com/market/10000014/orders/sell/', frozenset([('Accept', 'application/json'), ('Connection', 'keep-alive'), ('Accept-Encoding', 'gzip, deflate'), ('Authorization', u'Bearer 9t long string A2'), ('User-Agent', 'PyCrest/0.0.1')]), frozenset([('type', u'https://public-crest.eveonline.com/types/34/')]))


The actual call to requests doesn't have any auth info (or user agent)?:

res = self._session.get(resource, params=prms)
if res.status_code != 200:  # <-- status_code == 401
    raise APIException("Got unexpected status code from server: %i" % res.status_code)
Dreae
Spaalonebabuguuscooties
#15 - 2015-02-02 22:52:15 UTC
Nuke Cherenkov wrote:


However, getting a 401 from this line:
print getAllItems(region().marketSellOrders(type=item))



Looks like you just need to include the publicData scope when authorizing the application.

webbrowser.open(eve_auth.auth_uri(state=state_str, scopes=['publicData']))


Note: if you haven't included publicData in permissions when creating the application on the developer website you'll need to update it.

Also, line 38 should probably also use the authorized connection, otherwise you'll be referencing the public CREST resource for the item type, though in my testing that doesn't seem to matter.

Nuke Cherenkov wrote:

The actual call to requests doesn't have any auth info (or user agent)?:


The headers are stored in the session and are sent automatically whenever a request is made using self._session
Nuke Cherenkov
The Scope
Gallente Federation
#16 - 2015-02-03 07:41:54 UTC
Success! Thanks for hanging with me, apologize about missing adding scopes, I'd even copied and pasted the line from your docs in a comment above my line... Also needed to update the application for public data.
delivery waste
Center for Advanced Studies
Gallente Federation
#17 - 2015-02-19 02:10:52 UTC
Thanks very much for your effort!
Is there any convenient way to work with market history using your client?
CCP FoxFour
C C P
C C P Alliance
#18 - 2015-02-19 02:52:20 UTC
delivery waste wrote:
Thanks very much for your effort!
Is there any convenient way to work with market history using your client?


No but thats my fault and something I hope to fix soon.

@CCP_FoxFour // Technical Designer // Team Tech Co

Third-party developer? Check out the official developers site for dev blogs, resources, and more.

delivery waste
Center for Advanced Studies
Gallente Federation
#19 - 2015-02-22 16:56:40 UTC
Oh, thanks, FoxFour, that would be really nice!
Please keep us informed on this one.
Florian Lousberg
Furtherance.
#20 - 2015-03-02 16:56:55 UTC
delivery waste wrote:
Is there any convenient way to work with market history using your client?


Depends on your definition of "convenient". If you already have a list of valid regionIDs and typeIDs, you can just "cheat" by building your own URLs.

So, for example, to get yesterday's Tritanium from The Forge, you could just use:
>>> import pycrest
>>> eve = pycrest.EVE()
>>> history = eve.get('https://public-crest.eveonline.com/market/10000002/types/34/history/')
>>> print history['items'][-1]
{u'volume': 16016039000, u'orderCount': 2760, u'lowPrice': 5.92, u'highPrice': 6.05,
u'avgPrice': 5.99, u'volume_str': u'16016039000', u'orderCount_str': u'2760',
u'date': u'2015-03-01T00:00:00'}


I know, it's not the recommended way of using CREST, but it works. (unless FoxFour changes URLs without notice ;-) )
12Next page