python-linkedin icon indicating copy to clipboard operation
python-linkedin copied to clipboard

Incompatibility with Python 3

Open hugoncosta opened this issue 6 years ago • 14 comments

Putting it simply:

File "/usr/local/lib/python3.6/dist-packages/linkedin/server.py", line 24 print auth.authorization_url ^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(int auth.authorization_url)?

hugoncosta avatar Jul 04 '18 09:07 hugoncosta

Ran into this importing the linkedin module due to the older exception handling syntax. I see the __future__ module used in this file so it looks like python 3 compatibility is being attempted or planned.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Anaconda3\lib\site-packages\linkedin\linkedin.py", line 294
    except (requests.ConnectionError, requests.HTTPError), error:
                                                         ^
SyntaxError: invalid syntax

ramosjoel avatar Jul 12 '18 00:07 ramosjoel

pip install --upgrade https://github.com/ozgur/python-linkedin/tarball/master seems to solve the issue.

admhpr avatar Jul 15 '18 14:07 admhpr

I get ModuleNotFoundError: No module named 'BaseHTTPServer' when I call quick_api function of the module server. Between my Python version is 3.7.1 which is currently the latest version.

talhakabakus avatar Dec 16 '18 20:12 talhakabakus

pip install --upgrade https://github.com/ozgur/python-linkedin/tarball/master seems to solve the issue.

So, I could use this solution when I was running the import on a Jupyter Notebook (I'm trying to get the data for doing some analysis), but this doesn't help when using IPython in the Terminal. @harps116

I'm trying to execute the steps on the Quick usage, I have all the requirements installed, including future.

Also, I saw that most of the issues are related to python3 compatibility or so.

luisberns avatar Nov 01 '19 23:11 luisberns

@luisberns you might want to try taking a look at this fork which looks like has some updates for Python 3 compatibility.

https://github.com/HootsuiteLabs/python-linkedin-v2

Even that fork's setup.py lists the latest Python version as 3.4. So it's possible that it works on later versions, or it hasn't been tested with later versions, or they just forgot to update the setup.py, etc. But might as well try it! 😄 That said, even if you have all the requirements installed, if all the files in this library haven't been updated to work with Python 3, you run your code with Python 3, and your code imports one of those files that has Python 2-specific syntax, then the Python 3 interpreter will choke on when it encounters the Python 2-specific syntax.

As for your jupyter notebook, it's possible that your jupyter notebook kernel is running python 2, but your IPython terminal is starting a Python 3 interpreter. I.e., when you installed jupyter, it's possible the Python 2 lib got installed into your Python 2's site-packages (if you have one). Your Python versions should be visible in the upper-right corner of the notebook under the logout button, and also at the top of your IPython prompt. I'm not sure this is the case for you but just throwing it out there as a possibility :) 🤷‍♂

ramosjoel avatar Nov 02 '19 01:11 ramosjoel

@ramosjoel Thank you for the reply!

I'm using Conda and my Python version is 3.6, I'm trying to rewrite the server.py to get compatibility with the later version because in the PR that you send the server.py is still using some modules from python 2.

Now I'm able to run quick_api function within the server to make the request, but my browser isn't opening to make the authentication and I don't know why.

I'm attaching my server.py code bellow.

# -*- coding: utf-8 -*-
import http.server
from urllib import parse

from .linkedin import LinkedInApplication, LinkedInAuthentication, PERMISSIONS


def quick_api(api_key, secret_key, port=8000):
    """
    This method helps you get access to linkedin api quickly when using it
    from the interpreter.
    Notice that this method creates http server and wait for a request, so it
    shouldn't be used in real production code - it's just an helper for debugging

    The usage is basically:
    api = quick_api(KEY, SECRET)
    After you do that, it will print a URL to the screen which you must go in
    and allow the access, after you do that, the method will return with the api
    object.
    """
    auth = LinkedInAuthentication(api_key, secret_key, 'http://localhost:8000/',
                                  PERMISSIONS.enums.values())
    app = LinkedInApplication(authentication=auth)
    print(auth.authorization_url)
    _wait_for_user_to_enter_browser(app, port)
    return app

