Skip to content
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

Incompatibility with Python 3 #115

Open
hugoncosta opened this issue Jul 4, 2018 · 14 comments
Open

Incompatibility with Python 3 #115

hugoncosta opened this issue Jul 4, 2018 · 14 comments

Comments

@hugoncosta
Copy link

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)?

@ramosjoel
Copy link

ramosjoel commented Jul 12, 2018

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

@admhpr
Copy link

admhpr commented Jul 15, 2018

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

@talhakabakus
Copy link

talhakabakus commented Dec 16, 2018

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.

@luisberns
Copy link

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.

@ramosjoel
Copy link

@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 :) 🤷‍♂

@luisberns
Copy link

@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
Copy link

Just an update, I'm now using the code from this PR #126

@ramosjoel
Copy link

@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 :)

@luisberns
Copy link

@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!

@ramosjoel
Copy link

@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.

@luisberns
Copy link

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.

@ramosjoel
Copy link

ramosjoel commented Nov 3, 2019

@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 🤷‍♂

@longopy
Copy link

longopy commented May 4, 2021

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

Thank you @admhpr. This fixed my problem. 🥇

@GusarovArtem
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants