From aa76e2097ac68d136ee9b24575a0b89d8989549c Mon Sep 17 00:00:00 2001 From: darune Date: Mon, 4 May 2015 13:59:27 +0200 Subject: [PATCH 1/2] Add python 3.4 support --- examples/http_api.py | 27 ++++++++++++++++----------- linkedin/linkedin.py | 24 +++++++++++++++--------- linkedin/utils.py | 35 ++++++++++++++++++++--------------- requirements.txt | 1 + setup.py | 2 ++ 5 files changed, 54 insertions(+), 35 deletions(-) diff --git a/examples/http_api.py b/examples/http_api.py index 92c63a4..84059d8 100644 --- a/examples/http_api.py +++ b/examples/http_api.py @@ -1,13 +1,18 @@ __author__ = 'Samuel Marks ' __version__ = '0.1.0' -from SocketServer import ThreadingTCPServer -from SimpleHTTPServer import SimpleHTTPRequestHandler +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse + +from socketserver import ThreadingTCPServer +from http.server import SimpleHTTPRequestHandler + from webbrowser import open_new_tab from json import dumps -from urlparse import urlparse from os import environ -from types import NoneType + from linkedin.linkedin import LinkedInAuthentication, LinkedInApplication, PERMISSIONS @@ -38,35 +43,35 @@ def json_headers(self, status_code=200): def do_GET(self): parsedurl = urlparse(self.path) - authed = type(liw.authentication.token) is not NoneType + authed = liw.authentication.token is not None if parsedurl.path == '/code': self.json_headers() liw.authentication.authorization_code = params_to_d(self.path).get('code') self.wfile.write(dumps({'access_token': liw.authentication.get_access_token(), - 'routes': filter(lambda d: not d.startswith('_'), dir(liw.application))})) + 'routes': list(filter(lambda d: not d.startswith('_'), dir(liw.application)))}).encode('utf8')) elif parsedurl.path == '/routes': self.json_headers() - self.wfile.write(dumps({'routes': filter(lambda d: not d.startswith('_'), dir(liw.application))})) + self.wfile.write(dumps({'routes': list(filter(lambda d: not d.startswith('_'), dir(liw.application)))}).encode('utf8')) elif not authed: self.json_headers() if not globals()['run_already']: open_new_tab(liw.authentication.authorization_url) globals()['run_already'] = True - self.wfile.write(dumps({'path': self.path, 'authed': type(liw.authentication.token) is NoneType})) + self.wfile.write(dumps({'path': self.path, 'authed': type(liw.authentication.token) is None}).encode('utf8')) elif authed and len(parsedurl.path) and parsedurl.path[1:] in dir(liw.application): self.json_headers() - self.wfile.write(dumps(getattr(liw.application, parsedurl.path[1:])())) + self.wfile.write(dumps(getattr(liw.application, parsedurl.path[1:])()).encode('utf8')) else: self.json_headers(501) - self.wfile.write(dumps({'error': 'NotImplemented'})) + self.wfile.write(dumps({'error': 'NotImplemented'}).encode('utf8')) if __name__ == '__main__': httpd = ThreadingTCPServer(('localhost', PORT), CustomHandler) - print 'Server started on port:', PORT + print('Server started on port:{}'.format(PORT)) httpd.serve_forever() diff --git a/linkedin/linkedin.py b/linkedin/linkedin.py index c138fce..5c3a1b0 100644 --- a/linkedin/linkedin.py +++ b/linkedin/linkedin.py @@ -1,8 +1,13 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals import contextlib import hashlib import random -import urllib + +try: + from urllib.parse import quote, quote_plus +except ImportError: + from urllib import quote, quote_plus import requests from requests_oauthlib import OAuth1 @@ -93,7 +98,7 @@ def authorization_url(self): 'redirect_uri': self.redirect_uri} # urlencode uses quote_plus when encoding the query string so, # we ought to be encoding the qs by on our own. - qsl = ['%s=%s' % (urllib.quote(k), urllib.quote(v)) for k, v in qd.items()] + qsl = ['%s=%s' % (quote(k), quote(v)) for k, v in qd.items()] return '%s?%s' % (self.AUTHORIZATION_URL, '&'.join(qsl)) @property @@ -102,7 +107,8 @@ def last_error(self): def _make_new_state(self): return hashlib.md5( - '%s%s' % (random.randrange(0, 2 ** 63), self.secret)).hexdigest() + '{}{}'.format(random.randrange(0, 2 ** 63), self.secret).encode("utf8") + ).hexdigest() def get_access_token(self, timeout=60): assert self.authorization_code, 'You must first get the authorization code' @@ -174,7 +180,7 @@ def get_profile(self, member_id=None, member_url=None, selectors=None, else: url = '%s/id=%s' % (ENDPOINTS.PEOPLE, str(member_id)) elif member_url: - url = '%s/url=%s' % (ENDPOINTS.PEOPLE, urllib.quote_plus(member_url)) + url = '%s/url=%s' % (ENDPOINTS.PEOPLE, quote_plus(member_url)) else: url = '%s/~' % ENDPOINTS.PEOPLE if selectors: @@ -200,7 +206,7 @@ def get_picture_urls(self, member_id=None, member_url=None, url = '%s/id=%s/picture-urls::(original)' % (ENDPOINTS.PEOPLE, str(member_id)) elif member_url: url = '%s/url=%s/picture-urls::(original)' % (ENDPOINTS.PEOPLE, - urllib.quote_plus(member_url)) + quote_plus(member_url)) else: url = '%s/~/picture-urls::(original)' % ENDPOINTS.PEOPLE @@ -214,7 +220,7 @@ def get_connections(self, member_id=None, member_url=None, selectors=None, url = '%s/id=%s/connections' % (ENDPOINTS.PEOPLE, str(member_id)) elif member_url: url = '%s/url=%s/connections' % (ENDPOINTS.PEOPLE, - urllib.quote_plus(member_url)) + quote_plus(member_url)) else: url = '%s/~/connections' % ENDPOINTS.PEOPLE if selectors: @@ -230,7 +236,7 @@ def get_memberships(self, member_id=None, member_url=None, group_id=None, url = '%s/id=%s/group-memberships' % (ENDPOINTS.PEOPLE, str(member_id)) elif member_url: url = '%s/url=%s/group-memberships' % (ENDPOINTS.PEOPLE, - urllib.quote_plus(member_url)) + quote_plus(member_url)) else: url = '%s/~/group-memberships' % ENDPOINTS.PEOPLE @@ -307,7 +313,7 @@ def like_post(self, post_id, action): url = '%s/%s/relation-to-viewer/is-liked' % (ENDPOINTS.POSTS, str(post_id)) try: self.make_request('PUT', url, data=json.dumps(action)) - except (requests.ConnectionError, requests.HTTPError), error: + except (requests.ConnectionError, requests.HTTPError) as error: raise LinkedInError(error.message) else: return True @@ -319,7 +325,7 @@ def comment_post(self, post_id, comment): url = '%s/%s/comments' % (ENDPOINTS.POSTS, str(post_id)) try: self.make_request('POST', url, data=json.dumps(post)) - except (requests.ConnectionError, requests.HTTPError), error: + except (requests.ConnectionError, requests.HTTPError) as error: raise LinkedInError(error.message) else: return True diff --git a/linkedin/utils.py b/linkedin/utils.py index 7eda996..8b10aeb 100644 --- a/linkedin/utils.py +++ b/linkedin/utils.py @@ -1,11 +1,8 @@ # -*- coding: utf-8 -*- import requests from .exceptions import LinkedInError, get_exception_for_error_code - -try: - from cStringIO import StringIO -except ImportError: - from StringIO import StringIO +import sys +from io import StringIO try: import simplejson as json @@ -16,6 +13,22 @@ import json +if sys.version_info < (3,): + import __builtin__ + + def to_utf8(x): + return __builtin__.unicode(x) + + def to_string(x): + return str(x) +else: + def to_utf8(x): + return x + + def to_string(x): + return x + + def enum(enum_type='enum', base_classes=None, methods=None, **attrs): """ Generates a enumeration with the given attributes. @@ -31,21 +44,13 @@ def __init__(instance, *args, **kwargs): methods = {} base_classes = base_classes + (object,) - for k, v in methods.iteritems(): + for k, v in methods.items(): methods[k] = classmethod(v) attrs['enums'] = attrs.copy() methods.update(attrs) methods['__init__'] = __init__ - return type(enum_type, base_classes, methods) - - -def to_utf8(st): - if isinstance(st, unicode): - return st.encode('utf-8') - else: - return bytes(st) - + return type(to_string(enum_type), base_classes, methods) def raise_for_error(response): try: diff --git a/requirements.txt b/requirements.txt index 11b99d9..51a21b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ +future==0.14.3 requests requests_oauthlib \ No newline at end of file diff --git a/setup.py b/setup.py index 18b3ed3..5fa1bd9 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,8 @@ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', 'Natural Language :: English', ], keywords='linkedin python', From c019e53d60d6e9e1434a9c9336e32e722989e5ff Mon Sep 17 00:00:00 2001 From: John Franey Date: Wed, 17 Jun 2015 12:04:03 -0300 Subject: [PATCH 2/2] Update README.md Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 117636a..9967ef0 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ application = server.quick_api(KEY, SECRET) This will print the authorization url to the screen. Go into that URL using a browser to grant access to the application. After you do so, the method will return with an API object you can now use. ## Profile API -The Profile API returns a member's LinkedIn profile. You can use this call to return one of two versions of a user's profile which are **public profile** and **standart profile**. For more information, check out the [documentation](http://developers.linkedin.com/documents/profile-api). +The Profile API returns a member's LinkedIn profile. You can use this call to return one of two versions of a user's profile which are **public profile** and **standard profile**. For more information, check out the [documentation](http://developers.linkedin.com/documents/profile-api). ```python application.get_profile()