def _wait_for_user_to_enter_browser(app, port):
    class MyHandler(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            p = self.path.split('?')
            if len(p) > 1:
                params = parse.parse_qs(p[1], True, True)
                app.authentication.authorization_code = params['code'][0]
                app.authentication.get_access_token()

    server_address = ('', port)
    httpd = http.server.HTTPServer(server_address, MyHandler)
    httpd.handle_request()

luisberns avatar Nov 02 '19 14:11 luisberns

Just an update, I'm now using the code from this PR https://github.com/ozgur/python-linkedin/pull/126/

luisberns avatar Nov 02 '19 15:11 luisberns

@luisberns the docstring on that function suggests that it will print an authorization url. Looking at the code you pasted, you can see that is the case.

app = LinkedInApplication(authentication=auth)
print(auth.authorization_url)

From the docstring:

...it will print a URL to the screen which you must go in and allow the access...

So I expect that once that authorization url is printed to your terminal (or notebook) you'll have to launch the browser yourself, navigate to the printed URL, and log in to authenticate yourself. Evidently this function waits for that to happen before it continues, and then returns an authenticated LinkedInApplication instance.

That's at least how it looks to me. Hope this helps :)

ramosjoel avatar Nov 02 '19 15:11 ramosjoel

@ramosjoel Yes, got it! I could open by clicking the link, now I'm trying to get the request to work.

First I had problems with the urllib.parse.parseurl.parse_qs that apparently changed to urllib.parse.parse_qs, but I got an error calling the params['code'][0] accusing a Keyword error 'code'.

I'm trying to figure this out, if you have any suggestions, thank you for your help until now!

luisberns avatar Nov 02 '19 16:11 luisberns

@luisberns the parse_qs function takes the query-string portion of a URL and returns a dictionary.

For example, for the URL https://www.google.com/search?q=tesla, q=tesla is the query-string. The function would take query-string q=tesla as input, and return { 'q': ['tesla'] } as output.

In [1]: from urllib import parse

In [2]: qs = 'q=tesla'

In [3]: params = parse.parse_qs(qs)

In [4]: params
Out[4]: {'q': ['tesla']}

Given that in my example, 'q' is the dictionary key, and ['tesla'] is the dictionary value, your error suggests the following:

  • params is a dictionary
  • your code expects the params dictionary to have a key named 'code' in it.

If this is the case, you should have gotten the, KeyError: 'code' exception.

This implies that your params dict does not have a key named 'code', which means the query-string, p[1], did not have a code=something param in it, which might mean the URL query-string param name has changed since this function was written. Anything's possible.

If I were you, I would run your code in a debugger and step through to inspect what the values are. Alternatively, if you run your program on the command line, you can pass python the -i option (e.g., python -i your_script.py) so that when the program hits the error, it drops you into the python interpreter in the context of your program so that you can inspect the values of some of these variables.

ramosjoel avatar Nov 02 '19 17:11 ramosjoel

Oh perfect, now I understand better how this function works, thanks.

I managed to make the request or at least I'm not getting an error anymore, but I'm still getting a response error: unauthorized_scope_error on the DevTools > Network tab.

One thing is that the Redirect URL without ending with a '.com' couldn't make the request, once I changed I was able to perform the request but got the error above.

luisberns avatar Nov 02 '19 21:11 luisberns

@luisberns this is how it looks to me:

  1. quick_api() prints the URL you that you have to open in the browser to log in.
  2. After it prints, it calls _wait_for_user_to_enter_browser()
  3. Previously you were getting stuck at line 35 of that function where params['code'][0] is, but you've now made it to line 36 where it calls app.authentication.get_access_token()
  4. The get_access_token() is defined at line 113 in linkedin.py where it sends the POST request with requests.post(), stores the response, and then calls raise_for_error(response) at line 121 to check if the response contains an error.
  5. raise_for_error() (in utils.py) checks if the response object contains an error, and then pulls out the error message and raises an error with that message.

This hides the response code from you which I think would be helpful to know, but judging by the error message that was printed for you, I would guess that you received a 401 response which would mean that your credentials are unauthorized.

In fact, it looks like the author of the library grabs the status code of the response so that it can be looked up in a dictionary that maps bad status codes to custom exceptions so as to raise an exception that's specific to the error you received.

ERROR_CODE_EXCEPTION_MAPPING = {
    400: LinkedInBadRequestError,
    401: LinkedInUnauthorizedError,
    402: LinkedInPaymentRequiredError,
    403: LinkedInForbiddenError,
    404: LinkedInNotFoundError,
    409: LinkedInForbiddenError,
    500: LinkedInInternalServiceError}

Thus, if you did receive a 401, you should have seen a LinkedInUnauthorizedError exception, which would mean that the actual LinkedIn REST API endpoint received your request and determined that either your credentials are wrong, or not recognized, or something like that. You'll have to look into that 🤷‍♂

ramosjoel avatar Nov 03 '19 17:11 ramosjoel

pip install --upgrade https://github.com/ozgur/python-linkedin/tarball/master seems to solve the issue.

Thank you @admhpr. This fixed my problem. 🥇

longopy avatar May 04 '21 20:05 longopy

pip install --upgrade https://github.com/ozgur/python-linkedin/tarball/master Thank you @admhpr

GusarovArtem avatar Jun 13 '23 22:06 GusarovArtem