From 2ad2fba8abf295dbe8e2a555db5be412e0055995 Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Thu, 18 Feb 2021 15:50:18 +0200 Subject: [PATCH 1/7] adding current FB version & setting global vars in app.context --- examples/flask/app/templates/base.html | 2 +- examples/flask/app/views.py | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/flask/app/templates/base.html b/examples/flask/app/templates/base.html index 544a6e4a..c3285197 100644 --- a/examples/flask/app/templates/base.html +++ b/examples/flask/app/templates/base.html @@ -30,7 +30,7 @@ appId : '{{ app_id }}', status : true, cookie : true, - version : 'v2.11', + version : 'v9.0', xfbml : true }); diff --git a/examples/flask/app/views.py b/examples/flask/app/views.py index d18e311d..69804f38 100644 --- a/examples/flask/app/views.py +++ b/examples/flask/app/views.py @@ -10,16 +10,22 @@ FB_APP_SECRET = "" +@app.context_processor +def inject_facebook_vars(): + return dict(app_id=FB_APP_ID, + name=FB_APP_NAME) + + @app.route("/") def index(): # If a user was set in the get_current_user function before the request, # the user is logged in. if g.user: return render_template( - "index.html", app_id=FB_APP_ID, app_name=FB_APP_NAME, user=g.user - ) + "index.html", user=g.user) + # Otherwise, a user is not logged in. - return render_template("login.html", app_id=FB_APP_ID, name=FB_APP_NAME) + return render_template("login.html") @app.route("/logout") @@ -55,8 +61,7 @@ def get_current_user(): # Attempt to get the short term access token for the current user. result = get_user_from_cookie( - cookies=request.cookies, app_id=FB_APP_ID, app_secret=FB_APP_SECRET - ) + cookies=request.cookies,app_secret=FB_APP_SECRET) # If there is no result, we assume the user is not logged in. if result: From 649aac6e36e8d6433d9959c9c9cbd395862b3358 Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Thu, 18 Feb 2021 15:54:28 +0200 Subject: [PATCH 2/7] adding flask_sqlalchemy --- examples/flask/app/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/flask/app/__init__.py b/examples/flask/app/__init__.py index 0329ab2d..f9542810 100644 --- a/examples/flask/app/__init__.py +++ b/examples/flask/app/__init__.py @@ -1,5 +1,5 @@ from flask import Flask -from flask.ext.sqlalchemy import SQLAlchemy +from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config.from_object("config") From cab66e0c75fcdb8202f069ecec2e8f64da5f9e28 Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Wed, 24 Feb 2021 23:18:06 +0200 Subject: [PATCH 3/7] refomatted code using python-black --- docs/conf.py | 57 +++++++++++++++++++++++-------------- examples/flask/app/views.py | 9 ++---- examples/tornado/example.py | 4 +-- facebook/__init__.py | 39 +++++++------------------ test/__init__.py | 15 ++-------- test/test_access_tokens.py | 4 +-- test/test_connections.py | 8 ++---- test/test_graphapierror.py | 3 +- test/test_permissions.py | 8 ++---- test/test_search.py | 4 +-- test/test_versions.py | 24 ++++------------ 11 files changed, 65 insertions(+), 110 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 70b10a24..09214505 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,29 +29,29 @@ extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Facebook SDK for Python' -copyright = u'2010 Facebook, 2015 Mobolic' +project = u"Facebook SDK for Python" +copyright = u"2010 Facebook, 2015 Mobolic" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '.'.join(__version__.split('.')[:2]) # noqa: F821 +version = ".".join(__version__.split(".")[:2]) # noqa: F821 # The full version, including alpha/beta/rc tags. -release = __version__ # noqa: F821 +release = __version__ # noqa: F821 # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -65,7 +65,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -83,7 +83,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -93,7 +93,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -122,7 +122,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. @@ -166,7 +166,7 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'FacebookSDKforPythondoc' +htmlhelp_basename = "FacebookSDKforPythondoc" # -- Options for LaTeX output ------------------------------------------------- @@ -174,10 +174,8 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # 'preamble': '', } @@ -186,9 +184,13 @@ # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ - ('index', 'FacebookSDKforPython.tex', - u'Facebook SDK for Python Documentation', - u'Martey Dodoo', 'manual'), + ( + "index", + "FacebookSDKforPython.tex", + u"Facebook SDK for Python Documentation", + u"Martey Dodoo", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -217,8 +219,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'facebooksdkforpython', u'Facebook SDK for Python Documentation', - [u'Martey Dodoo'], 1) + ( + "index", + "facebooksdkforpython", + u"Facebook SDK for Python Documentation", + [u"Martey Dodoo"], + 1, + ) ] # If true, show URL addresses after external links. @@ -231,9 +238,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'FacebookSDKforPython', u'Facebook SDK for Python Documentation', - u'Martey Dodoo', 'FacebookSDKforPython', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "FacebookSDKforPython", + u"Facebook SDK for Python Documentation", + u"Martey Dodoo", + "FacebookSDKforPython", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. diff --git a/examples/flask/app/views.py b/examples/flask/app/views.py index 69804f38..c787f28f 100644 --- a/examples/flask/app/views.py +++ b/examples/flask/app/views.py @@ -12,8 +12,7 @@ @app.context_processor def inject_facebook_vars(): - return dict(app_id=FB_APP_ID, - name=FB_APP_NAME) + return dict(app_id=FB_APP_ID, name=FB_APP_NAME) @app.route("/") @@ -21,8 +20,7 @@ def index(): # If a user was set in the get_current_user function before the request, # the user is logged in. if g.user: - return render_template( - "index.html", user=g.user) + return render_template("index.html", user=g.user) # Otherwise, a user is not logged in. return render_template("login.html") @@ -60,8 +58,7 @@ def get_current_user(): return # Attempt to get the short term access token for the current user. - result = get_user_from_cookie( - cookies=request.cookies,app_secret=FB_APP_SECRET) + result = get_user_from_cookie(cookies=request.cookies, app_secret=FB_APP_SECRET) # If there is no result, we assume the user is not logged in. if result: diff --git a/examples/tornado/example.py b/examples/tornado/example.py index 70201e5a..d2a2aa81 100755 --- a/examples/tornado/example.py +++ b/examples/tornado/example.py @@ -61,9 +61,7 @@ def get_current_user(self): profile["link"], cookie["access_token"], ) - user = self.db.get( - "SELECT * FROM users WHERE id = %s", profile["id"] - ) + user = self.db.get("SELECT * FROM users WHERE id = %s", profile["id"]) elif user.access_token != cookie["access_token"]: self.db.execute( "UPDATE users SET access_token = %s WHERE id = %s", diff --git a/facebook/__init__.py b/facebook/__init__.py index e91c5939..462c96d2 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -98,8 +98,7 @@ def __init__( if match is not None: if str(version) not in VALID_API_VERSIONS: raise GraphAPIError( - "Valid API versions are " - + str(VALID_API_VERSIONS).strip("[]") + "Valid API versions are " + str(VALID_API_VERSIONS).strip("[]") ) else: self.version = "v" + str(version) @@ -141,9 +140,7 @@ def get_objects(self, ids, **args): def search(self, type, **args): """https://developers.facebook.com/docs/places/search""" if type not in VALID_SEARCH_TYPES: - raise GraphAPIError( - "Valid types are: %s" % ", ".join(VALID_SEARCH_TYPES) - ) + raise GraphAPIError("Valid types are: %s" % ", ".join(VALID_SEARCH_TYPES)) args["type"] = type return self.request(self.version + "/search/", args) @@ -206,15 +203,11 @@ def put_like(self, object_id): def delete_object(self, id): """Deletes the object with the given ID from the graph.""" - return self.request( - "{0}/{1}".format(self.version, id), method="DELETE" - ) + return self.request("{0}/{1}".format(self.version, id), method="DELETE") def delete_request(self, user_id, request_id): """Deletes the Request with the given ID for the given user.""" - return self.request( - "{0}_{1}".format(request_id, user_id), method="DELETE" - ) + return self.request("{0}_{1}".format(request_id, user_id), method="DELETE") def put_photo(self, image, album_path="me/photos", **kwargs): """ @@ -253,9 +246,7 @@ def get_version(self): except Exception: raise GraphAPIError("API version number not available") - def request( - self, path, args=None, post_args=None, files=None, method=None - ): + def request(self, path, args=None, post_args=None, files=None, method=None): """Fetches the given path in the Graph API. We translate args to a valid query string. If post_args is @@ -343,9 +334,7 @@ def get_app_access_token(self, app_id, app_secret, offline=False): "{0}/oauth/access_token".format(self.version), args=args )["access_token"] - def get_access_token_from_code( - self, code, redirect_uri, app_id, app_secret - ): + def get_access_token_from_code(self, code, redirect_uri, app_id, app_secret): """Get an access token from the "code" returned from an OAuth dialog. Returns a dict containing the user-specific access token and its @@ -359,9 +348,7 @@ def get_access_token_from_code( "client_secret": app_secret, } - return self.request( - "{0}/oauth/access_token".format(self.version), args - ) + return self.request("{0}/oauth/access_token".format(self.version), args) def extend_access_token(self, app_id, app_secret): """ @@ -377,9 +364,7 @@ def extend_access_token(self, app_id, app_secret): "fb_exchange_token": self.access_token, } - return self.request( - "{0}/oauth/access_token".format(self.version), args=args - ) + return self.request("{0}/oauth/access_token".format(self.version), args=args) def debug_access_token(self, token, app_id, app_secret): """ @@ -491,9 +476,7 @@ def parse_signed_request(signed_request, app_secret): sig = base64.urlsafe_b64decode( encoded_sig + "=" * ((4 - len(encoded_sig) % 4) % 4) ) - data = base64.urlsafe_b64decode( - payload + "=" * ((4 - len(payload) % 4) % 4) - ) + data = base64.urlsafe_b64decode(payload + "=" * ((4 - len(payload) % 4) % 4)) except IndexError: # Signed request was malformed. return False @@ -513,9 +496,7 @@ def parse_signed_request(signed_request, app_secret): app_secret = app_secret.encode("ascii") payload = payload.encode("ascii") - expected_sig = hmac.new( - app_secret, msg=payload, digestmod=hashlib.sha256 - ).digest() + expected_sig = hmac.new(app_secret, msg=payload, digestmod=hashlib.sha256).digest() if sig != expected_sig: return False diff --git a/test/__init__.py b/test/__init__.py index c03946f0..9f5a9f0d 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -40,9 +40,7 @@ def setUp(self): def tearDown(self): """Deletes the test users included in the test user list.""" - token = facebook.GraphAPI().get_app_access_token( - self.app_id, self.secret, True - ) + token = facebook.GraphAPI().get_app_access_token(self.app_id, self.secret, True) graph = facebook.GraphAPI(token) for user in self.test_users: @@ -50,12 +48,7 @@ def tearDown(self): del self.test_users[:] def assert_raises_multi_regex( - self, - expected_exception, - expected_regexp, - callable_obj=None, - *args, - **kwargs + self, expected_exception, expected_regexp, callable_obj=None, *args, **kwargs ): """ Custom function to backport assertRaisesRegexp to all supported @@ -71,9 +64,7 @@ def assert_raises_multi_regex( def create_test_users(self, app_id, graph, amount): """Function for creating test users.""" for i in range(amount): - u = graph.request( - app_id + "/accounts/test-users", {}, {}, method="POST" - ) + u = graph.request(app_id + "/accounts/test-users", {}, {}, method="POST") self.test_users.append(u) def create_friend_connections(self, user, friends): diff --git a/test/test_access_tokens.py b/test/test_access_tokens.py index 6d92f0a2..5d861c60 100644 --- a/test/test_access_tokens.py +++ b/test/test_access_tokens.py @@ -14,9 +14,7 @@ def test_extend_access_token(self): try: facebook.GraphAPI().extend_access_token(self.app_id, self.secret) except facebook.GraphAPIError as e: - self.assertEqual( - e.message, "fb_exchange_token parameter not specified" - ) + self.assertEqual(e.message, "fb_exchange_token parameter not specified") def test_bogus_access_token(self): graph = facebook.GraphAPI(access_token="wrong_token") diff --git a/test/test_connections.py b/test/test_connections.py index d70e3994..46b1c226 100644 --- a/test/test_connections.py +++ b/test/test_connections.py @@ -6,15 +6,11 @@ class FacebookAllConnectionsMethodTestCase(FacebookTestCase): def test_function_with_zero_connections(self): - token = facebook.GraphAPI().get_app_access_token( - self.app_id, self.secret, True - ) + token = facebook.GraphAPI().get_app_access_token(self.app_id, self.secret, True) graph = facebook.GraphAPI(token) self.create_test_users(self.app_id, graph, 1) - friends = graph.get_all_connections( - self.test_users[0]["id"], "friends" - ) + friends = graph.get_all_connections(self.test_users[0]["id"], "friends") self.assertTrue(inspect.isgenerator(friends)) self.assertTrue(len(list(friends)) == 0) diff --git a/test/test_graphapierror.py b/test/test_graphapierror.py index 2dc5ce85..1fe9f16c 100644 --- a/test/test_graphapierror.py +++ b/test/test_graphapierror.py @@ -17,8 +17,7 @@ def test_setting_error_subcode(self): """Verify that error subcode is set properly.""" # Generate random string. error_subcode = "".join( - random.choice(string.ascii_letters + string.digits) - for _ in range(10) + random.choice(string.ascii_letters + string.digits) for _ in range(10) ) result = { "error": { diff --git a/test/test_permissions.py b/test/test_permissions.py index eb3aaf53..51e9fa98 100644 --- a/test/test_permissions.py +++ b/test/test_permissions.py @@ -13,9 +13,7 @@ class FacebookUserPermissionsTestCase(FacebookTestCase): """ def test_get_user_permissions_node(self): - token = facebook.GraphAPI().get_app_access_token( - self.app_id, self.secret, True - ) + token = facebook.GraphAPI().get_app_access_token(self.app_id, self.secret, True) graph = facebook.GraphAPI(access_token=token) self.create_test_users(self.app_id, graph, 1) permissions = graph.get_permissions(self.test_users[0]["id"]) @@ -25,8 +23,6 @@ def test_get_user_permissions_node(self): self.assertFalse("email" in permissions) def test_get_user_permissions_nonexistant_user(self): - token = facebook.GraphAPI().get_app_access_token( - self.app_id, self.secret, True - ) + token = facebook.GraphAPI().get_app_access_token(self.app_id, self.secret, True) with self.assertRaises(facebook.GraphAPIError): facebook.GraphAPI(token).get_permissions(1) diff --git a/test/test_search.py b/test/test_search.py index e56c7f33..8586e866 100644 --- a/test/test_search.py +++ b/test/test_search.py @@ -23,6 +23,4 @@ def setUp(self): def test_invalid_search_type(self): """Verify that search method fails when an invalid type is passed.""" search_args = {"type": "foo", "q": "bar"} - self.assertRaises( - facebook.GraphAPIError, self.graph.search, search_args - ) + self.assertRaises(facebook.GraphAPIError, self.graph.search, search_args) diff --git a/test/test_versions.py b/test/test_versions.py index 7cb33970..1667dcb9 100644 --- a/test/test_versions.py +++ b/test/test_versions.py @@ -8,9 +8,7 @@ class FacebookAPIVersionTestCase(FacebookTestCase): def test_no_version(self): graph = facebook.GraphAPI() self.assertNotEqual(graph.version, None, "Version should not be None.") - self.assertNotEqual( - graph.version, "", "Version should not be an empty string." - ) + self.assertNotEqual(graph.version, "", "Version should not be an empty string.") def test_valid_versions(self): for version in facebook.VALID_API_VERSIONS: @@ -18,20 +16,10 @@ def test_valid_versions(self): self.assertEqual(str(graph.get_version()), version) def test_invalid_version(self): - self.assertRaises( - facebook.GraphAPIError, facebook.GraphAPI, version=1.2 - ) + self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version=1.2) def test_invalid_format(self): - self.assertRaises( - facebook.GraphAPIError, facebook.GraphAPI, version="2.a" - ) - self.assertRaises( - facebook.GraphAPIError, facebook.GraphAPI, version="a.1" - ) - self.assertRaises( - facebook.GraphAPIError, facebook.GraphAPI, version=2.23 - ) - self.assertRaises( - facebook.GraphAPIError, facebook.GraphAPI, version="2.23" - ) + self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version="2.a") + self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version="a.1") + self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version=2.23) + self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version="2.23") From 0895b0d26cb806e297b64c41c16961135f1a63ea Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Wed, 24 Feb 2021 23:26:59 +0200 Subject: [PATCH 4/7] adding python 3.9 support --- setup.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/setup.py b/setup.py index 13e8373d..32a1f5a8 100644 --- a/setup.py +++ b/setup.py @@ -4,27 +4,28 @@ exec(open("facebook/version.py").read()) setup( - name='facebook-sdk', - version=__version__, # noqa: F821 - description='This client library is designed to support the Facebook ' - 'Graph API and the official Facebook JavaScript SDK, which ' - 'is the canonical way to implement Facebook authentication.', - author='Facebook', - maintainer='Martey Dodoo', - maintainer_email='martey+facebook-sdk@mobolic.com', - url='https://github.com/mobolic/facebook-sdk', - license='Apache', + name="facebook-sdk", + version=__version__, # noqa: F821 + description="This client library is designed to support the Facebook " + "Graph API and the official Facebook JavaScript SDK, which " + "is the canonical way to implement Facebook authentication.", + author="Facebook", + maintainer="Martey Dodoo", + maintainer_email="martey+facebook-sdk@mobolic.com", + url="https://github.com/mobolic/facebook-sdk", + license="Apache", packages=["facebook"], long_description=open("README.rst").read(), classifiers=[ - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", ], - install_requires=['requests'], + install_requires=["requests"], tests_require=["coverage"], ) From 24a6c62419df9693f684692f5d9044d058140a16 Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Wed, 24 Feb 2021 23:36:46 +0200 Subject: [PATCH 5/7] added version 9.0 of facebook SDK --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 462c96d2..661bec43 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -41,7 +41,7 @@ FACEBOOK_GRAPH_URL = "https://graph.facebook.com/" FACEBOOK_WWW_URL = "https://www.facebook.com/" FACEBOOK_OAUTH_DIALOG_PATH = "dialog/oauth?" -VALID_API_VERSIONS = ["3.1", "3.2", "3.3", "4.0", "5.0", "6.0", "7.0", "8.0"] +VALID_API_VERSIONS = ["3.1", "3.2", "3.3", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0"] VALID_SEARCH_TYPES = ["place", "placetopic"] From ced4f4530434801030f141d3a91b335682ae66b6 Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Wed, 24 Feb 2021 23:46:02 +0200 Subject: [PATCH 6/7] changed the default version in facebook/__init__.py --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 661bec43..51d2c92f 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -84,7 +84,7 @@ def __init__( app_secret=None, ): # The default version is only used if the version kwarg does not exist. - default_version = VALID_API_VERSIONS[0] + default_version = VALID_API_VERSIONS[8] self.access_token = access_token self.timeout = timeout From 0802cf15b9fef8a1f57df239f675e1f2f78c55c9 Mon Sep 17 00:00:00 2001 From: Beven Nyamande Date: Fri, 14 May 2021 23:54:31 +0200 Subject: [PATCH 7/7] reverted changes on API VERSION in __init__.py --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 51d2c92f..661bec43 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -84,7 +84,7 @@ def __init__( app_secret=None, ): # The default version is only used if the version kwarg does not exist. - default_version = VALID_API_VERSIONS[8] + default_version = VALID_API_VERSIONS[0] self.access_token = access_token self.timeout = timeout