From 143a38f136482ec339d525746938750af7dec3d0 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 01:12:23 -0500 Subject: [PATCH 001/269] Happy New Year: update dates for 2014. --- docs/conf.py | 2 +- tests/test_facebook.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 3f396bb1..0927c371 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,7 +41,7 @@ # General information about the project. project = u'Facebook SDK for Python' -copyright = u'2010-2013, Facebook, Python for Facebook developers' +copyright = u'2010 Facebook, 2010-2014 Python for Facebook developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/tests/test_facebook.py b/tests/test_facebook.py index f8ca0ae2..d27757c5 100644 --- a/tests/test_facebook.py +++ b/tests/test_facebook.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2013 Martey Dodoo +# Copyright 2013-2014 Martey Dodoo # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain From 6ece6cc15b7306e8f90fb341f2f32f5d95a7bf65 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 02:45:47 -0500 Subject: [PATCH 002/269] Add Travis configuration file. --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..26a99598 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: python +python: + - "2.7" + - "2.6" +install: + - "pip install . --use-mirrors" +script: pytest +env: + - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" + - secure: "nIkW8qKCSZMiyxISWoL74x08Z6/tcAfor9hvuuD0804KYqgK98EdFQWaGOnp3loPCqLTbTcmxggUgIlPgCAdkMUrur0WBE23FgbDuguBbWwOn+A984pFp6k1p+fY7/z4is3vBU8y+qLTAXQuUQxdvh9D9qJXQsp6mOyAMS8LWHM=" From 3dea4f048b35b1caa7f2afeb3388e934945ea5d5 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 02:57:29 -0500 Subject: [PATCH 003/269] Make sure all env variables are available at once. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26a99598..debcff62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,6 @@ install: - "pip install . --use-mirrors" script: pytest env: - - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" - - secure: "nIkW8qKCSZMiyxISWoL74x08Z6/tcAfor9hvuuD0804KYqgK98EdFQWaGOnp3loPCqLTbTcmxggUgIlPgCAdkMUrur0WBE23FgbDuguBbWwOn+A984pFp6k1p+fY7/z4is3vBU8y+qLTAXQuUQxdvh9D9qJXQsp6mOyAMS8LWHM=" + global: + - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" + - secure: "nIkW8qKCSZMiyxISWoL74x08Z6/tcAfor9hvuuD0804KYqgK98EdFQWaGOnp3loPCqLTbTcmxggUgIlPgCAdkMUrur0WBE23FgbDuguBbWwOn+A984pFp6k1p+fY7/z4is3vBU8y+qLTAXQuUQxdvh9D9qJXQsp6mOyAMS8LWHM=" From 27bf48c41c817a473e1e3371b6279b98524d89a7 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 03:07:18 -0500 Subject: [PATCH 004/269] Fix pytest command so tests will actually run. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index debcff62..9f90b347 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ python: - "2.6" install: - "pip install . --use-mirrors" -script: pytest +script: python pytest.py env: global: - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" From 3e317d354ca567f26c9e9a36dc03f9f0d1e14344 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 03:30:19 -0500 Subject: [PATCH 005/269] Use unittest instead of weird pytest. Since Travis mentions having pytest support, I was confused about why all builds were failing with a "pytest not found" error. I eventually realized that my dev virtualenv contains Logilab's "pytest" [0] instead of "pytest" [1]. This commit should have a passing build (fingers crossed). [0]: Part of the logilab-common module: https://bitbucket.org/logilab/logilab-common [1]: https://pypi.python.org/pypi/pytest --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9f90b347..1760ed3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ python: - "2.6" install: - "pip install . --use-mirrors" -script: python pytest.py +script: python tests/test_facebook.py env: global: - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" From 8dffeac8158b90482ee42db5a9e3b6a8e7f6383e Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 00:53:42 -0500 Subject: [PATCH 006/269] Encourage virtualenv use in install instructions. --- docs/install.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index f017eef3..95e5abe6 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -2,8 +2,11 @@ Installation ============ -We recommend using `pip`_ to install the SDK: +We recommend using `pip`_ and `virtualenv`_ to install the SDK: + virtualenv facebookenv + source facebook/bin/activate pip install facebook-sdk .. _pip: http://www.pip-installer.org/ +.. _virtualenv: http://www.virtualenv.org/ From 3e38d6ede2926bdaa35aaaa873c05487061ac182 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 14:03:58 -0500 Subject: [PATCH 007/269] Move intro & install text into index page. Merge these one-paragraph documentation subpages into the main page. --- docs/index.rst | 23 +++++++++++++++++++++-- docs/install.rst | 12 ------------ docs/intro.rst | 12 ------------ 3 files changed, 21 insertions(+), 26 deletions(-) delete mode 100644 docs/install.rst delete mode 100644 docs/intro.rst diff --git a/docs/index.rst b/docs/index.rst index 4105b041..cef38291 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,8 +11,27 @@ Contents: .. toctree:: :maxdepth: 2 - intro - install +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. You can read more about the Graph API by accessing its +`official documentation`_. + +.. _Facebook Graph API: https://developers.facebook.com/docs/reference/api/ +.. _Facebook JavaScript SDK: https://developers.facebook.com/docs/reference/javascript/ +.. _official documentation: https://developers.facebook.com/docs/reference/api/ + +============ +Installation +============ + +We recommend using `pip`_ and `virtualenv`_ to install the SDK: + + virtualenv facebookenv + source facebook/bin/activate + pip install facebook-sdk + +.. _pip: http://www.pip-installer.org/ +.. _virtualenv: http://www.virtualenv.org/ Indices and tables diff --git a/docs/install.rst b/docs/install.rst deleted file mode 100644 index 95e5abe6..00000000 --- a/docs/install.rst +++ /dev/null @@ -1,12 +0,0 @@ -============ -Installation -============ - -We recommend using `pip`_ and `virtualenv`_ to install the SDK: - - virtualenv facebookenv - source facebook/bin/activate - pip install facebook-sdk - -.. _pip: http://www.pip-installer.org/ -.. _virtualenv: http://www.virtualenv.org/ diff --git a/docs/intro.rst b/docs/intro.rst deleted file mode 100644 index f54ca4b4..00000000 --- a/docs/intro.rst +++ /dev/null @@ -1,12 +0,0 @@ -============ -Introduction -============ - -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. You can read more about the Graph API by accessing its -`official documentation`_. - -.. _Facebook Graph API: https://developers.facebook.com/docs/reference/api/ -.. _Facebook JavaScript SDK: https://developers.facebook.com/docs/reference/javascript/ -.. _official documentation: https://developers.facebook.com/docs/reference/api/ From 887730f49f5e1d7f15ebc4b2b00235daa334b6f4 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 14:33:56 -0500 Subject: [PATCH 008/269] Add info on supported Python versions. Fixes #111. --- docs/index.rst | 3 +++ setup.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index cef38291..fc5d8c50 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,9 @@ Facebook authentication. You can read more about the Graph API by accessing its Installation ============ +The SDK currently supports Python 2.5, 2.6, and 2.7. No external packages are +required. + We recommend using `pip`_ and `virtualenv`_ to install the SDK: virtualenv facebookenv diff --git a/setup.py b/setup.py index f10951d9..305571a2 100644 --- a/setup.py +++ b/setup.py @@ -18,5 +18,8 @@ long_description=open("README.rst").read(), classifiers=[ 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', ], ) From 2430f11f62961faaf3a0b3242f3d8c5c89986c05 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 8 Jan 2014 14:55:11 -0500 Subject: [PATCH 009/269] Change PEP 8 and pep8 RST references. --- CONTRIBUTING.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f1489733..c54d890b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,13 +13,13 @@ an unrelated area of the code). Code Style ---------- -Code *must* be compliant with `PEP8`_.Use the latest version of `pep8pypi`_ or +Code *must* be compliant with `PEP 8`_. Use the latest version of `pep8`_ or `flake8`_ to catch issues. Git commit messages should include `a summary and proper line wrapping`_. -.. _PEP8: http://www.python.org/dev/peps/pep-0008/ -.. _pep8pypi: https://pypi.python.org/pypi/pep8 +.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ +.. _pep8: https://pypi.python.org/pypi/pep8 .. _flake8: https://pypi.python.org/pypi/flake8 .. _a summary and proper line wrapping: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html From 36d6038c2ca821ad6f018d6968fdecd60aa8ab8f Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 13 Jan 2014 04:14:50 -0500 Subject: [PATCH 010/269] Remove some cruft from docs index page. --- docs/index.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index fc5d8c50..db2d5f6f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,8 +6,6 @@ Welcome to Facebook SDK for Python's documentation! =================================================== -Contents: - .. toctree:: :maxdepth: 2 @@ -36,11 +34,3 @@ We recommend using `pip`_ and `virtualenv`_ to install the SDK: .. _pip: http://www.pip-installer.org/ .. _virtualenv: http://www.virtualenv.org/ - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - From 70c414e52ec12718f0fb1858fff4bc1ecbae282a Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 13 Jan 2014 04:20:39 -0500 Subject: [PATCH 011/269] Fix installation source code listing. --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index db2d5f6f..175d171c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,7 +25,7 @@ Installation The SDK currently supports Python 2.5, 2.6, and 2.7. No external packages are required. -We recommend using `pip`_ and `virtualenv`_ to install the SDK: +We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: virtualenv facebookenv source facebook/bin/activate From e0c621b3a354dfc5f71e31dc274020ba075848d7 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 28 Jan 2013 22:24:19 -0800 Subject: [PATCH 012/269] Rewrite the sdk to use the requests library. This commit is based on , but contains a number of important changes: - moves the new 'requests' requirement from a requirements.txt file into setup.py - keeps the original order of methods in the code, so that the diff is easy to understand - removes unnecessary whitespace and formatting changes - backports important changes pushed to master since @mvid created his commit --- facebook.py | 308 ++++++++++++++-------------------------------------- setup.py | 3 + 2 files changed, 86 insertions(+), 225 deletions(-) diff --git a/facebook.py b/facebook.py index 4f81773a..bf0535c3 100755 --- a/facebook.py +++ b/facebook.py @@ -33,16 +33,11 @@ """ -import cgi -import time import urllib -import urllib2 -import httplib import hashlib import hmac import base64 -import logging -import socket +import requests # Find a JSON parser try: @@ -137,7 +132,8 @@ def put_object(self, parent_object, connection_name, **data): """ assert self.access_token, "Write operations require an access token" return self.request(parent_object + "/" + connection_name, - post_args=data) + post_args=data, + method="POST") def put_wall_post(self, message, attachment={}, profile_id="me"): """Writes a wall post to the given profile's wall. @@ -168,28 +164,11 @@ def put_like(self, object_id): def delete_object(self, id): """Deletes the object with the given ID from the graph.""" - self.request(id, post_args={"method": "delete"}) + self.request(id, method="DELETE") def delete_request(self, user_id, request_id): """Deletes the Request with the given ID for the given user.""" - conn = httplib.HTTPSConnection('graph.facebook.com') - - url = '/%s_%s?%s' % ( - request_id, - user_id, - urllib.urlencode({'access_token': self.access_token}), - ) - conn.request('DELETE', url) - response = conn.getresponse() - data = response.read() - - response = _parse_json(data) - # Raise an error if we got one, but don't not if Facebook just - # gave us a Bool value - if (response and isinstance(response, dict) and response.get("error")): - raise GraphAPIError(response) - - conn.close() + self.request("%s_%s" % (request_id, user_id), method="DELETE") def put_photo(self, image, message=None, album_id=None, **kwargs): """Uploads an image using multipart/form-data. @@ -201,79 +180,14 @@ def put_photo(self, image, message=None, album_id=None, **kwargs): """ object_id = album_id or "me" - #it would have been nice to reuse self.request; - #but multipart is messy in urllib - post_args = { - 'access_token': self.access_token, - 'source': image, - 'message': message, - } - post_args.update(kwargs) - content_type, body = self._encode_multipart_form(post_args) - req = urllib2.Request(("https://graph.facebook.com/%s/photos" % - object_id), - data=body) - req.add_header('Content-Type', content_type) - try: - data = urllib2.urlopen(req).read() - #For Python 3 use this: - #except urllib2.HTTPError as e: - except urllib2.HTTPError, e: - data = e.read() # Facebook sends OAuth errors as 400, and urllib2 - # throws an exception, we want a GraphAPIError - try: - response = _parse_json(data) - # Raise an error if we got one, but don't not if Facebook just - # gave us a Bool value - if (response and isinstance(response, dict) and - response.get("error")): - raise GraphAPIError(response) - except ValueError: - response = data - - return response - - # based on: http://code.activestate.com/recipes/146306/ - def _encode_multipart_form(self, fields): - """Encode files as 'multipart/form-data'. - - Fields are a dict of form name-> value. For files, value should - be a file object. Other file-like objects might work and a fake - name will be chosen. - - Returns (content_type, body) ready for httplib.HTTP instance. - - """ - BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' - CRLF = '\r\n' - L = [] - for (key, value) in fields.items(): - logging.debug("Encoding %s, (%s)%s" % (key, type(value), value)) - if not value: - continue - L.append('--' + BOUNDARY) - if hasattr(value, 'read') and callable(value.read): - filename = getattr(value, 'name', '%s.jpg' % key) - L.append(('Content-Disposition: form-data;' - 'name="%s";' - 'filename="%s"') % (key, filename)) - L.append('Content-Type: image/jpeg') - value = value.read() - logging.debug(type(value)) - else: - L.append('Content-Disposition: form-data; name="%s"' % key) - L.append('') - if isinstance(value, unicode): - logging.debug("Convert to ascii") - value = value.encode('ascii') - L.append(value) - L.append('--' + BOUNDARY + '--') - L.append('') - body = CRLF.join(L) - content_type = 'multipart/form-data; boundary=%s' % BOUNDARY - return content_type, body - - def request(self, path, args=None, post_args=None): + kwargs.update({"message": message}) + self.request(object_id, + post_args=kwargs, + files={"file": image}, + method="POST") + + 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 @@ -288,81 +202,82 @@ def request(self, path, args=None, post_args=None): post_args["access_token"] = self.access_token else: args["access_token"] = self.access_token - post_data = None if post_args is None else urllib.urlencode(post_args) + try: - file = urllib2.urlopen("https://graph.facebook.com/" + path + "?" + - urllib.urlencode(args), - post_data, timeout=self.timeout) - except urllib2.HTTPError, e: + response = requests.request(method or "GET", + "https://graph.facebook.com/" + path, + timeout=self.timeout, + params=args, + data=post_args, + files=files) + except requests.HTTPError, e: response = _parse_json(e.read()) raise GraphAPIError(response) - except TypeError: - # Timeout support for Python <2.6 - if self.timeout: - socket.setdefaulttimeout(self.timeout) - file = urllib2.urlopen("https://graph.facebook.com/" + path + "?" + - urllib.urlencode(args), post_data) - try: - fileInfo = file.info() - if fileInfo.maintype == 'text': - response = _parse_json(file.read()) - elif fileInfo.maintype == 'image': - mimetype = fileInfo['content-type'] - response = { - "data": file.read(), - "mime-type": mimetype, - "url": file.url, - } + + headers = response.headers + if 'json' in headers['content-type']: + result = response.json() + elif 'image/' in headers['content-type']: + mimetype = headers['content-type'] + result = {"data": response.content, + "mime-type": mimetype, + "url": response.url} + elif "access_token" in parse_qs(response.text): + query_str = parse_qs(response.text) + if "access_token" in query_str: + result = {"access_token": query_str["access_token"][0]} + if "expires" in query_str: + result["expires"] = query_str["expires"][0] else: - raise GraphAPIError('Maintype was not text or image') - finally: - file.close() - if response and isinstance(response, dict) and response.get("error"): - raise GraphAPIError(response["error"]["type"], - response["error"]["message"]) - return response - - def fql(self, query, args=None, post_args=None): + raise GraphAPIError(response.json()) + else: + raise GraphAPIError('Maintype was not text, image, or querystring') + + if result and isinstance(result, dict) and result.get("error"): + raise GraphAPIError(result) + return result + + def fql(self, query): """FQL query. Example query: "SELECT affiliations FROM user WHERE uid = me()" """ - args = args or {} - if self.access_token: - if post_args is not None: - post_args["access_token"] = self.access_token - else: - args["access_token"] = self.access_token - post_data = None if post_args is None else urllib.urlencode(post_args) + self.request("fql", {"q": query}) - args["q"] = query - args["format"] = "json" + def get_app_access_token(self, app_id, app_secret): + """Get the access_token for the app. - try: - file = urllib2.urlopen("https://graph.facebook.com/fql?" + - urllib.urlencode(args), - post_data, timeout=self.timeout) - except TypeError: - # Timeout support for Python <2.6 - if self.timeout: - socket.setdefaulttimeout(self.timeout) - file = urllib2.urlopen("https://graph.facebook.com/fql?" + - urllib.urlencode(args), - post_data) + This token can be used for insights and creating test users. - try: - content = file.read() - response = _parse_json(content) - #Return a list if success, return a dictionary if failed - if type(response) is dict and "error_code" in response: - raise GraphAPIError(response) - except Exception, e: - raise e - finally: - file.close() - - return response + app_id = retrieved from the developer page + app_secret = retrieved from the developer page + + Returns the application access_token. + + """ + # Get an app access token + args = {'grant_type': 'client_credentials', + 'client_id': app_id, + 'client_secret': app_secret} + + return self.request("oauth/access_token", args=args) + + 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 + expiration date (if applicable). + + """ + args = { + "code": code, + "redirect_uri": redirect_uri, + "client_id": app_id, + "client_secret": app_secret} + + return self.request("oauth/access_token", args) def extend_access_token(self, app_id, app_secret): """ @@ -377,24 +292,12 @@ def extend_access_token(self, app_id, app_secret): "grant_type": "fb_exchange_token", "fb_exchange_token": self.access_token, } - response = urllib2.urlopen("https://graph.facebook.com/oauth/" - "access_token?" + - urllib.urlencode(args)).read() - query_str = parse_qs(response) - if "access_token" in query_str: - result = {"access_token": query_str["access_token"][0]} - if "expires" in query_str: - result["expires"] = query_str["expires"][0] - return result - else: - response = json.loads(response) - raise GraphAPIError(response) + + return self.request("access_token", args=args) class GraphAPIError(Exception): def __init__(self, result): - #Exception.__init__(self, message) - #self.type = type self.result = result try: self.type = result["error_code"] @@ -501,56 +404,11 @@ def auth_url(app_id, canvas_url, perms=None, **kwargs): kvps.update(kwargs) return url + urllib.urlencode(kvps) -def get_access_token_from_code(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 - expiration date (if applicable). - """ - args = { - "code": code, - "redirect_uri": redirect_uri, - "client_id": app_id, - "client_secret": app_secret, - } - # We would use GraphAPI.request() here, except for that the fact - # that the response is a key-value pair, and not JSON. - response = urllib2.urlopen("https://graph.facebook.com/oauth/access_token" + - "?" + urllib.urlencode(args)).read() - query_str = parse_qs(response) - if "access_token" in query_str: - result = {"access_token": query_str["access_token"][0]} - if "expires" in query_str: - result["expires"] = query_str["expires"][0] - return result - else: - response = json.loads(response) - raise GraphAPIError(response) +def get_access_token_from_code(code, redirect_uri, app_id, app_secret): + return GraphAPI().get_access_token_from_code( + code, redirect_uri, app_id, app_secret) def get_app_access_token(app_id, app_secret): - """Get the access_token for the app. - - This token can be used for insights and creating test users. - - app_id = retrieved from the developer page - app_secret = retrieved from the developer page - - Returns the application access_token. - - """ - # Get an app access token - args = {'grant_type': 'client_credentials', - 'client_id': app_id, - 'client_secret': app_secret} - - file = urllib2.urlopen("https://graph.facebook.com/oauth/access_token?" + - urllib.urlencode(args)) - - try: - result = file.read().split("=")[1] - finally: - file.close() - - return result + return GraphAPI().get_app_access_token(app_id, app_secret) diff --git a/setup.py b/setup.py index 305571a2..607eb986 100644 --- a/setup.py +++ b/setup.py @@ -22,4 +22,7 @@ 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', ], + install_requires=[ + 'requests', + ], ) From d0ab7cfa98232c120824df9884730e7051a8d883 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Fri, 13 Dec 2013 06:23:46 -0500 Subject: [PATCH 013/269] Fix issues with get_app_access_token. Removes unneccessarily long and relatively information-free docstring. Changes function output and testcase so that they pass again. There are still issues with this function, but they will require larger architectural changes (e.g. storing the application ID and secret in the GraphAPI object). --- facebook.py | 14 ++------------ tests/test_facebook.py | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/facebook.py b/facebook.py index bf0535c3..e04fc34b 100755 --- a/facebook.py +++ b/facebook.py @@ -246,22 +246,12 @@ def fql(self, query): self.request("fql", {"q": query}) def get_app_access_token(self, app_id, app_secret): - """Get the access_token for the app. - - This token can be used for insights and creating test users. - - app_id = retrieved from the developer page - app_secret = retrieved from the developer page - - Returns the application access_token. - - """ - # Get an app access token + """Get the application's access token as a string.""" args = {'grant_type': 'client_credentials', 'client_id': app_id, 'client_secret': app_secret} - return self.request("oauth/access_token", args=args) + return self.request("oauth/access_token", args=args)["access_token"] def get_access_token_from_code( self, code, redirect_uri, app_id, app_secret): diff --git a/tests/test_facebook.py b/tests/test_facebook.py index d27757c5..49082ef2 100644 --- a/tests/test_facebook.py +++ b/tests/test_facebook.py @@ -39,7 +39,7 @@ class TestGetAppAccessToken(FacebookTestCase): """ def test_get_app_access_token(self): assert(isinstance(facebook.get_app_access_token( - self.app_id, self.secret), str)) + self.app_id, self.secret), unicode)) if __name__ == '__main__': From 97075646cf6af8f51f8033b50688489aa2a6eaed Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Fri, 13 Dec 2013 06:54:43 -0500 Subject: [PATCH 014/269] Simple json module import. Fixes #106. We now just use the built-in json module, instead of mucking around with simplejson. This commit also removes the _parse_json shim. --- facebook.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/facebook.py b/facebook.py index e04fc34b..9380310a 100755 --- a/facebook.py +++ b/facebook.py @@ -38,16 +38,7 @@ import hmac import base64 import requests - -# Find a JSON parser -try: - import simplejson as json -except ImportError: - try: - from django.utils import simplejson as json - except ImportError: - import json -_parse_json = json.loads +import json # Find a query string parser try: @@ -211,7 +202,7 @@ def request( data=post_args, files=files) except requests.HTTPError, e: - response = _parse_json(e.read()) + response = json.loads(e.read()) raise GraphAPIError(response) headers = response.headers @@ -368,7 +359,7 @@ def parse_signed_request(signed_request, app_secret): # Signed request had a corrupted payload. return False - data = _parse_json(data) + data = json.loads(data) if data.get('algorithm', '').upper() != 'HMAC-SHA256': return False From fcd027a115fd3c690f042468c303eeedc74774fa Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 25 Jan 2014 16:56:16 -0500 Subject: [PATCH 015/269] Use setuptools instead of distribute. Distribute does not support "install_requires": . Since Setuptools and Distribute have merged (see ), this should not cause any loss of functionality. Your virtualenvs may need to be updated, though. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 607eb986..0fb4f86f 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from distutils.core import setup +from setuptools import setup setup( name='facebook-sdk', From d0eb8319a25492177ad13e0b0b575c0774f5bbec Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 25 Jan 2014 17:25:06 -0500 Subject: [PATCH 016/269] Slightly better Python 3 compatibility. * Import parse_qs correctly. * Fix syntax error with exceptions. * Update access token test to work with both Python 2 and Python 3. --- facebook.py | 6 +++--- tests/test_facebook.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/facebook.py b/facebook.py index 9380310a..d308e06f 100755 --- a/facebook.py +++ b/facebook.py @@ -42,9 +42,9 @@ # Find a query string parser try: - from urlparse import parse_qs + from urllib.parse import parse_qs except ImportError: - from cgi import parse_qs + from urlparse import parse_qs class GraphAPI(object): @@ -201,7 +201,7 @@ def request( params=args, data=post_args, files=files) - except requests.HTTPError, e: + except requests.HTTPError as e: response = json.loads(e.read()) raise GraphAPIError(response) diff --git a/tests/test_facebook.py b/tests/test_facebook.py index 49082ef2..b7083809 100644 --- a/tests/test_facebook.py +++ b/tests/test_facebook.py @@ -38,8 +38,8 @@ class TestGetAppAccessToken(FacebookTestCase): """ def test_get_app_access_token(self): - assert(isinstance(facebook.get_app_access_token( - self.app_id, self.secret), unicode)) + token = facebook.get_app_access_token(self.app_id, self.secret) + assert(isinstance(token, str) or isinstance(token, unicode)) if __name__ == '__main__': From 1c0c12ddc3010d374aabea2e7fd4c03cc39f4204 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 25 Jan 2014 17:51:57 -0500 Subject: [PATCH 017/269] Update classifiers & Travis config w/ Python 3.3. --- .travis.yml | 1 + setup.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1760ed3b..80f46a7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: python python: + - "3.3" - "2.7" - "2.6" install: diff --git a/setup.py b/setup.py index 0fb4f86f..f86121be 100644 --- a/setup.py +++ b/setup.py @@ -18,9 +18,9 @@ long_description=open("README.rst").read(), classifiers=[ 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', ], install_requires=[ 'requests', From 8530d698c58132ddfec4fefd4028752cb47de76f Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 9 Feb 2014 15:44:23 -0500 Subject: [PATCH 018/269] Update version information. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f86121be..6ba8c4b5 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='facebook-sdk', - version='0.4.0', + version='1.0.0-alpha', 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.', From 9b32ae20a5fdf8b59a5594ee712a06a00d55148f Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 9 Feb 2014 15:54:21 -0500 Subject: [PATCH 019/269] Make version DRY. See PEP 396. --- docs/conf.py | 6 ++++-- facebook.py | 2 ++ setup.py | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0927c371..ee707ac3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,6 +13,8 @@ import sys, os +from facebook import __version__ + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -48,9 +50,9 @@ # built documents. # # The short X.Y version. -version = '0.4' +version = '.'.join(__version__.split('.')[:2]) # The full version, including alpha/beta/rc tags. -release = '0.4.0' +release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/facebook.py b/facebook.py index d308e06f..43d6f959 100755 --- a/facebook.py +++ b/facebook.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +__version__ = "1.0.0-alpha" + """Python client library for the Facebook Platform. This client library is designed to support the Graph API and the diff --git a/setup.py b/setup.py index 6ba8c4b5..1ea4246e 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,11 @@ #!/usr/bin/env python from setuptools import setup +from facebook import __version__ + setup( name='facebook-sdk', - version='1.0.0-alpha', + version=__version__, 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.', From 5cd059727dd35271ad135982600a2b7b072214cc Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 9 Feb 2014 17:25:56 -0500 Subject: [PATCH 020/269] Change from module to package. --- facebook.py => facebook/__init__.py | 2 -- setup.py | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) rename facebook.py => facebook/__init__.py (99%) diff --git a/facebook.py b/facebook/__init__.py similarity index 99% rename from facebook.py rename to facebook/__init__.py index 43d6f959..d308e06f 100755 --- a/facebook.py +++ b/facebook/__init__.py @@ -14,8 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -__version__ = "1.0.0-alpha" - """Python client library for the Facebook Platform. This client library is designed to support the Graph API and the diff --git a/setup.py b/setup.py index 1ea4246e..76e064a9 100644 --- a/setup.py +++ b/setup.py @@ -14,9 +14,7 @@ maintainer_email='facebook-sdk@marteydodoo.com', url='https://github.com/pythonforfacebook/facebook-sdk', license='Apache', - py_modules=[ - 'facebook', - ], + package_dir={'': 'facebook'}, long_description=open("README.rst").read(), classifiers=[ 'License :: OSI Approved :: Apache Software License', From 43b87c92abe20a4402dafc1e65d6bb477a5940fe Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 9 Feb 2014 17:26:26 -0500 Subject: [PATCH 021/269] Fix version string integration. --- facebook/__init__.py | 5 +++++ facebook/version.py | 17 +++++++++++++++++ setup.py | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 facebook/version.py diff --git a/facebook/__init__.py b/facebook/__init__.py index d308e06f..d842334c 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -46,6 +46,11 @@ except ImportError: from urlparse import parse_qs +from . import version + + +__version__ = version.__version__ + class GraphAPI(object): """A client for the Facebook Graph API. diff --git a/facebook/version.py b/facebook/version.py new file mode 100644 index 00000000..1ff2e07a --- /dev/null +++ b/facebook/version.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# +# Copyright 2014 Martey Dodoo +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +__version__ = "1.0.0-alpha" diff --git a/setup.py b/setup.py index 76e064a9..5b3ddebe 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from setuptools import setup -from facebook import __version__ +exec(open("facebook/version.py").read()) setup( name='facebook-sdk', From b337d0d59205e5cfaad2873d167c4066a0113340 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 9 Feb 2014 18:21:41 -0500 Subject: [PATCH 022/269] Update Travis config. New layout was causing tests to fail. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 80f46a7b..0dcbc760 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ python: - "2.6" install: - "pip install . --use-mirrors" -script: python tests/test_facebook.py +script: python -m tests.test_facebook env: global: - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" From 40bde9b9aed6549d604d2ef5e377c0d5378e20da Mon Sep 17 00:00:00 2001 From: Sam Sandberg Date: Mon, 10 Feb 2014 11:29:19 -0500 Subject: [PATCH 023/269] Something not working with setup.py package_dir --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5b3ddebe..1b444d0f 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ maintainer_email='facebook-sdk@marteydodoo.com', url='https://github.com/pythonforfacebook/facebook-sdk', license='Apache', - package_dir={'': 'facebook'}, + packages=["facebook"], long_description=open("README.rst").read(), classifiers=[ 'License :: OSI Approved :: Apache Software License', From 6a4d850aadeacb14bdf981ad1137cdd979cdcede Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 10 Feb 2014 15:28:33 -0500 Subject: [PATCH 024/269] Rename `tests` directory to `test`. Per , this ensures that test files are automatically pulled into source distributions. --- .travis.yml | 2 +- {tests => test}/__init__.py | 0 {tests => test}/test_facebook.py | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {tests => test}/__init__.py (100%) rename {tests => test}/test_facebook.py (100%) diff --git a/.travis.yml b/.travis.yml index 0dcbc760..38f6b799 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ python: - "2.6" install: - "pip install . --use-mirrors" -script: python -m tests.test_facebook +script: python -m test.test_facebook env: global: - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" diff --git a/tests/__init__.py b/test/__init__.py similarity index 100% rename from tests/__init__.py rename to test/__init__.py diff --git a/tests/test_facebook.py b/test/test_facebook.py similarity index 100% rename from tests/test_facebook.py rename to test/test_facebook.py From 8dd9f82aaf32742bfff81731584401390a7ab738 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Tue, 18 Feb 2014 01:33:16 -0500 Subject: [PATCH 025/269] Update SDK requirements. See 1c0c12dd. --- docs/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 175d171c..cf99bee1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,7 +22,7 @@ Facebook authentication. You can read more about the Graph API by accessing its Installation ============ -The SDK currently supports Python 2.5, 2.6, and 2.7. No external packages are +The SDK currently supports Python 2.6, 2.7, and 3.3. The `requests`_ package is required. We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: @@ -31,6 +31,7 @@ We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: source facebook/bin/activate pip install facebook-sdk +.. _requests: https://pypi.python.org/pypi/requests .. _pip: http://www.pip-installer.org/ .. _virtualenv: http://www.virtualenv.org/ From 0bc6a731511c3f93b76ef4beb88933511f7d540e Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Tue, 18 Feb 2014 01:43:46 -0500 Subject: [PATCH 026/269] Move installation instructions to separate file. While I am aware that this basically reverts 3a968fcc, I now think that installation instructions are important enough that they should have their own section. I am large, I contain multitudes. --- docs/index.rst | 20 ++------------------ docs/install.rst | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) create mode 100644 docs/install.rst diff --git a/docs/index.rst b/docs/index.rst index cf99bee1..479370ed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,8 @@ Welcome to Facebook SDK for Python's documentation! .. toctree:: :maxdepth: 2 + install + 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. You can read more about the Graph API by accessing its @@ -17,21 +19,3 @@ Facebook authentication. You can read more about the Graph API by accessing its .. _Facebook Graph API: https://developers.facebook.com/docs/reference/api/ .. _Facebook JavaScript SDK: https://developers.facebook.com/docs/reference/javascript/ .. _official documentation: https://developers.facebook.com/docs/reference/api/ - -============ -Installation -============ - -The SDK currently supports Python 2.6, 2.7, and 3.3. The `requests`_ package is -required. - -We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: - - virtualenv facebookenv - source facebook/bin/activate - pip install facebook-sdk - -.. _requests: https://pypi.python.org/pypi/requests -.. _pip: http://www.pip-installer.org/ -.. _virtualenv: http://www.virtualenv.org/ - diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 00000000..b9990fef --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,15 @@ +Installation +============ + +The SDK currently supports Python 2.6, 2.7, and 3.3. The `requests`_ package is +required. + +We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: + + virtualenv facebookenv + source facebook/bin/activate + pip install facebook-sdk + +.. _requests: https://pypi.python.org/pypi/requests +.. _pip: http://www.pip-installer.org/ +.. _virtualenv: http://www.virtualenv.org/ From 770aa37ac0caad45d47e39c256c80a49417a65a2 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Tue, 18 Feb 2014 02:17:29 -0500 Subject: [PATCH 027/269] Change headings to conform with RST specification. Also gets rid of weird "Welcome to ... documentation!" text in heading. --- docs/index.rst | 10 +++------- docs/install.rst | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 479370ed..060c9e44 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,10 +1,6 @@ -.. Facebook SDK for Python documentation master file, created by - sphinx-quickstart on Mon Oct 15 01:01:28 2012. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to Facebook SDK for Python's documentation! -=================================================== +======================= +Facebook SDK for Python +======================= .. toctree:: :maxdepth: 2 diff --git a/docs/install.rst b/docs/install.rst index b9990fef..9244b44a 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -1,3 +1,4 @@ +============ Installation ============ From 83ab3c382db118b2725dd4db9fb094e04fe7aa39 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 30 Mar 2014 18:56:20 -0400 Subject: [PATCH 028/269] Add docs on support and development. Fixes #109. --- docs/index.rst | 1 + docs/support.rst | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 docs/support.rst diff --git a/docs/index.rst b/docs/index.rst index 060c9e44..a23ad3d0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,6 +6,7 @@ Facebook SDK for Python :maxdepth: 2 install + support This client library is designed to support the `Facebook Graph API`_ and the official `Facebook JavaScript SDK`_, which is the canonical way to implement diff --git a/docs/support.rst b/docs/support.rst new file mode 100644 index 00000000..6bc19080 --- /dev/null +++ b/docs/support.rst @@ -0,0 +1,57 @@ +===================== +Support & Development +===================== + +Mailing List +============ + +Questions about the SDK should be sent to its `Google Group`_. + +.. _Google Group: https://groups.google.com/group/pythonforfacebook + +Reporting Bugs +============== + +Bugs with the SDK should be reported on the `issue tracker at Github`_. Bugs +with Facebook's Graph API should be reported on `Facebook's bugtracker`_. + +.. _issue tracker at Github: https://github.com/pythonforfacebook/facebook-sdk/issues +.. _Facebook's bugtracker: https://developers.facebook.com/x/bugs/ + +Security Issues +--------------- + +Security issues with the SDK that would adversely affect users if reported +publicly should be sent through private email to the project maintainer at +martey @ marteydodoo.com (GPG key ID is 0x2cd700988f74c455). + +Contributing +============ + +Use Github Pull Requests +------------------------ + +All potential code changes should be submitted as pull requests on Github. A +pull request should only include commits directly applicable to its change +(e.g. a pull request that adds a new feature should not include PEP8 changes in +an unrelated area of the code). + +Code Style +---------- + +Code *must* be compliant with `PEP 8`_. Use the latest version of `pep8`_ or +`flake8`_ to catch issues. + +Git commit messages should include `a summary and proper line wrapping`_. + +.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ +.. _pep8: https://pypi.python.org/pypi/pep8 +.. _flake8: https://pypi.python.org/pypi/flake8 +.. _a summary and proper line wrapping: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html + +Update Tests and Documentation +------------------------------ + +All non-trivial changes should include full test coverage. Please review +the package's documentation to ensure that it is up to date with any changes. + From 03ff77b7e21217a28d46299c08b185ca7b0bb43a Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 30 Mar 2014 18:58:06 -0400 Subject: [PATCH 029/269] Fix syntax error in installation instructions. --- docs/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index 9244b44a..54c85c1d 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -8,7 +8,7 @@ required. We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: virtualenv facebookenv - source facebook/bin/activate + source facebookenv/bin/activate pip install facebook-sdk .. _requests: https://pypi.python.org/pypi/requests From 29b4b2fe3aa21f18066038716d231f25e898aa2a Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 30 Mar 2014 18:59:31 -0400 Subject: [PATCH 030/269] Explicitly state package name in install docs. This will hopefully decrease the number of people installing broken packages ("facebook", "facebook-python-sdk", etc.) from PyPi. --- docs/install.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index 54c85c1d..db5bb1af 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -5,7 +5,8 @@ Installation The SDK currently supports Python 2.6, 2.7, and 3.3. The `requests`_ package is required. -We recommend using `pip`_ and `virtualenv`_ to install the SDK: :: +We recommend using `pip`_ and `virtualenv`_ to install the SDK. Please note +that the SDK's Python package is called **facebook-sdk**.: :: virtualenv facebookenv source facebookenv/bin/activate From 1bdbdeb6283f017913d23cfaa50ba535d9a3fe96 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 30 Mar 2014 19:08:27 -0400 Subject: [PATCH 031/269] Point CONTRIBUTING.rst at docs. Duplication of information makes it more likely that documentation will not be updated. --- CONTRIBUTING.rst | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c54d890b..341e3e9a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -2,37 +2,5 @@ Contributing to the Facebook Python SDK ======================================= -Use Github Pull Requests ------------------------- - -All potential code changes should be submitted as pull requests on Github. A -pull request should only include commits directly applicable to its change -(e.g. a pull request that adds a new feature should not include PEP8 changes in -an unrelated area of the code). - -Code Style ----------- - -Code *must* be compliant with `PEP 8`_. Use the latest version of `pep8`_ or -`flake8`_ to catch issues. - -Git commit messages should include `a summary and proper line wrapping`_. - -.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ -.. _pep8: https://pypi.python.org/pypi/pep8 -.. _flake8: https://pypi.python.org/pypi/flake8 -.. _a summary and proper line wrapping: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html - -Update Tests and Documentation ------------------------------- - -All non-trivial changes should include full test coverage. Please review -the package's documentation to ensure that it is up to date with any changes. - -Questions? ----------- - -Visit the library's `Google Group`_. - -.. _Google Group: https://groups.google.com/group/pythonforfacebook - +See the "Contributing" section of the package's documentation for details on +submitting pull requests, code style, and more. From c085264fe1a926f939d1b2287e79af3ac3f52bd3 Mon Sep 17 00:00:00 2001 From: rubinovitz Date: Sun, 30 Mar 2014 01:36:24 -0400 Subject: [PATCH 032/269] Update README.rst It was unclear to me that the package was called 'facebook' at first glance. --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index b0dec6d8..02c6340b 100644 --- a/README.rst +++ b/README.rst @@ -15,6 +15,7 @@ Basic usage: :: + import facebook graph = facebook.GraphAPI(oauth_access_token) profile = graph.get_object("me") friends = graph.get_connections("me", "friends") From 830d1fe6cb688cce22bd808c4b318b81c9d3f42c Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 30 Mar 2014 19:27:06 -0400 Subject: [PATCH 033/269] Remove spurious period. --- docs/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index db5bb1af..d7a9417c 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -6,7 +6,7 @@ The SDK currently supports Python 2.6, 2.7, and 3.3. The `requests`_ package is required. We recommend using `pip`_ and `virtualenv`_ to install the SDK. Please note -that the SDK's Python package is called **facebook-sdk**.: :: +that the SDK's Python package is called **facebook-sdk**: :: virtualenv facebookenv source facebookenv/bin/activate From 584889f79a78827aa91b14e0c12ed4a84180439c Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 30 Mar 2014 19:29:01 -0400 Subject: [PATCH 034/269] Update CONTRIBUTING with link to docs. This should increase the changes that new contributors actually read this section before creating pull requests on Github. --- CONTRIBUTING.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 341e3e9a..d8f110d1 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -2,5 +2,7 @@ Contributing to the Facebook Python SDK ======================================= -See the "Contributing" section of the package's documentation for details on +See `the "Contributing" section of the package's documentation`_ for details on submitting pull requests, code style, and more. + +.. _the "Contributing" section of the package's documentation: https://facebook-sdk.readthedocs.org/en/latest/support.html#contributing From 6be103ea92b6b4309cb7165cf681a8fad6b2df25 Mon Sep 17 00:00:00 2001 From: mylsb Date: Mon, 12 May 2014 09:58:23 +1000 Subject: [PATCH 035/269] Created flask example app A barebones Flask application that uses Facebook and the Facebook for Python SDK for login. --- examples/flask/app/__init__.py | 8 +++ examples/flask/app/models.py | 15 +++++ examples/flask/app/static/css/style.css | 22 +++++++ examples/flask/app/templates/base.html | 61 ++++++++++++++++++ examples/flask/app/templates/index.html | 11 ++++ examples/flask/app/templates/login.html | 10 +++ examples/flask/app/views.py | 84 +++++++++++++++++++++++++ examples/flask/config.py | 10 +++ examples/flask/requirements.txt | 12 ++++ examples/flask/run.py | 7 +++ 10 files changed, 240 insertions(+) create mode 100644 examples/flask/app/__init__.py create mode 100644 examples/flask/app/models.py create mode 100644 examples/flask/app/static/css/style.css create mode 100644 examples/flask/app/templates/base.html create mode 100644 examples/flask/app/templates/index.html create mode 100644 examples/flask/app/templates/login.html create mode 100644 examples/flask/app/views.py create mode 100644 examples/flask/config.py create mode 100644 examples/flask/requirements.txt create mode 100644 examples/flask/run.py diff --git a/examples/flask/app/__init__.py b/examples/flask/app/__init__.py new file mode 100644 index 00000000..f441218c --- /dev/null +++ b/examples/flask/app/__init__.py @@ -0,0 +1,8 @@ +from flask import Flask +from flask.ext.sqlalchemy import SQLAlchemy + +app = Flask(__name__) +app.config.from_object('config') +db = SQLAlchemy(app) + +from app import views, models diff --git a/examples/flask/app/models.py b/examples/flask/app/models.py new file mode 100644 index 00000000..f5f2962c --- /dev/null +++ b/examples/flask/app/models.py @@ -0,0 +1,15 @@ +from datetime import datetime + +from app import db + + +class User(db.Model): + __tablename__ = 'users' + + id = db.Column(db.String, nullable=False, primary_key=True) + created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) + updated = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, + onupdate=datetime.utcnow) + name = db.Column(db.String, nullable=False) + profile_url = db.Column(db.String, nullable=False) + access_token = db.Column(db.String, nullable=False) diff --git a/examples/flask/app/static/css/style.css b/examples/flask/app/static/css/style.css new file mode 100644 index 00000000..035afb25 --- /dev/null +++ b/examples/flask/app/static/css/style.css @@ -0,0 +1,22 @@ +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + text-align: center; +} + +.center { + margin: auto; + position: absolute; + top: 0; left: 0; bottom: 0; right: 0; + width: 50%; + height: 50%; + min-width: 200px; + max-width: 400px; + padding: 40px; +} + +.circle-image { + width: 200px; + height: 200px; + border-radius: 100%; + display: block; +} diff --git a/examples/flask/app/templates/base.html b/examples/flask/app/templates/base.html new file mode 100644 index 00000000..14414857 --- /dev/null +++ b/examples/flask/app/templates/base.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + {{ app_name }} + + + + + + + + + +
+ {% block content %} {% endblock %} +
+ + + + diff --git a/examples/flask/app/templates/index.html b/examples/flask/app/templates/index.html new file mode 100644 index 00000000..1c4dc8f1 --- /dev/null +++ b/examples/flask/app/templates/index.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% block content %} +
+

+

+

Hello, {{ user['name'] }}.

+ Log out +
+{% endblock %} diff --git a/examples/flask/app/templates/login.html b/examples/flask/app/templates/login.html new file mode 100644 index 00000000..1ea74d80 --- /dev/null +++ b/examples/flask/app/templates/login.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block content %} +
+

Python for Facebook SDK

+

Flask example

+ +
+{% endblock %} diff --git a/examples/flask/app/views.py b/examples/flask/app/views.py new file mode 100644 index 00000000..5da73dd0 --- /dev/null +++ b/examples/flask/app/views.py @@ -0,0 +1,84 @@ +from facebook import get_user_from_cookie, GraphAPI +from flask import g, render_template, redirect, request, session, url_for + +from app import app, db +from models import User + +# Facebook app details +FB_APP_ID = '' +FB_APP_NAME = '' +FB_APP_SECRET = '' + + +@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) + # Otherwise, a user is not logged in. + return render_template('login.html', app_id=FB_APP_ID, name=FB_APP_NAME) + + +@app.route('/logout') +def logout(): + """Log out the user from the application. + + Log out the user from the application by removing them from the + session. Note: this does not log the user out of Facebook - this is done + by the JavaScript SDK. + """ + session.pop('user', None) + return redirect(url_for('index')) + + +@app.before_request +def get_current_user(): + """Set g.user to the currently logged in user. + + Called before each request, get_current_user sets the global g.user + variable to the currently logged in user. A currently logged in user is + determined by seeing if it exists in Flask's session dictionary. + + If it is the first time the user is logging into this application it will + create the user and insert it into the database. If the user is not logged + in, None will be set to g.user. + """ + + # Set the user in the session dictionary as a global g.user and bail out + # of this function early. + if session.get('user'): + g.user = session.get('user') + return + + # 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) + + # If there is no result, we assume the user is not logged in. + if result: + # Check to see if this user is already in our database. + user = User.query.filter(User.id == result['uid']).first() + + if not user: + # Not an existing user so get info + graph = GraphAPI(result['access_token']) + profile = graph.get_object('me') + + # Create the user and insert it into the database + user = User(id=str(profile['id']), name=profile['name'], + profile_url=profile['link'], + access_token=result['access_token']) + db.session.add(user) + elif user.access_token != result['access_token']: + # If an existing user, update the access token + user.access_token = result['access_token'] + + # Add the user to the current session + session['user'] = dict(name=user.name, profile_url=user.profile_url, + id=user.id, access_token=user.access_token) + + # Commit changes to the database and set the user as a global g.user + db.session.commit() + g.user = session.get('user', None) diff --git a/examples/flask/config.py b/examples/flask/config.py new file mode 100644 index 00000000..109993e1 --- /dev/null +++ b/examples/flask/config.py @@ -0,0 +1,10 @@ +from os import path + +# App details +BASE_DIRECTORY = path.abspath(path.dirname(__file__)) +DEBUG = True +SECRET_KEY = 'keep_it_like_a_secret' + +# Database details +SQLALCHEMY_DATABASE_URI = '{0}{1}'.format('sqlite:///', + path.join(BASE_DIRECTORY, 'app.db')) diff --git a/examples/flask/requirements.txt b/examples/flask/requirements.txt new file mode 100644 index 00000000..c3b6673a --- /dev/null +++ b/examples/flask/requirements.txt @@ -0,0 +1,12 @@ +Flask==0.10.1 +Flask-SQLAlchemy==1.0 +Jinja2==2.7.2 +MarkupSafe==0.19 +SQLAlchemy==0.9.4 +Werkzeug==0.9.4 +argparse==1.2.1 +distribute==0.6.24 +facebook-sdk==0.4.0 +itsdangerous==0.24 +requests==2.2.1 +wsgiref==0.1.2 diff --git a/examples/flask/run.py b/examples/flask/run.py new file mode 100644 index 00000000..ab7ba9e7 --- /dev/null +++ b/examples/flask/run.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +from os import environ + +from app import app, db + +db.create_all() +app.run(host='0.0.0.0', port=8000) From 6169cae69949eb8948268b15c2dc21862b5cb7d8 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 14 Jun 2014 00:03:02 -0400 Subject: [PATCH 036/269] Add example script to retrieve timeline posts. Thanks to @mylsb for agreeing to its inclusion as an example. Since this includes an example of pagination, this is also relevant to issue #85. --- examples/get_posts.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/get_posts.py diff --git a/examples/get_posts.py b/examples/get_posts.py new file mode 100644 index 00000000..8115b994 --- /dev/null +++ b/examples/get_posts.py @@ -0,0 +1,40 @@ +""" +A simple example script to get all posts on a user's timeline. +Originally created by Mitchell Stewart. + +""" +import facebook +import requests + + +def some_action(post): + """ Here you might want to do something with each post. E.g. grab the + post's message (post['message']) or the post's picture (post['picture']). + In this implementation we just print the post's created time. + """ + print post['created_time'] + + +# You'll need an access token here to do anything. You can get a temporary one +# here: https://developers.facebook.com/tools/explorer/ +access_token = '' +# Look at Bill Gates's profile for this example by using his Facebook id. +user = 'BillGates' + +graph = facebook.GraphAPI(access_token) +profile = graph.get_object(user) +posts = graph.get_connections(profile['id'], 'posts') + +# Wrap this block in a while loop so we can keep paginating requests until +# finished. +while True: + try: + # Perform some action on each post in the collection we receive from + # Facebook. + [some_action(post=post) for post in posts['data']] + # Attempt to make a request to the next page of data, if it exists. + posts = requests.get(posts['paging']['next']).json() + except KeyError: + # When there are no more pages (['paging']['next']), break from the + # loop and end the script. + break From 9fa98800cbd3db31898093270c8d41dd3c89698e Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Tue, 24 Jun 2014 05:42:26 -0400 Subject: [PATCH 037/269] Unencrypt Facebook app credentials so builds pass. Stop encrypting OAuth credentials for Facebook app used in tests so that Travis builds for pull requests stop failing. Hopefully, this will not be abused. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 38f6b799..a4569d99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,5 +8,5 @@ install: script: python -m test.test_facebook env: global: - - secure: "fMcBUM5+gIqqhLUIoBcy4ErV/F6BLIajyLR4Jff7VMVqEXJzfJ7yh/T9C+K2cnCyAODg0PgOuZNdobiW/wM+HUnknB7/K/6EzeLG4JwYEWNtoL6QI/oMw2wDYD8X5FYsIAzKhJHfbXkv0DzmX16ksPbHJ5d89azwoPYLpA6lA1U=" - - secure: "nIkW8qKCSZMiyxISWoL74x08Z6/tcAfor9hvuuD0804KYqgK98EdFQWaGOnp3loPCqLTbTcmxggUgIlPgCAdkMUrur0WBE23FgbDuguBbWwOn+A984pFp6k1p+fY7/z4is3vBU8y+qLTAXQuUQxdvh9D9qJXQsp6mOyAMS8LWHM=" + - FACEBOOK_APP_ID=198798870326423 + - FACEBOOK_SECRET=2db4d76fe8a336cf292470c20a5a5684 From 38f3e2875fe575f29a0ad057ef7a6497325c0393 Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 03:01:23 -0700 Subject: [PATCH 038/269] Added support for using different versions of Facebook SDK --- facebook/__init__.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index d842334c..d6591b75 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -39,6 +39,7 @@ import base64 import requests import json +import re # Find a query string parser try: @@ -81,10 +82,26 @@ class GraphAPI(object): for the active user from the cookie saved by the SDK. """ - def __init__(self, access_token=None, timeout=None): + def __init__(self, access_token=None, timeout=None, version=None): self.access_token = access_token self.timeout = timeout + valid_API_versions = ["1.0", "2.0", "2.1"] + if version: + version_regex = re.compile("^\d\.\d$") + match = version_regex.search(str(version)) + if match is not None: + if str(version) not in valid_API_versions: + raise GraphAPIError("Valid API versions are 1.0, 2.0," + " and 2.1.") + else: + self.version = "/v" + str(version) + else: + raise GraphAPIError("Version number should be in the" + " following format: #.# (e.g. 1.0).") + else: + self.version = "" + def get_object(self, id, **args): """Fetchs the given object from the graph.""" return self.request(id, args) @@ -201,7 +218,8 @@ def request( try: response = requests.request(method or "GET", - "https://graph.facebook.com/" + path, + "https://graph.facebook.com/" + + self.version + path, timeout=self.timeout, params=args, data=post_args, From 28996da89e5b4f94ae52dca8a00c72bd85d3a03d Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 03:02:10 -0700 Subject: [PATCH 039/269] Added a function to check what version of Graph API is being used. --- facebook/__init__.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/facebook/__init__.py b/facebook/__init__.py index d6591b75..73375549 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -199,6 +199,26 @@ def put_photo(self, image, message=None, album_id=None, **kwargs): files={"file": image}, method="POST") + def get_version(self): + """Fetches the current version number of the Graph API being used""" + args = {"access_token" : self.access_token} + try: + response = requests.request("GET", + "https://graph.facebook.com/" + + self.version + "/me", + params=args, + timeout=self.timeout) + except requests.HTTPError as e: + response = json.loads(e.read()) + raise GraphAPIError(response) + + try: + headers = response.headers + version = headers["facebook-api-version"].replace("v", "") + return float(version) + except Exception: + raise GraphAPIError("API version number not available") + def request( self, path, args=None, post_args=None, files=None, method=None): """Fetches the given path in the Graph API. From 7d964dc15323d5eda2b29bee4373ea8a327545fc Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 03:02:38 -0700 Subject: [PATCH 040/269] Added tests for using different versions of the Graph API --- test/test_facebook.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/test_facebook.py b/test/test_facebook.py index b7083809..1a0b545d 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -41,6 +41,40 @@ def test_get_app_access_token(self): token = facebook.get_app_access_token(self.app_id, self.secret) assert(isinstance(token, str) or isinstance(token, unicode)) +class TestAPIVersion(unittest.TestCase): + """Test if using the correct version of Graph API.""" + def setUp(self): + try: + self.access_token = os.environ["FACEBOOK_ACCESS_TOKEN"] + except KeyError: + raise Exception("FACEBOOK_ACCESS_TOKEN must be set as environmental" + "variables.") + + def test_version_1_0(self): + graph = facebook.GraphAPI(self.access_token, version=1.0) + self.assertEqual(graph.get_version(), 1.0) + + def test_version_2_0(self): + graph = facebook.GraphAPI(self.access_token, version=2.0) + self.assertEqual(graph.get_version(), 2.0) + + def test_version_2_1(self): + graph = facebook.GraphAPI(self.access_token, version=2.1) + self.assertEqual(graph.get_version(), 2.1) + + def test_invalid_version(self): + self.assertRaises(facebook.GraphAPIError, + facebook.GraphAPI, self.access_token, version=1.2) + + def test_invalid_format(self): + self.assertRaises(facebook.GraphAPIError, + facebook.GraphAPI, self.access_token, version="1.a") + self.assertRaises(facebook.GraphAPIError, + facebook.GraphAPI, self.access_token, version="a.1") + self.assertRaises(facebook.GraphAPIError, + facebook.GraphAPI, self.access_token, version=1.23) + self.assertRaises(facebook.GraphAPIError, + facebook.GraphAPI, self.access_token, version="1.23") if __name__ == '__main__': unittest.main() From c1ea00d15f2bbebe9b32051f04edd5fb685ad5e5 Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 03:37:18 -0700 Subject: [PATCH 041/269] Code is now PEP 8 compliant. --- facebook/__init__.py | 4 ++-- test/test_facebook.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 73375549..24a86f3c 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -200,8 +200,8 @@ def put_photo(self, image, message=None, album_id=None, **kwargs): method="POST") def get_version(self): - """Fetches the current version number of the Graph API being used""" - args = {"access_token" : self.access_token} + """Fetches the current version number of the Graph API being used.""" + args = {"access_token": self.access_token} try: response = requests.request("GET", "https://graph.facebook.com/" + diff --git a/test/test_facebook.py b/test/test_facebook.py index 1a0b545d..7475406b 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -41,14 +41,15 @@ def test_get_app_access_token(self): token = facebook.get_app_access_token(self.app_id, self.secret) assert(isinstance(token, str) or isinstance(token, unicode)) + class TestAPIVersion(unittest.TestCase): """Test if using the correct version of Graph API.""" def setUp(self): try: self.access_token = os.environ["FACEBOOK_ACCESS_TOKEN"] except KeyError: - raise Exception("FACEBOOK_ACCESS_TOKEN must be set as environmental" - "variables.") + raise Exception("FACEBOOK_ACCESS_TOKEN must be set as" + " environmental variables.") def test_version_1_0(self): graph = facebook.GraphAPI(self.access_token, version=1.0) From 6c49353b71e7b359b95f887bed0f02345ed5bca9 Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 04:27:53 -0700 Subject: [PATCH 042/269] Removed extra '/' from version. Adding new api versions now only involves one line change --- facebook/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 24a86f3c..39d6af12 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -92,10 +92,10 @@ def __init__(self, access_token=None, timeout=None, version=None): match = version_regex.search(str(version)) if match is not None: if str(version) not in valid_API_versions: - raise GraphAPIError("Valid API versions are 1.0, 2.0," - " and 2.1.") + raise GraphAPIError("Valid API versions are " + + str(valid_API_versions).strip('[]')) else: - self.version = "/v" + str(version) + self.version = "v" + str(version) else: raise GraphAPIError("Version number should be in the" " following format: #.# (e.g. 1.0).") From 7f235260069dadddb2d9673fc814e17eabed95e6 Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 04:29:24 -0700 Subject: [PATCH 043/269] Tests now rely on an app access token created with the app ID/secret enviroment variables instead of relying on another access token enviroment variable --- test/test_facebook.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 7475406b..3d4edfdf 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -42,14 +42,15 @@ def test_get_app_access_token(self): assert(isinstance(token, str) or isinstance(token, unicode)) -class TestAPIVersion(unittest.TestCase): +class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" def setUp(self): try: - self.access_token = os.environ["FACEBOOK_ACCESS_TOKEN"] - except KeyError: - raise Exception("FACEBOOK_ACCESS_TOKEN must be set as" - " environmental variables.") + super(self.__class__, self).setUp() + self.access_token = facebook.get_app_access_token(self.app_id, + self.secret) + except Exception: + raise Exception("Could not get app access token") def test_version_1_0(self): graph = facebook.GraphAPI(self.access_token, version=1.0) From aed9f908e7583590c8b061d08d20cc1095e22260 Mon Sep 17 00:00:00 2001 From: Jeffrey Chan Date: Fri, 22 Aug 2014 04:55:03 -0700 Subject: [PATCH 044/269] Removed setUp method entirely from tests because no access token is necessary to run tests --- test/test_facebook.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 3d4edfdf..2857f958 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -44,39 +44,31 @@ def test_get_app_access_token(self): class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" - def setUp(self): - try: - super(self.__class__, self).setUp() - self.access_token = facebook.get_app_access_token(self.app_id, - self.secret) - except Exception: - raise Exception("Could not get app access token") - def test_version_1_0(self): - graph = facebook.GraphAPI(self.access_token, version=1.0) + graph = facebook.GraphAPI(version=1.0) self.assertEqual(graph.get_version(), 1.0) def test_version_2_0(self): - graph = facebook.GraphAPI(self.access_token, version=2.0) + graph = facebook.GraphAPI(version=2.0) self.assertEqual(graph.get_version(), 2.0) def test_version_2_1(self): - graph = facebook.GraphAPI(self.access_token, version=2.1) + graph = facebook.GraphAPI(version=2.1) self.assertEqual(graph.get_version(), 2.1) def test_invalid_version(self): self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, self.access_token, version=1.2) + facebook.GraphAPI, version=1.2) def test_invalid_format(self): self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, self.access_token, version="1.a") + facebook.GraphAPI, version="1.a") self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, self.access_token, version="a.1") + facebook.GraphAPI, version="a.1") self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, self.access_token, version=1.23) + facebook.GraphAPI, version=1.23) self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, self.access_token, version="1.23") + facebook.GraphAPI, version="1.23") if __name__ == '__main__': unittest.main() From 39204d614578143b3ecf0e98f1a960254636df75 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Thu, 28 Aug 2014 20:38:50 -0400 Subject: [PATCH 045/269] Add licensing information to README. Fix #147. --- README.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.rst b/README.rst index 02c6340b..722ebc78 100644 --- a/README.rst +++ b/README.rst @@ -11,6 +11,9 @@ Facebook authentication. You can read more about the Graph API by accessing its .. _Facebook JavaScript SDK: https://developers.facebook.com/docs/reference/javascript/ .. _official documentation: https://developers.facebook.com/docs/reference/api/ +Examples +======== + Basic usage: :: @@ -45,6 +48,14 @@ you could get the profile of the logged in user with: You can see a full AppEngine example application in examples/appengine. +Licensing +========= + +This library uses the `Apache License, version 2.0`_. Please see the library's +individual files for more information. + +.. _Apache License, version 2.0: http://www.apache.org/licenses/LICENSE-2.0.html + Reporting Issues ================ From e3a7c11ac1c984612df9edfce57f3b6a39b6a9e0 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 13 Oct 2014 03:42:08 -0400 Subject: [PATCH 046/269] Fix bug where access token calls fail w/ version. When an version was applied to the GraphAPI object, access token calls would fail (because the "oauth/access_token" endpoint does not use a version). Fix bug by removing version from the request method and manually applying it to the specific methods that need it. --- facebook/__init__.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 39d6af12..e632e7e7 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -104,7 +104,7 @@ def __init__(self, access_token=None, timeout=None, version=None): def get_object(self, id, **args): """Fetchs the given object from the graph.""" - return self.request(id, args) + return self.request(self.version + "/" + id, args) def get_objects(self, ids, **args): """Fetchs all of the given object from the graph. @@ -113,11 +113,12 @@ def get_objects(self, ids, **args): invalid, we raise an exception. """ args["ids"] = ",".join(ids) - return self.request("", args) + return self.request(self.version + "/", args) def get_connections(self, id, connection_name, **args): """Fetchs the connections for given object.""" - return self.request(id + "/" + connection_name, args) + return self.request( + self.version + "/" + id + "/" + connection_name, args) def put_object(self, parent_object, connection_name, **data): """Writes the given object to the graph, connected to the given parent. @@ -144,9 +145,10 @@ def put_object(self, parent_object, connection_name, **data): """ assert self.access_token, "Write operations require an access token" - return self.request(parent_object + "/" + connection_name, - post_args=data, - method="POST") + return self.request( + self.version + "/" + parent_object + "/" + connection_name, + post_args=data, + method="POST") def put_wall_post(self, message, attachment={}, profile_id="me"): """Writes a wall post to the given profile's wall. @@ -177,7 +179,7 @@ def put_like(self, object_id): def delete_object(self, id): """Deletes the object with the given ID from the graph.""" - self.request(id, method="DELETE") + self.request(self.version + "/" + id, method="DELETE") def delete_request(self, user_id, request_id): """Deletes the Request with the given ID for the given user.""" @@ -194,7 +196,7 @@ def put_photo(self, image, message=None, album_id=None, **kwargs): """ object_id = album_id or "me" kwargs.update({"message": message}) - self.request(object_id, + self.request(self.version + "/" + object_id, post_args=kwargs, files={"file": image}, method="POST") @@ -239,7 +241,7 @@ def request( try: response = requests.request(method or "GET", "https://graph.facebook.com/" + - self.version + path, + path, timeout=self.timeout, params=args, data=post_args, @@ -277,7 +279,7 @@ def fql(self, query): Example query: "SELECT affiliations FROM user WHERE uid = me()" """ - self.request("fql", {"q": query}) + self.request(self.version + "/" + "fql", {"q": query}) def get_app_access_token(self, app_id, app_secret): """Get the application's access token as a string.""" From 1da18b7d9d359a764bad43549b33481b077cb062 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 13 Oct 2014 04:07:23 -0400 Subject: [PATCH 047/269] Fix #161. Fix regression described in #161 by adding a default API version to all calls. Note that while the default version is 1.0, Facebook will automatically coerce API calls to later versions for newer applications that are unable to access this version. --- facebook/__init__.py | 8 ++++++-- test/test_facebook.py | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index e632e7e7..4c2af35f 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -82,11 +82,15 @@ class GraphAPI(object): for the active user from the cookie saved by the SDK. """ + def __init__(self, access_token=None, timeout=None, version=None): + # The default version is only used if the version kwarg does not exist. + default_version = "1.0" + valid_API_versions = ["1.0", "2.0", "2.1"] + self.access_token = access_token self.timeout = timeout - valid_API_versions = ["1.0", "2.0", "2.1"] if version: version_regex = re.compile("^\d\.\d$") match = version_regex.search(str(version)) @@ -100,7 +104,7 @@ def __init__(self, access_token=None, timeout=None, version=None): raise GraphAPIError("Version number should be in the" " following format: #.# (e.g. 1.0).") else: - self.version = "" + self.version = "v" + default_version def get_object(self, id, **args): """Fetchs the given object from the graph.""" diff --git a/test/test_facebook.py b/test/test_facebook.py index 2857f958..c39ff673 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -44,6 +44,12 @@ def test_get_app_access_token(self): class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" + 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.") + def test_version_1_0(self): graph = facebook.GraphAPI(version=1.0) self.assertEqual(graph.get_version(), 1.0) From fa04cf3d05ac2491d399824c5e73f1fd91a7ec28 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 13 Oct 2014 05:26:14 -0400 Subject: [PATCH 048/269] Fix #131. Thanks @dvolgyes for reporting this. --- facebook/__init__.py | 2 +- test/test_facebook.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 4c2af35f..74631a61 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -283,7 +283,7 @@ def fql(self, query): Example query: "SELECT affiliations FROM user WHERE uid = me()" """ - self.request(self.version + "/" + "fql", {"q": query}) + return self.request(self.version + "/" + "fql", {"q": query}) def get_app_access_token(self, app_id, app_secret): """Get the application's access token as a string.""" diff --git a/test/test_facebook.py b/test/test_facebook.py index c39ff673..843d1d59 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -76,5 +76,21 @@ def test_invalid_format(self): self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version="1.23") + +class TestFQL(FacebookTestCase): + def test_fql(self): + graph = facebook.GraphAPI(access_token=facebook.get_app_access_token( + self.app_id, self.secret), version=2.0) + # Ensure that version is below 2.1. Facebook has stated that FQL is + # not present in this or future versions of the Graph API. + if graph.get_version() < 2.1: + # This is a tautology, but we are limited in what information + # we can retrieve with a proper OAuth access token. + fql_result = graph.fql( + "SELECT app_id from application where app_id = %s" % + self.app_id) + self.assertEqual(fql_result["data"][0]["app_id"], str(self.app_id)) + + if __name__ == '__main__': unittest.main() From b8b8fbb7a7a3ff56d59c917f6552caeeb29b19ef Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Tue, 14 Oct 2014 21:11:13 -0400 Subject: [PATCH 049/269] Fix #169. Fix API endpoint called by extend_access_token. --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 74631a61..d110349e 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -323,7 +323,7 @@ def extend_access_token(self, app_id, app_secret): "fb_exchange_token": self.access_token, } - return self.request("access_token", args=args) + return self.request("oauth/access_token", args=args) class GraphAPIError(Exception): From ca8fd4fbb4c611a649783f416d5edd45338acd5a Mon Sep 17 00:00:00 2001 From: ChaYoung You Date: Thu, 30 Oct 2014 23:15:21 +0900 Subject: [PATCH 050/269] Highlight code blocks --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 722ebc78..8ec3c6a3 100644 --- a/README.rst +++ b/README.rst @@ -16,7 +16,7 @@ Examples Basic usage: -:: +.. code-block:: python import facebook graph = facebook.GraphAPI(oauth_access_token) @@ -26,7 +26,7 @@ Basic usage: Photo uploads: -:: +.. code-block:: python graph = facebook.GraphAPI(oauth_access_token) tags = json.dumps([{'x':50, 'y':50, 'tag_uid':12345}, {'x':10, 'y':60, 'tag_text':'a turtle'}]) @@ -37,7 +37,7 @@ you can also use the module to use Facebook for login, parsing the cookie set by the JavaScript SDK for logged in users. For example, in Google AppEngine, you could get the profile of the logged in user with: -:: +.. code-block:: python user = facebook.get_user_from_cookie(self.request.cookies, key, secret) if user: From 026268341c957eaa910bb5e529cfa711a27b20fd Mon Sep 17 00:00:00 2001 From: ChaYoung You Date: Thu, 30 Oct 2014 23:19:42 +0900 Subject: [PATCH 051/269] Remove trailing whitespace --- examples/appengine/example.html | 4 ++-- examples/newsfeed/templates/base.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/appengine/example.html b/examples/appengine/example.html index 982d5ec2..5f68f935 100644 --- a/examples/appengine/example.html +++ b/examples/appengine/example.html @@ -13,10 +13,10 @@ {% endif %}
- + {% if current_user %}
- Upload photo test: + Upload photo test:
Enter URL to URLFetch from: diff --git a/examples/newsfeed/templates/base.html b/examples/newsfeed/templates/base.html index bb30ef4f..3934ba0c 100644 --- a/examples/newsfeed/templates/base.html +++ b/examples/newsfeed/templates/base.html @@ -1,7 +1,7 @@ - + {% block title %}Facebook Client Example{% endblock %} {% block head %}{% endblock %} From 57bc49023355be1fcdd23c0fd6af0b1a67d5b2ac Mon Sep 17 00:00:00 2001 From: terra Date: Fri, 31 Oct 2014 12:35:07 +0900 Subject: [PATCH 052/269] Add valid API verstion 2.2. --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index d110349e..57661e7b 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -86,7 +86,7 @@ class GraphAPI(object): def __init__(self, access_token=None, timeout=None, version=None): # The default version is only used if the version kwarg does not exist. default_version = "1.0" - valid_API_versions = ["1.0", "2.0", "2.1"] + valid_API_versions = ["1.0", "2.0", "2.1", "2.2"] self.access_token = access_token self.timeout = timeout From 0363a66fbfcd40fe652f11d32dd1885e46738987 Mon Sep 17 00:00:00 2001 From: terra Date: Fri, 31 Oct 2014 14:25:39 +0900 Subject: [PATCH 053/269] Updated tests for API version 2.2 --- test/test_facebook.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/test_facebook.py b/test/test_facebook.py index 843d1d59..bc8f2f1b 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -62,6 +62,10 @@ def test_version_2_1(self): graph = facebook.GraphAPI(version=2.1) self.assertEqual(graph.get_version(), 2.1) + def test_version_2_2(self): + graph = facebook.GraphAPI(version=2.2) + self.assertEqual(graph.get_version(), 2.2) + def test_invalid_version(self): self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version=1.2) From 2d8139c0e3bbdb9f6abe5dbd9d064b11dd8fca16 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 10 Dec 2014 16:51:23 -0500 Subject: [PATCH 054/269] Add flake8 to Travis configuration. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a4569d99..806e3290 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,9 @@ python: - "2.7" - "2.6" install: + - "pip install flake8 --use-mirrors" - "pip install . --use-mirrors" +before_script: flake8 --exclude=docs,setup.py . script: python -m test.test_facebook env: global: From 0ecfc66a18f914709dff677b5db87defaf6eafce Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 10 Dec 2014 17:05:01 -0500 Subject: [PATCH 055/269] Fix F841 issue in facebookoauth example. --- examples/oauth/facebookoauth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/oauth/facebookoauth.py b/examples/oauth/facebookoauth.py index cb36b563..82face7d 100644 --- a/examples/oauth/facebookoauth.py +++ b/examples/oauth/facebookoauth.py @@ -82,9 +82,9 @@ def get(self): verification_code = self.request.get("code") args = dict(client_id=FACEBOOK_APP_ID, redirect_uri=self.request.path_url) - if self.request.get("code"): + if verification_code: args["client_secret"] = FACEBOOK_APP_SECRET - args["code"] = self.request.get("code") + args["code"] = verification_code response = cgi.parse_qs(urllib.urlopen( "https://graph.facebook.com/oauth/access_token?" + urllib.urlencode(args)).read()) From 006ae0501ebde1462ca8c6c7ca802112711b05da Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 10 Dec 2014 17:11:04 -0500 Subject: [PATCH 056/269] Run flake8 on examples and main code separately. The examples have multiple F401 issues that seem more like false positives. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 806e3290..0a5f5a2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,9 @@ python: install: - "pip install flake8 --use-mirrors" - "pip install . --use-mirrors" -before_script: flake8 --exclude=docs,setup.py . +before_script: + flake8 --ignore=F401 examples + flake8 facebook script: python -m test.test_facebook env: global: From b107778814f949edbe183c7bfeb68d9fe453c958 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 10 Dec 2014 17:19:42 -0500 Subject: [PATCH 057/269] Fix syntax error that mangled flake8 commands. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0a5f5a2b..ac73f203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,8 @@ install: - "pip install flake8 --use-mirrors" - "pip install . --use-mirrors" before_script: - flake8 --ignore=F401 examples - flake8 facebook + - "flake8 --ignore=F401 examples" + - "flake8 facebook" script: python -m test.test_facebook env: global: From c033d92636973f33c0c80bb3925b974da72eef7c Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 10 Dec 2014 17:20:37 -0500 Subject: [PATCH 058/269] Stop using deprecated use-mirrors option with pip. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ac73f203..8b069475 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ python: - "2.7" - "2.6" install: - - "pip install flake8 --use-mirrors" - - "pip install . --use-mirrors" + - "pip install flake8" + - "pip install ." before_script: - "flake8 --ignore=F401 examples" - "flake8 facebook" From 20cc61dd008fa4c1ced0d4726baaeba78d0d1f05 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 10 Dec 2014 17:30:30 -0500 Subject: [PATCH 059/269] Update get_posts example script for Python 3. --- examples/get_posts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/get_posts.py b/examples/get_posts.py index 8115b994..fdec849f 100644 --- a/examples/get_posts.py +++ b/examples/get_posts.py @@ -12,7 +12,7 @@ def some_action(post): post's message (post['message']) or the post's picture (post['picture']). In this implementation we just print the post's created time. """ - print post['created_time'] + print(post['created_time']) # You'll need an access token here to do anything. You can get a temporary one From caf2d49a7bf7dc60704604da9ea35958cc4b88c6 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 02:07:49 +1100 Subject: [PATCH 060/269] Fix module import strategy to avoid ImportError Exception occurred: File "conf.py", line 16, in from facebook import __version__ ImportError: cannot import name __version__ Now using the same method as setup.py --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index ee707ac3..08231d9e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ import sys, os -from facebook import __version__ +exec(open("../facebook/version.py").read()) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the From 2ef549bc78e6786c5e1ae4013c9681c65c1482ed Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 02:26:14 +1100 Subject: [PATCH 061/269] Add api.rst and reference it in index toctree --- docs/api.rst | 3 +++ docs/index.rst | 1 + 2 files changed, 4 insertions(+) create mode 100644 docs/api.rst diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..e60a5013 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,3 @@ +===================== +API Reference +===================== diff --git a/docs/index.rst b/docs/index.rst index a23ad3d0..7aeaafff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,6 +7,7 @@ Facebook SDK for Python install support + api This client library is designed to support the `Facebook Graph API`_ and the official `Facebook JavaScript SDK`_, which is the canonical way to implement From 7687653573de8146b1766d890453d4df13582be7 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 03:08:00 +1100 Subject: [PATCH 062/269] Add documentation on facebook.GraphAPI --- docs/api.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index e60a5013..82789db9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,3 +1,33 @@ ===================== API Reference ===================== + +This page contains specific information on the SDK's classes, methods and functions. + +class facebook.GraphAPI +======== +A client for the Facebook Graph API. The Graph API is made up of the objects or +nodes in Facebook (e.g., people, pages, events, photos) and the connections or +edges between them (e.g., friends, photo tags, and event RSVPs). This client +provides access to those primitive types in a generic way. + +You can read more about `Facebook's Graph API here`_. + +.. _Facebook's Graph API here: https://developers.facebook.com/docs/graph-api + +**Parameters** + +* *access_token (optional)* – A string that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. +* *timeout (optional)* - A float describing the timeout of the request in seconds. I.e., you can tell the client to stop waiting for a response after a given number of seconds with the timeout parameter. `See more here`_. +* *version (optional)* - TODO + +.. _Read more about access tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens +.. _See more here: http://docs.python-requests.org/en/latest/user/quickstart/#timeouts + +**Example** + +.. code-block:: python + + import facebook + + graph = facebook.GraphAPI(access_token='your_token') From e317a1a8b37e72401524c2eca4e2c417bee7b29b Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 03:18:41 +1100 Subject: [PATCH 063/269] Add documentation about the version kwarg --- docs/api.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 82789db9..ccdc4136 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -19,10 +19,11 @@ You can read more about `Facebook's Graph API here`_. * *access_token (optional)* – A string that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. * *timeout (optional)* - A float describing the timeout of the request in seconds. I.e., you can tell the client to stop waiting for a response after a given number of seconds with the timeout parameter. `See more here`_. -* *version (optional)* - TODO +* *version (optional)* - A string describing the `version of Facebook's Graph API to use`_. Valid API versions are "1.0", "2.0", "2.1" and "2.2". The default version is "1.0" and is used if the version keyword argument is not provided. .. _Read more about access tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens .. _See more here: http://docs.python-requests.org/en/latest/user/quickstart/#timeouts +.. _version of Facebook's Graph API to use: https://developers.facebook.com/docs/apps/versions **Example** @@ -30,4 +31,4 @@ You can read more about `Facebook's Graph API here`_. import facebook - graph = facebook.GraphAPI(access_token='your_token') + graph = facebook.GraphAPI(access_token='your_token', version='2.2') From b7869ce510a2096116d23428a0f6b5595b00c0fc Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 03:24:52 +1100 Subject: [PATCH 064/269] Add documentation for get_object --- docs/api.rst | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index ccdc4136..2a464732 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5,7 +5,7 @@ API Reference This page contains specific information on the SDK's classes, methods and functions. class facebook.GraphAPI -======== +======================= A client for the Facebook Graph API. The Graph API is made up of the objects or nodes in Facebook (e.g., people, pages, events, photos) and the connections or edges between them (e.g., friends, photo tags, and event RSVPs). This client @@ -32,3 +32,23 @@ You can read more about `Facebook's Graph API here`_. import facebook graph = facebook.GraphAPI(access_token='your_token', version='2.2') + +Methods +======= + +get_object(id, **args) +---------------------- +Returns the given object from the graph as a dict. A list of `supported objects can be found here`_. + +.. _supported objects can be found here: https://developers.facebook.com/docs/graph-api/reference/v2.2 + +**Parameters** + +* *id (required)* – A string that is a unique ID for that particular resource. + +**Example** + +.. code-block:: python + + post = graph.get_object(id='post_id') + print post['message'] \ No newline at end of file From acdebf73c0e3c879e8cb86d48f820175d41ca0d0 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 03:29:46 +1100 Subject: [PATCH 065/269] Add documentation for get_objects --- docs/api.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 2a464732..cdf21944 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -51,4 +51,22 @@ Returns the given object from the graph as a dict. A list of `supported objects .. code-block:: python post = graph.get_object(id='post_id') - print post['message'] \ No newline at end of file + print post['message'] + +get_objects(id, **args) +---------------------- +Returns all of the given objects from the graph as a dict. Each given ID maps to an object. + +**Parameters** + +* *ids (required)* – A list containing ids for multiple resources. + +**Example** + +.. code-block:: python + + post_ids = ['post_id_1', 'post_id_2'] + posts = graph.get_objects(ids=post_ids) + + for post_id in post_ids: + print posts[post_id]['created_time'] \ No newline at end of file From 47f50fc319b1691956bd24b34886d04db371a96d Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 03:32:34 +1100 Subject: [PATCH 066/269] Add documentation for get_connections --- docs/api.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index cdf21944..ba8c0ce9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -69,4 +69,23 @@ Returns all of the given objects from the graph as a dict. Each given ID maps to posts = graph.get_objects(ids=post_ids) for post_id in post_ids: - print posts[post_id]['created_time'] \ No newline at end of file + print posts[post_id]['created_time'] + +get_connections(id, connection_name, **args) +---------------------- +Returns all connections for given object as a dict. + +**Parameters** + +* *id (required)* – A string that is a unique id for that particular resource. +* *connection_name* - A string that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, get_connections will simply return the authenticated user's basic information. + +**Example** + +.. code-block:: python + + # Get all of the authenticated user's friends + friends = graph.get_connections(id='me', connection_name='friends') + + # Get all the comments from a post + comments = graph.get_connections(id='post_id', connection_name='comments') \ No newline at end of file From ce4594fc86e96f38a5ebca96136d66183234e512 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 09:01:05 +1100 Subject: [PATCH 067/269] Add documentation for put_object --- docs/api.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index ba8c0ce9..1e1ce875 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -53,6 +53,7 @@ Returns the given object from the graph as a dict. A list of `supported objects post = graph.get_object(id='post_id') print post['message'] + get_objects(id, **args) ---------------------- Returns all of the given objects from the graph as a dict. Each given ID maps to an object. @@ -71,6 +72,7 @@ Returns all of the given objects from the graph as a dict. Each given ID maps to for post_id in post_ids: print posts[post_id]['created_time'] + get_connections(id, connection_name, **args) ---------------------- Returns all connections for given object as a dict. @@ -88,4 +90,24 @@ Returns all connections for given object as a dict. friends = graph.get_connections(id='me', connection_name='friends') # Get all the comments from a post - comments = graph.get_connections(id='post_id', connection_name='comments') \ No newline at end of file + comments = graph.get_connections(id='post_id', connection_name='comments') + + +put_object(parent_object, connection_name, **data) +---------------------- +Writes the given object to the graph, connected to the given parent. + +**Parameters** + +* *parent_object (required)* – A string that is a unique id for that particular resource. The parent_object is parent of a connection or edge. E.g., profile is a parent of a feed, and a post is a parent of a comment. +* *connection_name* - A string that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, get_connections will simply return the authenticated user's basic information. + +**Example** + +.. code-block:: python + + # Writes 'Hello, world' to the active user's wall. + graph.put_object('me', 'feed', message='Hello, world') + + # Writes a comment on a post + graph.put_object('post_id', 'comments', message='First!') From 848120ec2acf05afd62995f40195a1358378b966 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 09:17:37 +1100 Subject: [PATCH 068/269] Add code samples for python params and types --- docs/api.rst | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 1e1ce875..39975a9c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -17,9 +17,9 @@ You can read more about `Facebook's Graph API here`_. **Parameters** -* *access_token (optional)* – A string that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. -* *timeout (optional)* - A float describing the timeout of the request in seconds. I.e., you can tell the client to stop waiting for a response after a given number of seconds with the timeout parameter. `See more here`_. -* *version (optional)* - A string describing the `version of Facebook's Graph API to use`_. Valid API versions are "1.0", "2.0", "2.1" and "2.2". The default version is "1.0" and is used if the version keyword argument is not provided. +* ``access_token`` – A ``string`` that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. +* ``timeout`` - A ``float`` describing the timeout of the request in seconds. I.e., you can tell the client to stop waiting for a response after a given number of seconds with the timeout parameter. `See more here`_. +* ``version`` - A ``string`` describing the `version of Facebook's Graph API to use`_. Valid API versions are ``1.0``, ``2.0``, ``2.1`` and ``2.2``. The default version is ``1.0`` and is used if the version keyword argument is not provided. .. _Read more about access tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens .. _See more here: http://docs.python-requests.org/en/latest/user/quickstart/#timeouts @@ -29,22 +29,22 @@ You can read more about `Facebook's Graph API here`_. .. code-block:: python - import facebook + import facebook - graph = facebook.GraphAPI(access_token='your_token', version='2.2') + graph = facebook.GraphAPI(access_token='your_token', version='2.2') Methods ======= get_object(id, **args) ---------------------- -Returns the given object from the graph as a dict. A list of `supported objects can be found here`_. +Returns the given object from the graph as a ``dict``. A list of `supported objects can be found here`_. .. _supported objects can be found here: https://developers.facebook.com/docs/graph-api/reference/v2.2 **Parameters** -* *id (required)* – A string that is a unique ID for that particular resource. +* ``id`` – A ``string`` that is a unique id for that particular resource. **Example** @@ -56,11 +56,11 @@ Returns the given object from the graph as a dict. A list of `supported objects get_objects(id, **args) ---------------------- -Returns all of the given objects from the graph as a dict. Each given ID maps to an object. +Returns all of the given objects from the graph as a ``dict``. Each given id maps to an object. **Parameters** -* *ids (required)* – A list containing ids for multiple resources. +* ``ids`` – A ``list`` containing ids for multiple resources. **Example** @@ -69,18 +69,19 @@ Returns all of the given objects from the graph as a dict. Each given ID maps to post_ids = ['post_id_1', 'post_id_2'] posts = graph.get_objects(ids=post_ids) + # Each given id maps to an object. for post_id in post_ids: print posts[post_id]['created_time'] get_connections(id, connection_name, **args) ---------------------- -Returns all connections for given object as a dict. +Returns all connections for given object as a ``dict``. **Parameters** -* *id (required)* – A string that is a unique id for that particular resource. -* *connection_name* - A string that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, get_connections will simply return the authenticated user's basic information. +* ``id`` – A ``string`` that is a unique id for that particular resource. +* ``connection_name`` - A ``string`` that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, ``get_connections`` will simply return the authenticated user's basic information. **Example** @@ -99,15 +100,17 @@ Writes the given object to the graph, connected to the given parent. **Parameters** -* *parent_object (required)* – A string that is a unique id for that particular resource. The parent_object is parent of a connection or edge. E.g., profile is a parent of a feed, and a post is a parent of a comment. -* *connection_name* - A string that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, get_connections will simply return the authenticated user's basic information. +* ``parent_object`` – A ``string`` that is a unique id for that particular resource. The ``parent_object`` is parent of a connection or edge. E.g., profile is a parent of a feed, and a post is a parent of a comment. +* ``connection_name`` - A ``string`` that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. **Example** .. code-block:: python # Writes 'Hello, world' to the active user's wall. - graph.put_object('me', 'feed', message='Hello, world') + graph.put_object(parent_object='me', connection_name='feed', + message='Hello, world') # Writes a comment on a post - graph.put_object('post_id', 'comments', message='First!') + graph.put_object(parent_object='post_id', connection_name='comments', + message='First!') From 602921f788c6a56e4113ff4ea5b4db3c23df5b0a Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 09:25:33 +1100 Subject: [PATCH 069/269] Add documentation for put_wall_post --- docs/api.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 39975a9c..cd59f55d 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -114,3 +114,36 @@ Writes the given object to the graph, connected to the given parent. # Writes a comment on a post graph.put_object(parent_object='post_id', connection_name='comments', message='First!') + +put_wall_post(message, attachment, profile_id) +---------------------- +Writes a wall post to the given profile's wall. It defaults to writing to the authenticated user's wall if no ``profile_id`` is specified. + +**Parameters** + +* ``message`` - A ``string`` that will be posted to the user's wall. +* ``attachment`` - A ``dict`` that adds a structured attachment to the message being posted to the Wall. If you are sharing a URL, you will want to use the ``attachment`` parameter so that a thumbnail preview appears in the post. It should be a ``dict`` of the form: +.. code-block:: python + + attachment = { + 'name': '' + 'link': '', + 'caption': '', + 'description': '', + 'picture': '' + } +* ``profile_id`` - A ``string`` that is a unique id for that particular user. Defaults to the authenticated user's wall. + +**Example** + +.. code-block:: python + + attachment = { + 'name': 'Link name' + 'link': 'http://www.example.com/', + 'caption': 'Check out this example', + 'description': 'This is a longer description of the attachment', + 'picture': 'http://www.example.com/thumbnail.jpg' + } + + graph.put_wall_post(message='Check this out...', attachment=attachment) From 021a7c68eaa19cda3046bba936af585ba9bfb4dd Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 09:34:23 +1100 Subject: [PATCH 070/269] Add put_comment documentation --- docs/api.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index cd59f55d..aa56b747 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -115,6 +115,7 @@ Writes the given object to the graph, connected to the given parent. graph.put_object(parent_object='post_id', connection_name='comments', message='First!') + put_wall_post(message, attachment, profile_id) ---------------------- Writes a wall post to the given profile's wall. It defaults to writing to the authenticated user's wall if no ``profile_id`` is specified. @@ -147,3 +148,19 @@ Writes a wall post to the given profile's wall. It defaults to writing to the au } graph.put_wall_post(message='Check this out...', attachment=attachment) + + +put_comment(object_id, message) +---------------------- +Writes the given message as a comment on an object. + +**Parameters** + +* ``object_id`` - A ``string`` that is a unique id for a particular resource. +* ``message`` - A ``string`` that will be posted as the comment. + +**Example** + +.. code-block:: python + + graph.put_comment(object_id='post_id', message='Great post...') From 79aad0e1163bb6d8489bbb7dcc65bfd2431e4f60 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 10:00:12 +1100 Subject: [PATCH 071/269] Add documentation for put_like and put_photo --- docs/api.rst | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index aa56b747..c3c0f02c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -164,3 +164,40 @@ Writes the given message as a comment on an object. .. code-block:: python graph.put_comment(object_id='post_id', message='Great post...') + + +put_like(object_id) +------------------- +Writes a like to the given object. + +**Parameters** + +* ``object_id`` - A ``string`` that is a unique id for a particular resource. + +**Example** + +.. code-block:: python + + graph.put_like(object_id='comment_id') + + +put_photo(image, message, album_id, **kwargs) +-------------------------------------------------- +Uploads an image using multipart/form-data. + +**Parameters** + +* ``image`` - An image of the ``file`` type +* ``message`` - A ``string`` that will caption the image +* ``album_id`` - A ``string`` that is a unique id for an album. If no ``album_id`` is provided, the photo posts to /me/photos which uses, or creates and uses, an album for your application. + +**Example** + +.. code-block:: python + + tags = json.dumps([ + {'x':50, 'y':50, 'tag_uid':12345}, + {'x':10, 'y':60, 'tag_text':'a turtle'} + ]) + graph.put_photo(image=open('img.jpg'), message='Look at this cool photo!', + tags=tags) From cc7751750de0cb5d96cc8c390cfb760529512925 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 10:03:59 +1100 Subject: [PATCH 072/269] Add documentation for delete_object --- docs/api.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index c3c0f02c..d584f4b0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -201,3 +201,17 @@ Uploads an image using multipart/form-data. ]) graph.put_photo(image=open('img.jpg'), message='Look at this cool photo!', tags=tags) + +delete_object(id) +----------------- +Deletes the object with the given id from the graph. + +**Parameters** + +* ``id`` - A ``string`` that is a unique id for a particular resource. + +**Example** + +.. code-block:: python + + graph.delete_object(id='post_id') \ No newline at end of file From 53318f09c3022648fcc79558dfd4994ff78c1f10 Mon Sep 17 00:00:00 2001 From: mylsb Date: Sat, 20 Dec 2014 10:10:29 +1100 Subject: [PATCH 073/269] Fix length of header lines --- docs/api.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index d584f4b0..8e602cc9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -55,7 +55,7 @@ Returns the given object from the graph as a ``dict``. A list of `supported obje get_objects(id, **args) ----------------------- +----------------------- Returns all of the given objects from the graph as a ``dict``. Each given id maps to an object. **Parameters** @@ -75,7 +75,7 @@ Returns all of the given objects from the graph as a ``dict``. Each given id map get_connections(id, connection_name, **args) ----------------------- +-------------------------------------------- Returns all connections for given object as a ``dict``. **Parameters** @@ -95,7 +95,7 @@ Returns all connections for given object as a ``dict``. put_object(parent_object, connection_name, **data) ----------------------- +-------------------------------------------------- Writes the given object to the graph, connected to the given parent. **Parameters** @@ -117,7 +117,7 @@ Writes the given object to the graph, connected to the given parent. put_wall_post(message, attachment, profile_id) ----------------------- +---------------------------------------------- Writes a wall post to the given profile's wall. It defaults to writing to the authenticated user's wall if no ``profile_id`` is specified. **Parameters** @@ -151,7 +151,7 @@ Writes a wall post to the given profile's wall. It defaults to writing to the au put_comment(object_id, message) ----------------------- +------------------------------- Writes the given message as a comment on an object. **Parameters** @@ -182,7 +182,7 @@ Writes a like to the given object. put_photo(image, message, album_id, **kwargs) --------------------------------------------------- +--------------------------------------------- Uploads an image using multipart/form-data. **Parameters** From ba717b4b16ea28fa8996c82d06bc210d0538ce52 Mon Sep 17 00:00:00 2001 From: Viktor Voronin Date: Sun, 28 Dec 2014 13:54:34 +0100 Subject: [PATCH 074/269] Resolve urlencode import with tolerance for Python 3 --- facebook/__init__.py | 7 ++++++- test/test_facebook.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 57661e7b..03ce9452 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -47,6 +47,11 @@ except ImportError: from urlparse import parse_qs +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode + from . import version @@ -432,7 +437,7 @@ def auth_url(app_id, canvas_url, perms=None, **kwargs): if perms: kvps['scope'] = ",".join(perms) kvps.update(kwargs) - return url + urllib.urlencode(kvps) + return url + urlencode(kvps) def get_access_token_from_code(code, redirect_uri, app_id, app_secret): diff --git a/test/test_facebook.py b/test/test_facebook.py index bc8f2f1b..8228e2c8 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -96,5 +96,25 @@ def test_fql(self): self.assertEqual(fql_result["data"][0]["app_id"], str(self.app_id)) +class TestAuthURL(FacebookTestCase): + def test_auth_url(self): + + try: + from urllib.parse import urlencode + except ImportError: + from urllib import urlencode + + perms = ['email', 'birthday'] + redirect_url = 'https://localhost/facebook/callback/' + + expected_url = 'https://www.facebook.com/dialog/oauth?' + urlencode( + dict(client_id=self.app_id, + redirect_uri=redirect_url, + scope=','.join(perms))) + actual_url = facebook.auth_url(self.app_id, redirect_url, perms=perms) + + self.assertEqual(actual_url, expected_url) + + if __name__ == '__main__': unittest.main() From 671da83c0ae344849157391e435e58051cc65c22 Mon Sep 17 00:00:00 2001 From: Viktor Voronin Date: Sun, 28 Dec 2014 13:58:52 +0100 Subject: [PATCH 075/269] Skip PEP8 #F401 for urllib import --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 03ce9452..abec3be7 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -33,7 +33,7 @@ """ -import urllib +import urllib # noqa import hashlib import hmac import base64 From 66029b28289f70db2f77f78bb692a0abb0fad9b2 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 29 Dec 2014 11:53:17 -0500 Subject: [PATCH 076/269] Change documentation section headings. Since the "Methods" section describes the methods used in the GraphAPI object, it should be a subsection of the GraphAPI section. --- docs/api.rst | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 8e602cc9..f03c77b0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,11 +1,12 @@ -===================== +============= API Reference -===================== +============= This page contains specific information on the SDK's classes, methods and functions. class facebook.GraphAPI ======================= + A client for the Facebook Graph API. The Graph API is made up of the objects or nodes in Facebook (e.g., people, pages, events, photos) and the connections or edges between them (e.g., friends, photo tags, and event RSVPs). This client @@ -34,10 +35,11 @@ You can read more about `Facebook's Graph API here`_. graph = facebook.GraphAPI(access_token='your_token', version='2.2') Methods -======= +------- get_object(id, **args) ----------------------- +^^^^^^^^^^^^^^^^^^^^^^ + Returns the given object from the graph as a ``dict``. A list of `supported objects can be found here`_. .. _supported objects can be found here: https://developers.facebook.com/docs/graph-api/reference/v2.2 @@ -55,7 +57,8 @@ Returns the given object from the graph as a ``dict``. A list of `supported obje get_objects(id, **args) ------------------------ +^^^^^^^^^^^^^^^^^^^^^^^ + Returns all of the given objects from the graph as a ``dict``. Each given id maps to an object. **Parameters** @@ -75,7 +78,8 @@ Returns all of the given objects from the graph as a ``dict``. Each given id map get_connections(id, connection_name, **args) --------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Returns all connections for given object as a ``dict``. **Parameters** @@ -95,7 +99,8 @@ Returns all connections for given object as a ``dict``. put_object(parent_object, connection_name, **data) --------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Writes the given object to the graph, connected to the given parent. **Parameters** @@ -117,7 +122,8 @@ Writes the given object to the graph, connected to the given parent. put_wall_post(message, attachment, profile_id) ----------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Writes a wall post to the given profile's wall. It defaults to writing to the authenticated user's wall if no ``profile_id`` is specified. **Parameters** @@ -151,7 +157,8 @@ Writes a wall post to the given profile's wall. It defaults to writing to the au put_comment(object_id, message) -------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Writes the given message as a comment on an object. **Parameters** @@ -167,7 +174,8 @@ Writes the given message as a comment on an object. put_like(object_id) -------------------- +^^^^^^^^^^^^^^^^^^^ + Writes a like to the given object. **Parameters** @@ -182,7 +190,8 @@ Writes a like to the given object. put_photo(image, message, album_id, **kwargs) ---------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Uploads an image using multipart/form-data. **Parameters** @@ -203,7 +212,8 @@ Uploads an image using multipart/form-data. tags=tags) delete_object(id) ------------------ +^^^^^^^^^^^^^^^^^ + Deletes the object with the given id from the graph. **Parameters** @@ -214,4 +224,4 @@ Deletes the object with the given id from the graph. .. code-block:: python - graph.delete_object(id='post_id') \ No newline at end of file + graph.delete_object(id='post_id') From 27cac401f449a746410ec7d2befb457a6699e5b3 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 29 Dec 2014 11:58:13 -0500 Subject: [PATCH 077/269] Escape args and kwargs in section headings. --- docs/api.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index f03c77b0..76e4fc7a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -37,8 +37,8 @@ You can read more about `Facebook's Graph API here`_. Methods ------- -get_object(id, **args) -^^^^^^^^^^^^^^^^^^^^^^ +get_object(id, \*\*args) +^^^^^^^^^^^^^^^^^^^^^^^^ Returns the given object from the graph as a ``dict``. A list of `supported objects can be found here`_. @@ -56,8 +56,8 @@ Returns the given object from the graph as a ``dict``. A list of `supported obje print post['message'] -get_objects(id, **args) -^^^^^^^^^^^^^^^^^^^^^^^ +get_objects(id, \*\*args) +^^^^^^^^^^^^^^^^^^^^^^^^^ Returns all of the given objects from the graph as a ``dict``. Each given id maps to an object. @@ -77,8 +77,8 @@ Returns all of the given objects from the graph as a ``dict``. Each given id map print posts[post_id]['created_time'] -get_connections(id, connection_name, **args) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +get_connections(id, connection_name, \*\*args) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Returns all connections for given object as a ``dict``. @@ -98,8 +98,8 @@ Returns all connections for given object as a ``dict``. comments = graph.get_connections(id='post_id', connection_name='comments') -put_object(parent_object, connection_name, **data) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +put_object(parent_object, connection_name, \*\*data) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Writes the given object to the graph, connected to the given parent. @@ -189,8 +189,8 @@ Writes a like to the given object. graph.put_like(object_id='comment_id') -put_photo(image, message, album_id, **kwargs) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +put_photo(image, message, album_id, \*\*kwargs) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Uploads an image using multipart/form-data. From cd549b38bf0ebbab0ea8e955c9de28648d5653a7 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 29 Dec 2014 12:20:33 -0500 Subject: [PATCH 078/269] Fix line lengths and whitespace. See https://docs.python.org/devguide/documenting.html#use-of-whitespace. --- docs/api.rst | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 76e4fc7a..620a0875 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -2,7 +2,8 @@ API Reference ============= -This page contains specific information on the SDK's classes, methods and functions. +This page contains specific information on the SDK's classes, methods and +functions. class facebook.GraphAPI ======================= @@ -18,9 +19,16 @@ You can read more about `Facebook's Graph API here`_. **Parameters** -* ``access_token`` – A ``string`` that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. -* ``timeout`` - A ``float`` describing the timeout of the request in seconds. I.e., you can tell the client to stop waiting for a response after a given number of seconds with the timeout parameter. `See more here`_. -* ``version`` - A ``string`` describing the `version of Facebook's Graph API to use`_. Valid API versions are ``1.0``, ``2.0``, ``2.1`` and ``2.2``. The default version is ``1.0`` and is used if the version keyword argument is not provided. +* ``access_token`` – A ``string`` that identifies a user, app, or page and can + be used by the app to make graph API calls. + `Read more about access tokens here`_. +* ``timeout`` - A ``float`` describing the timeout of the request in seconds. + I.e., you can tell the client to stop waiting for a response after a given + number of seconds with the timeout parameter. `See more here`_. +* ``version`` - A ``string`` describing the `version of Facebook's Graph API to + use`_. Valid API versions are ``1.0``, ``2.0``, ``2.1`` and ``2.2``. The + default version is ``1.0`` and is used if the version keyword argument is not + provided. .. _Read more about access tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens .. _See more here: http://docs.python-requests.org/en/latest/user/quickstart/#timeouts @@ -40,7 +48,8 @@ Methods get_object(id, \*\*args) ^^^^^^^^^^^^^^^^^^^^^^^^ -Returns the given object from the graph as a ``dict``. A list of `supported objects can be found here`_. +Returns the given object from the graph as a ``dict``. A list of +`supported objects can be found here`_. .. _supported objects can be found here: https://developers.facebook.com/docs/graph-api/reference/v2.2 @@ -59,7 +68,8 @@ Returns the given object from the graph as a ``dict``. A list of `supported obje get_objects(id, \*\*args) ^^^^^^^^^^^^^^^^^^^^^^^^^ -Returns all of the given objects from the graph as a ``dict``. Each given id maps to an object. +Returns all of the given objects from the graph as a ``dict``. Each given id +maps to an object. **Parameters** @@ -85,7 +95,10 @@ Returns all connections for given object as a ``dict``. **Parameters** * ``id`` – A ``string`` that is a unique id for that particular resource. -* ``connection_name`` - A ``string`` that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, ``get_connections`` will simply return the authenticated user's basic information. +* ``connection_name`` - A ``string`` that specifies the connection or edge + between objects, e.g., feed, friends, groups, likes, posts. If left empty, + ``get_connections`` will simply return the authenticated user's basic + information. **Example** @@ -105,8 +118,11 @@ Writes the given object to the graph, connected to the given parent. **Parameters** -* ``parent_object`` – A ``string`` that is a unique id for that particular resource. The ``parent_object`` is parent of a connection or edge. E.g., profile is a parent of a feed, and a post is a parent of a comment. -* ``connection_name`` - A ``string`` that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. +* ``parent_object`` – A ``string`` that is a unique id for that particular + resource. The ``parent_object`` is parent of a connection or edge. E.g., + profile is a parent of a feed, and a post is a parent of a comment. +* ``connection_name`` - A ``string`` that specifies the connection or edge + between objects, e.g., feed, friends, groups, likes, posts. **Example** @@ -124,12 +140,17 @@ Writes the given object to the graph, connected to the given parent. put_wall_post(message, attachment, profile_id) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Writes a wall post to the given profile's wall. It defaults to writing to the authenticated user's wall if no ``profile_id`` is specified. +Writes a wall post to the given profile's wall. It defaults to writing to the +authenticated user's wall if no ``profile_id`` is specified. **Parameters** * ``message`` - A ``string`` that will be posted to the user's wall. -* ``attachment`` - A ``dict`` that adds a structured attachment to the message being posted to the Wall. If you are sharing a URL, you will want to use the ``attachment`` parameter so that a thumbnail preview appears in the post. It should be a ``dict`` of the form: +* ``attachment`` - A ``dict`` that adds a structured attachment to the message + being posted to the Wall. If you are sharing a URL, you will want to use the + ``attachment`` parameter so that a thumbnail preview appears in the post. It + should be a ``dict`` of the form: + .. code-block:: python attachment = { @@ -139,7 +160,9 @@ Writes a wall post to the given profile's wall. It defaults to writing to the au 'description': '', 'picture': '' } -* ``profile_id`` - A ``string`` that is a unique id for that particular user. Defaults to the authenticated user's wall. + +* ``profile_id`` - A ``string`` that is a unique id for that particular user. + Defaults to the authenticated user's wall. **Example** @@ -198,7 +221,9 @@ Uploads an image using multipart/form-data. * ``image`` - An image of the ``file`` type * ``message`` - A ``string`` that will caption the image -* ``album_id`` - A ``string`` that is a unique id for an album. If no ``album_id`` is provided, the photo posts to /me/photos which uses, or creates and uses, an album for your application. +* ``album_id`` - A ``string`` that is a unique id for an album. If no + ``album_id`` is provided, the photo posts to /me/photos which uses, or + creates and uses, an album for your application. **Example** From b2072987b26f72ad7c5f294cfc74aa054b66abfb Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 29 Dec 2014 12:52:25 -0500 Subject: [PATCH 079/269] Minor grammar and wording changes. --- docs/api.rst | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 620a0875..8dbac288 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -22,9 +22,8 @@ You can read more about `Facebook's Graph API here`_. * ``access_token`` – A ``string`` that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. -* ``timeout`` - A ``float`` describing the timeout of the request in seconds. - I.e., you can tell the client to stop waiting for a response after a given - number of seconds with the timeout parameter. `See more here`_. +* ``timeout`` - A ``float`` describing (in seconds) how long the client will + waiting for a response from Facebook's servers. `See more here`_. * ``version`` - A ``string`` describing the `version of Facebook's Graph API to use`_. Valid API versions are ``1.0``, ``2.0``, ``2.1`` and ``2.2``. The default version is ``1.0`` and is used if the version keyword argument is not @@ -55,7 +54,7 @@ Returns the given object from the graph as a ``dict``. A list of **Parameters** -* ``id`` – A ``string`` that is a unique id for that particular resource. +* ``id`` – A ``string`` that is a unique ID for that particular resource. **Example** @@ -68,12 +67,12 @@ Returns the given object from the graph as a ``dict``. A list of get_objects(id, \*\*args) ^^^^^^^^^^^^^^^^^^^^^^^^^ -Returns all of the given objects from the graph as a ``dict``. Each given id +Returns all of the given objects from the graph as a ``dict``. Each given ID maps to an object. **Parameters** -* ``ids`` – A ``list`` containing ids for multiple resources. +* ``ids`` – A ``list`` containing IDs for multiple resources. **Example** @@ -90,11 +89,11 @@ maps to an object. get_connections(id, connection_name, \*\*args) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Returns all connections for given object as a ``dict``. +Returns all connections for a given object as a ``dict``. **Parameters** -* ``id`` – A ``string`` that is a unique id for that particular resource. +* ``id`` – A ``string`` that is a unique ID for that particular resource. * ``connection_name`` - A ``string`` that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. If left empty, ``get_connections`` will simply return the authenticated user's basic @@ -118,9 +117,9 @@ Writes the given object to the graph, connected to the given parent. **Parameters** -* ``parent_object`` – A ``string`` that is a unique id for that particular - resource. The ``parent_object`` is parent of a connection or edge. E.g., - profile is a parent of a feed, and a post is a parent of a comment. +* ``parent_object`` – A ``string`` that is a unique ID for that particular + resource. The ``parent_object`` is the parent of a connection or edge. E.g., + profile is the parent of a feed, and a post is the parent of a comment. * ``connection_name`` - A ``string`` that specifies the connection or edge between objects, e.g., feed, friends, groups, likes, posts. @@ -161,7 +160,7 @@ authenticated user's wall if no ``profile_id`` is specified. 'picture': '' } -* ``profile_id`` - A ``string`` that is a unique id for that particular user. +* ``profile_id`` - A ``string`` that is a unique ID for that particular user. Defaults to the authenticated user's wall. **Example** @@ -239,11 +238,11 @@ Uploads an image using multipart/form-data. delete_object(id) ^^^^^^^^^^^^^^^^^ -Deletes the object with the given id from the graph. +Deletes the object with the given ID from the graph. **Parameters** -* ``id`` - A ``string`` that is a unique id for a particular resource. +* ``id`` - A ``string`` that is a unique ID for a particular resource. **Example** From 060c25aadf4d134f917b0d55ffdd1f7276c39b09 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 29 Dec 2014 12:53:37 -0500 Subject: [PATCH 080/269] Change Graph API reference link. Do not link to a specific version in the URL, since Facebook seems to update the API every few months. --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 8dbac288..53f8f84e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -50,7 +50,7 @@ get_object(id, \*\*args) Returns the given object from the graph as a ``dict``. A list of `supported objects can be found here`_. -.. _supported objects can be found here: https://developers.facebook.com/docs/graph-api/reference/v2.2 +.. _supported objects can be found here: https://developers.facebook.com/docs/graph-api/reference/ **Parameters** From e8cb253f9ec61e1c606c8ec3d6afa5dfbc0ae8e0 Mon Sep 17 00:00:00 2001 From: Viktor Voronin Date: Mon, 29 Dec 2014 20:50:52 +0100 Subject: [PATCH 081/269] Improve imports and follow PEP8 --- facebook/__init__.py | 8 +------- test/test_facebook.py | 11 +++++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index abec3be7..f139e729 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -33,7 +33,6 @@ """ -import urllib # noqa import hashlib import hmac import base64 @@ -41,15 +40,10 @@ import json import re -# Find a query string parser try: - from urllib.parse import parse_qs + from urllib.parse import parse_qs, urlencode except ImportError: from urlparse import parse_qs - -try: - from urllib.parse import urlencode -except ImportError: from urllib import urlencode from . import version diff --git a/test/test_facebook.py b/test/test_facebook.py index 8228e2c8..9b18cfbd 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -17,6 +17,11 @@ import os import unittest +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode + class FacebookTestCase(unittest.TestCase): """Sets up application ID and secret from environment.""" @@ -98,12 +103,6 @@ def test_fql(self): class TestAuthURL(FacebookTestCase): def test_auth_url(self): - - try: - from urllib.parse import urlencode - except ImportError: - from urllib import urlencode - perms = ['email', 'birthday'] redirect_url = 'https://localhost/facebook/callback/' From a1f76c61b50a7b38f3e8b6fa946e74e9cc15d1c2 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Jan 2015 16:57:29 -0500 Subject: [PATCH 082/269] Update auth_url to deal with querystring order. We cannot guarantee that the querystring order returned by auth_url will always be the same, so we need to compare all components of the returned URL individually so that the test does not intermittently fail. --- test/test_facebook.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 9b18cfbd..926f96a3 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -18,8 +18,9 @@ import unittest try: - from urllib.parse import urlencode + from urllib.parse import parse_qs, urlencode, urlparse except ImportError: + from urlparse import parse_qs, urlparse from urllib import urlencode @@ -112,7 +113,19 @@ def test_auth_url(self): scope=','.join(perms))) actual_url = facebook.auth_url(self.app_id, redirect_url, perms=perms) - self.assertEqual(actual_url, expected_url) + # Since the order of the query string parameters might be + # different in each URL, we cannot just compare them to each + # other. + expected_url_result = urlparse(expected_url) + actual_url_result = urlparse(actual_url) + expected_query = parse_qs(expected_url_result.query) + actual_query = parse_qs(actual_url_result.query) + + self.assertEqual(actual_url_result.scheme, expected_url_result.scheme) + self.assertEqual(actual_url_result.netloc, expected_url_result.netloc) + self.assertEqual(actual_url_result.path, expected_url_result.path) + self.assertEqual(actual_url_result.params, expected_url_result.params) + self.assertEqual(actual_query, expected_query) if __name__ == '__main__': From ec5c187a4e2456060c518aaa7f3e41760f1d246d Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Jan 2015 17:03:01 -0500 Subject: [PATCH 083/269] Fix PEP8 error in tests. --- test/test_facebook.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 926f96a3..f6013e78 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -45,7 +45,9 @@ class TestGetAppAccessToken(FacebookTestCase): """ def test_get_app_access_token(self): token = facebook.get_app_access_token(self.app_id, self.secret) - assert(isinstance(token, str) or isinstance(token, unicode)) + # Since "unicode" does not exist in Python 3, we cannot check + # the following line with flake8 (hence the noqa comment). + assert(isinstance(token, str) or isinstance(token, unicode)) # noqa class TestAPIVersion(FacebookTestCase): From 63144f26f9487b39e54c55343386832755805321 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Jan 2015 17:03:42 -0500 Subject: [PATCH 084/269] Use flake8 to check tests module. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8b069475..7884c037 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ install: before_script: - "flake8 --ignore=F401 examples" - "flake8 facebook" + - "flake8 test" script: python -m test.test_facebook env: global: From 1fafcc9a3a7581082035ba6d6a098551bba6e4b9 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 26 Jan 2015 05:57:15 -0500 Subject: [PATCH 085/269] Remove "Examples" section from README. Examples should be stored in the documentation, not in the README. --- README.rst | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/README.rst b/README.rst index 8ec3c6a3..95c99675 100644 --- a/README.rst +++ b/README.rst @@ -11,43 +11,6 @@ Facebook authentication. You can read more about the Graph API by accessing its .. _Facebook JavaScript SDK: https://developers.facebook.com/docs/reference/javascript/ .. _official documentation: https://developers.facebook.com/docs/reference/api/ -Examples -======== - -Basic usage: - -.. code-block:: python - - import facebook - graph = facebook.GraphAPI(oauth_access_token) - profile = graph.get_object("me") - friends = graph.get_connections("me", "friends") - graph.put_object("me", "feed", message="I am writing on my wall!") - -Photo uploads: - -.. code-block:: python - - graph = facebook.GraphAPI(oauth_access_token) - tags = json.dumps([{'x':50, 'y':50, 'tag_uid':12345}, {'x':10, 'y':60, 'tag_text':'a turtle'}]) - graph.put_photo(open('img.jpg'), 'Look at this cool photo!', album_id_or_None, tags=tags) - -If you are using the module within a web application with the JavaScript SDK, -you can also use the module to use Facebook for login, parsing the cookie set -by the JavaScript SDK for logged in users. For example, in Google AppEngine, -you could get the profile of the logged in user with: - -.. code-block:: python - - user = facebook.get_user_from_cookie(self.request.cookies, key, secret) - if user: - graph = facebook.GraphAPI(user["access_token"]) - profile = graph.get_object("me") - friends = graph.get_connections("me", "friends") - - -You can see a full AppEngine example application in examples/appengine. - Licensing ========= From 4662e4f6457ca475f89fdd13c5a0684c632264ed Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 26 Jan 2015 06:25:29 -0500 Subject: [PATCH 086/269] Fix regression in put_photo. Have `put_photo` return the results of the HTTP request. Fix #168. Thanks @timwu20! --- facebook/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index f139e729..8fa8857c 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -199,10 +199,11 @@ def put_photo(self, image, message=None, album_id=None, **kwargs): """ object_id = album_id or "me" kwargs.update({"message": message}) - self.request(self.version + "/" + object_id, - post_args=kwargs, - files={"file": image}, - method="POST") + return self.request( + self.version + "/" + object_id, + post_args=kwargs, + files={"file": image}, + method="POST") def get_version(self): """Fetches the current version number of the Graph API being used.""" From 80afaac1ff6589132ef9d1444601b57ddebea03e Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 26 Jan 2015 08:41:38 -0500 Subject: [PATCH 087/269] Update put_photo method. Fix #104. Thanks to @spectralsun. Create new `album_path` parameter to allow using put_photo to upload Page pictures (and event pictures in Graph API v1.0). Remove `message` parameter since it was just being added to kwargs anyway. Note that this breaks backward compatibility. --- facebook/__init__.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 8fa8857c..46ab1fe8 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -188,21 +188,18 @@ def delete_request(self, user_id, request_id): """Deletes the Request with the given ID for the given user.""" self.request("%s_%s" % (request_id, user_id), method="DELETE") - def put_photo(self, image, message=None, album_id=None, **kwargs): - """Uploads an image using multipart/form-data. + def put_photo(self, image, album_path="me/photos", **kwargs): + """ + Upload an image using multipart/form-data. - image=File like object for the image - message=Caption for your image - album_id=None posts to /me/photos which uses or creates and uses - an album for your application. + image - A file object representing the image to be uploaded. + album_path - A path representing where the image should be uploaded. """ - object_id = album_id or "me" - kwargs.update({"message": message}) return self.request( - self.version + "/" + object_id, + self.version + "/" + album_path, post_args=kwargs, - files={"file": image}, + files={"source": image}, method="POST") def get_version(self): From f739c0f213da94db37deedac79257adf7d5c9364 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 26 Jan 2015 08:47:47 -0500 Subject: [PATCH 088/269] Update put_photo documentation. Fix #177 by removing the bad example that does not work properly. --- docs/api.rst | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 53f8f84e..b6e72b06 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -211,29 +211,31 @@ Writes a like to the given object. graph.put_like(object_id='comment_id') -put_photo(image, message, album_id, \*\*kwargs) +put_photo(image, message, path, \*\*kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Uploads an image using multipart/form-data. +https://developers.facebook.com/docs/graph-api/reference/user/photos#publish + +Upload an image using multipart/form-data. Returns JSON with the IDs of the +photo and its post. **Parameters** -* ``image`` - An image of the ``file`` type -* ``message`` - A ``string`` that will caption the image -* ``album_id`` - A ``string`` that is a unique id for an album. If no - ``album_id`` is provided, the photo posts to /me/photos which uses, or - creates and uses, an album for your application. + * ``image`` - A file object representing the image to be uploaded. + * ``album_path`` - A path representing where the image should be uploaded. + Defaults to `/me/photos` which creates/uses a custom album for each + Facebook application. **Example** .. code-block:: python - tags = json.dumps([ - {'x':50, 'y':50, 'tag_uid':12345}, - {'x':10, 'y':60, 'tag_text':'a turtle'} - ]) - graph.put_photo(image=open('img.jpg'), message='Look at this cool photo!', - tags=tags) + # Upload an image with a caption. + graph.put_photo(image=open('img.jpg'), message='Look at this cool photo!') + # Upload a photo to an album. + graph.put_photo(image=open("img.jpg"), album_path=album_id + "/photos") + # Upload a profile photo for a Page. + graph.put_photo(image=open("img.jpg"), album_path=page_id + "/picture") delete_object(id) ^^^^^^^^^^^^^^^^^ From 4bb0a1a417b7f37bee101c4fcbf826ef875e7ebd Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 26 Jan 2015 09:16:26 -0500 Subject: [PATCH 089/269] Fix heading of put_photo in documentation. :rage: --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index b6e72b06..d0aff1c0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -211,7 +211,7 @@ Writes a like to the given object. graph.put_like(object_id='comment_id') -put_photo(image, message, path, \*\*kwargs) +put_photo(image, album_path, \*\*kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ https://developers.facebook.com/docs/graph-api/reference/user/photos#publish From 5f311b4642372c37c1faa375d92f040311f8d672 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 26 Jan 2015 09:22:34 -0500 Subject: [PATCH 090/269] Remove parameters from method headings in docs. Changing parameters of a method should not change the documentation heading and break bookmarks. --- docs/api.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index d0aff1c0..03867d30 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -44,8 +44,8 @@ You can read more about `Facebook's Graph API here`_. Methods ------- -get_object(id, \*\*args) -^^^^^^^^^^^^^^^^^^^^^^^^ +get_object +^^^^^^^^^^ Returns the given object from the graph as a ``dict``. A list of `supported objects can be found here`_. @@ -64,8 +64,8 @@ Returns the given object from the graph as a ``dict``. A list of print post['message'] -get_objects(id, \*\*args) -^^^^^^^^^^^^^^^^^^^^^^^^^ +get_objects +^^^^^^^^^^^ Returns all of the given objects from the graph as a ``dict``. Each given ID maps to an object. @@ -86,8 +86,8 @@ maps to an object. print posts[post_id]['created_time'] -get_connections(id, connection_name, \*\*args) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +get_connections +^^^^^^^^^^^^^^^ Returns all connections for a given object as a ``dict``. @@ -110,8 +110,8 @@ Returns all connections for a given object as a ``dict``. comments = graph.get_connections(id='post_id', connection_name='comments') -put_object(parent_object, connection_name, \*\*data) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +put_object +^^^^^^^^^^ Writes the given object to the graph, connected to the given parent. @@ -136,8 +136,8 @@ Writes the given object to the graph, connected to the given parent. message='First!') -put_wall_post(message, attachment, profile_id) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +put_wall_post +^^^^^^^^^^^^^ Writes a wall post to the given profile's wall. It defaults to writing to the authenticated user's wall if no ``profile_id`` is specified. @@ -178,8 +178,8 @@ authenticated user's wall if no ``profile_id`` is specified. graph.put_wall_post(message='Check this out...', attachment=attachment) -put_comment(object_id, message) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +put_comment +^^^^^^^^^^^ Writes the given message as a comment on an object. @@ -195,8 +195,8 @@ Writes the given message as a comment on an object. graph.put_comment(object_id='post_id', message='Great post...') -put_like(object_id) -^^^^^^^^^^^^^^^^^^^ +put_like +^^^^^^^^ Writes a like to the given object. @@ -211,8 +211,8 @@ Writes a like to the given object. graph.put_like(object_id='comment_id') -put_photo(image, album_path, \*\*kwargs) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +put_photo +^^^^^^^^^ https://developers.facebook.com/docs/graph-api/reference/user/photos#publish @@ -237,8 +237,8 @@ photo and its post. # Upload a profile photo for a Page. graph.put_photo(image=open("img.jpg"), album_path=page_id + "/picture") -delete_object(id) -^^^^^^^^^^^^^^^^^ +delete_object +^^^^^^^^^^^^^ Deletes the object with the given ID from the graph. From 4ab3657fecf8bac276b824a609f1f4328b3dc991 Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Sun, 15 Feb 2015 15:26:02 -0600 Subject: [PATCH 091/269] Use new world print() syntax Python 2.6, 2.7 support the new style, and Python 3 requires it. Not sure yet if this API is Python3 compliant, but at least the examples are now! --- docs/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 03867d30..664b3523 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -61,7 +61,7 @@ Returns the given object from the graph as a ``dict``. A list of .. code-block:: python post = graph.get_object(id='post_id') - print post['message'] + print(post['message']) get_objects @@ -83,7 +83,7 @@ maps to an object. # Each given id maps to an object. for post_id in post_ids: - print posts[post_id]['created_time'] + print(posts[post_id]['created_time']) get_connections From 04443b34bf6446c70e4ddbc5cfe3faf4817f5f8c Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Thu, 19 Feb 2015 02:08:57 -0500 Subject: [PATCH 092/269] Fix majority of pep8 E402 errors in example files. --- examples/appengine/example.py | 7 ++++--- examples/newsfeed/facebookclient.py | 8 ++++---- examples/oauth/facebookoauth.py | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/examples/appengine/example.py b/examples/appengine/example.py index 2db52157..218fc01e 100644 --- a/examples/appengine/example.py +++ b/examples/appengine/example.py @@ -27,9 +27,6 @@ 5. Change the application name in app.yaml. """ -FACEBOOK_APP_ID = "your app id" -FACEBOOK_APP_SECRET = "your app secret" - import facebook import webapp2 import os @@ -39,6 +36,10 @@ from google.appengine.ext import db from webapp2_extras import sessions + +FACEBOOK_APP_ID = "your app id" +FACEBOOK_APP_SECRET = "your app secret" + config = {} config['webapp2_extras.sessions'] = dict(secret_key='') diff --git a/examples/newsfeed/facebookclient.py b/examples/newsfeed/facebookclient.py index 3cdb2597..3d8a1d3b 100644 --- a/examples/newsfeed/facebookclient.py +++ b/examples/newsfeed/facebookclient.py @@ -15,10 +15,6 @@ # under the License. """A Facebook stream client written against the Facebook Graph API.""" - -FACEBOOK_APP_ID = "your app id" -FACEBOOK_APP_SECRET = "your app secret" - import datetime import facebook import os @@ -31,6 +27,10 @@ from google.appengine.ext.webapp import template +FACEBOOK_APP_ID = "your app id" +FACEBOOK_APP_SECRET = "your app secret" + + class User(db.Model): id = db.StringProperty(required=True) created = db.DateTimeProperty(auto_now_add=True) diff --git a/examples/oauth/facebookoauth.py b/examples/oauth/facebookoauth.py index 82face7d..7cb08809 100644 --- a/examples/oauth/facebookoauth.py +++ b/examples/oauth/facebookoauth.py @@ -26,10 +26,6 @@ as it handles some complex authentication states that can only be detected in client-side code. """ - -FACEBOOK_APP_ID = "your app id" -FACEBOOK_APP_SECRET = "your app secret" - import base64 import cgi import Cookie @@ -49,6 +45,10 @@ from google.appengine.ext.webapp import template +FACEBOOK_APP_ID = "your app id" +FACEBOOK_APP_SECRET = "your app secret" + + class User(db.Model): id = db.StringProperty(required=True) created = db.DateTimeProperty(auto_now_add=True) From 1ac306ffc98074fa82b4389768264ac54b95423b Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Thu, 19 Feb 2015 02:16:32 -0500 Subject: [PATCH 093/269] Ignore future E402 errors in examples. Since the Flask example still has a E402 error in its __init__.py and I don't have any current interest in spending large amounts of time to try and fix it, just ignore any future import errors in the examples directory. If this bothers you, either submit a pull request or refer to http://ur1.ca/jrab9. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7884c037..08d32784 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ install: - "pip install flake8" - "pip install ." before_script: - - "flake8 --ignore=F401 examples" + - "flake8 --ignore=E402,F401 examples" - "flake8 facebook" - "flake8 test" script: python -m test.test_facebook From e4c4b333ae47cbdb6e001df7a4c1ddfe1e65f9c9 Mon Sep 17 00:00:00 2001 From: Aryan Yazdani Date: Wed, 25 Mar 2015 16:10:45 -0400 Subject: [PATCH 094/269] adding very basic v2.3 support and equivalent test case --- facebook/__init__.py | 2 +- test/test_facebook.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 46ab1fe8..d23872f0 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -85,7 +85,7 @@ class GraphAPI(object): def __init__(self, access_token=None, timeout=None, version=None): # The default version is only used if the version kwarg does not exist. default_version = "1.0" - valid_API_versions = ["1.0", "2.0", "2.1", "2.2"] + valid_API_versions = ["1.0", "2.0", "2.1", "2.2", "2.3"] self.access_token = access_token self.timeout = timeout diff --git a/test/test_facebook.py b/test/test_facebook.py index f6013e78..69e7d4a3 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -74,6 +74,10 @@ def test_version_2_2(self): graph = facebook.GraphAPI(version=2.2) self.assertEqual(graph.get_version(), 2.2) + def test_version_2_3(self): + graph = facebook.GraphAPI(version=2.3) + self.assertEqual(graph.get_version(), 2.3) + def test_invalid_version(self): self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version=1.2) From c7b48d4abd66cb2ae04b34d975022687c1d6ed43 Mon Sep 17 00:00:00 2001 From: Sergey Chvalyuk Date: Wed, 14 Jan 2015 00:45:30 +0200 Subject: [PATCH 095/269] https://github.com/pythonforfacebook/facebook-sdk/issues/187 --- facebook/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/facebook/__init__.py b/facebook/__init__.py index d23872f0..bc998d8b 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -233,6 +233,9 @@ def request( """ args = args or {} + if post_args is not None: + method="POST" + if self.access_token: if post_args is not None: post_args["access_token"] = self.access_token From d626d833f34d019d24c6f5dcdcb6484ce2524dcf Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 15 Apr 2015 07:11:01 -0400 Subject: [PATCH 096/269] Fix PEP8 error from c7b48d4abd66cb2ae04b34d975022. --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index bc998d8b..3bb4c65e 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -234,7 +234,7 @@ def request( args = args or {} if post_args is not None: - method="POST" + method = "POST" if self.access_token: if post_args is not None: From fe7cd9fb42320fab5ce805382d151a9e654d1fab Mon Sep 17 00:00:00 2001 From: Nick Macri Date: Mon, 28 Apr 2014 18:32:04 -0400 Subject: [PATCH 097/269] add test to ensure regression of #127 does not happen in the future --- test/test_facebook.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/test_facebook.py b/test/test_facebook.py index 69e7d4a3..03ff6637 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -133,6 +133,19 @@ def test_auth_url(self): self.assertEqual(actual_url_result.params, expected_url_result.params) self.assertEqual(actual_query, expected_query) +class TestExtendAccessToken(FacebookTestCase): + """ + Test if extend_access_token requests the correct endpoint. + + Note that this only tests whether extend_access_token returns the correct + error message when called without a proper user-access token. + """ + def test_extend_access_token(self): + try: + facebook.extend_access_token(self.app_id, self.secret) + except facebook.GraphAPIError, e: + assert e.message == 'No user access token specified' + if __name__ == '__main__': unittest.main() From dad16301d52e80b31dfdc313e9730189e9f5c060 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 2 May 2015 02:05:32 -0400 Subject: [PATCH 098/269] PEP8 fixes for new test. --- test/test_facebook.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 03ff6637..26b9f6bd 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -133,19 +133,20 @@ def test_auth_url(self): self.assertEqual(actual_url_result.params, expected_url_result.params) self.assertEqual(actual_query, expected_query) + class TestExtendAccessToken(FacebookTestCase): """ Test if extend_access_token requests the correct endpoint. - Note that this only tests whether extend_access_token returns the correct + Note that this only tests whether extend_access_token returns the correct error message when called without a proper user-access token. """ - def test_extend_access_token(self): + def test_extend_access_token(self): try: facebook.extend_access_token(self.app_id, self.secret) - except facebook.GraphAPIError, e: + except facebook.GraphAPIError as e: assert e.message == 'No user access token specified' - + if __name__ == '__main__': unittest.main() From eb82f9b8fe544295d2d08e6bfa9a6c87ba84b95b Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 2 May 2015 02:10:08 -0400 Subject: [PATCH 099/269] Update test based on changes in library and API. - `extend_access_token` is now located inside GraphAPI(). - Facebook error message has changed. --- test/test_facebook.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 26b9f6bd..449becd2 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -143,9 +143,10 @@ class TestExtendAccessToken(FacebookTestCase): """ def test_extend_access_token(self): try: - facebook.extend_access_token(self.app_id, self.secret) + facebook.GraphAPI().extend_access_token(self.app_id, self.secret) except facebook.GraphAPIError as e: - assert e.message == 'No user access token specified' + self.assertEqual( + e.message, "fb_exchange_token parameter not specified") if __name__ == '__main__': From 51d40b0edd07a73aaec865e935b5f648bf51f5f4 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 2 May 2015 02:29:09 -0400 Subject: [PATCH 100/269] Remove 1.0 from valid API versions. --- facebook/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 3bb4c65e..867ffd64 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -84,8 +84,8 @@ class GraphAPI(object): def __init__(self, access_token=None, timeout=None, version=None): # The default version is only used if the version kwarg does not exist. - default_version = "1.0" - valid_API_versions = ["1.0", "2.0", "2.1", "2.2", "2.3"] + default_version = "2.0" + valid_API_versions = ["2.0", "2.1", "2.2", "2.3"] self.access_token = access_token self.timeout = timeout @@ -101,7 +101,7 @@ def __init__(self, access_token=None, timeout=None, version=None): self.version = "v" + str(version) else: raise GraphAPIError("Version number should be in the" - " following format: #.# (e.g. 1.0).") + " following format: #.# (e.g. 2.0).") else: self.version = "v" + default_version From 93a93050bd3841a357e534ce7c11425792a8e866 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 2 May 2015 02:30:08 -0400 Subject: [PATCH 101/269] Remove version 1.0 test. --- test/test_facebook.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 449becd2..5b4525a4 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -58,10 +58,6 @@ def test_no_version(self): self.assertNotEqual( graph.version, "", "Version should not be an empty string.") - def test_version_1_0(self): - graph = facebook.GraphAPI(version=1.0) - self.assertEqual(graph.get_version(), 1.0) - def test_version_2_0(self): graph = facebook.GraphAPI(version=2.0) self.assertEqual(graph.get_version(), 2.0) From de82392796c3cfa04a38dc0b4117de4656ae8318 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 2 May 2015 02:30:16 -0400 Subject: [PATCH 102/269] Update invalid format tests. Change invalid format tests to use API versions which start with 2.* instead of 1.*. --- test/test_facebook.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 5b4525a4..e739fab6 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -80,13 +80,13 @@ def test_invalid_version(self): def test_invalid_format(self): self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, version="1.a") + facebook.GraphAPI, version="2.a") self.assertRaises(facebook.GraphAPIError, facebook.GraphAPI, version="a.1") self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, version=1.23) + facebook.GraphAPI, version=2.23) self.assertRaises(facebook.GraphAPIError, - facebook.GraphAPI, version="1.23") + facebook.GraphAPI, version="2.23") class TestFQL(FacebookTestCase): From d3aae05debde9405efb6ba3e529b7264c4b10910 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 2 May 2015 02:32:28 -0400 Subject: [PATCH 103/269] Remove references to version 1.0 in documentation. --- docs/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 664b3523..7db4ba45 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -25,8 +25,8 @@ You can read more about `Facebook's Graph API here`_. * ``timeout`` - A ``float`` describing (in seconds) how long the client will waiting for a response from Facebook's servers. `See more here`_. * ``version`` - A ``string`` describing the `version of Facebook's Graph API to - use`_. Valid API versions are ``1.0``, ``2.0``, ``2.1`` and ``2.2``. The - default version is ``1.0`` and is used if the version keyword argument is not + use`_. Valid API versions are ``2.0``, ``2.1`` and ``2.2``. The + default version is ``2.0`` and is used if the version keyword argument is not provided. .. _Read more about access tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens From 7e8032f7e0ff8d24fe19359526890cfa2ca56011 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 6 May 2015 17:09:28 -0400 Subject: [PATCH 104/269] Run flake8 tests after main test suite. Run main test suite before validating code with flake8, so that it is easier to determine whether PEP8 issues are the only thing preventing tests from succeeding. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08d32784..1eb6347f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,11 +6,11 @@ python: install: - "pip install flake8" - "pip install ." -before_script: +script: + - "python -m test.test_facebook" - "flake8 --ignore=E402,F401 examples" - "flake8 facebook" - "flake8 test" -script: python -m test.test_facebook env: global: - FACEBOOK_APP_ID=198798870326423 From 185ce16cb028a7e2f1120d083fd6d5b95636aa3a Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 6 May 2015 17:11:53 -0400 Subject: [PATCH 105/269] Add Python 3.4 to tested Python versions. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1eb6347f..f702950a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: python python: + - "3.4" - "3.3" - "2.7" - "2.6" From 997c35362933be9fea14a0b75e512a8b28ec82f3 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Wed, 6 May 2015 17:37:34 -0400 Subject: [PATCH 106/269] Add Python 3.4 to supported versions. See 185ce16cb028a7e2f1120d083fd6d5b95636aa3a. --- docs/install.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index d7a9417c..c57b8e2a 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -2,8 +2,8 @@ Installation ============ -The SDK currently supports Python 2.6, 2.7, and 3.3. The `requests`_ package is -required. +The SDK currently supports Python 2.6, 2.7, 3.3, and 3.4. The `requests`_ +package is required. We recommend using `pip`_ and `virtualenv`_ to install the SDK. Please note that the SDK's Python package is called **facebook-sdk**: :: From c3eb8ad52e5ebb4e4fe24e773e4e33702c7de105 Mon Sep 17 00:00:00 2001 From: Dean Malmgren Date: Thu, 28 May 2015 14:33:08 -0500 Subject: [PATCH 107/269] use data-scope and public_profile on the login button --- examples/flask/app/templates/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/flask/app/templates/login.html b/examples/flask/app/templates/login.html index 1ea74d80..ad172929 100644 --- a/examples/flask/app/templates/login.html +++ b/examples/flask/app/templates/login.html @@ -3,7 +3,7 @@

Python for Facebook SDK

Flask example

- From 22aab53368ce181626a217d07e16e2e68100e2f2 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 21 Jun 2015 11:26:38 -0400 Subject: [PATCH 108/269] Remove broken AppEngine examples. --- examples/appengine/app.yaml | 13 -- examples/appengine/example.html | 43 ------ examples/appengine/example.py | 161 ----------------------- examples/newsfeed/app.yaml | 19 --- examples/newsfeed/facebookclient.py | 136 ------------------- examples/newsfeed/static/base.css | 162 ----------------------- examples/newsfeed/static/favicon.ico | Bin 1150 -> 0 bytes examples/newsfeed/static/robots.txt | 2 - examples/newsfeed/templates/base.html | 29 ----- examples/newsfeed/templates/home.html | 52 -------- examples/newsfeed/templates/index.html | 7 - examples/oauth/app.yaml | 8 -- examples/oauth/facebookoauth.py | 174 ------------------------- examples/oauth/oauth.html | 17 --- 14 files changed, 823 deletions(-) delete mode 100644 examples/appengine/app.yaml delete mode 100644 examples/appengine/example.html delete mode 100644 examples/appengine/example.py delete mode 100644 examples/newsfeed/app.yaml delete mode 100644 examples/newsfeed/facebookclient.py delete mode 100644 examples/newsfeed/static/base.css delete mode 100644 examples/newsfeed/static/favicon.ico delete mode 100644 examples/newsfeed/static/robots.txt delete mode 100644 examples/newsfeed/templates/base.html delete mode 100644 examples/newsfeed/templates/home.html delete mode 100644 examples/newsfeed/templates/index.html delete mode 100644 examples/oauth/app.yaml delete mode 100644 examples/oauth/facebookoauth.py delete mode 100644 examples/oauth/oauth.html diff --git a/examples/appengine/app.yaml b/examples/appengine/app.yaml deleted file mode 100644 index 7b1b54de..00000000 --- a/examples/appengine/app.yaml +++ /dev/null @@ -1,13 +0,0 @@ -application: facebook-example-py27 -version: 1 -runtime: python27 -api_version: 1 -threadsafe: true - -handlers: -- url: /.* - script: example.app - -libraries: -- name: jinja2 - version: latest diff --git a/examples/appengine/example.html b/examples/appengine/example.html deleted file mode 100644 index 5f68f935..00000000 --- a/examples/appengine/example.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - Facebook Example - - - - - {% if current_user %} -

-

Hello, {{ current_user.name|escape }}

- {% endif %} - -
- - {% if current_user %} -
- Upload photo test: - - Enter URL to URLFetch from: - - -
- {% endif %} - - - diff --git a/examples/appengine/example.py b/examples/appengine/example.py deleted file mode 100644 index 218fc01e..00000000 --- a/examples/appengine/example.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2010 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -A barebones AppEngine application that uses Facebook for login. - -1. Make sure you add a copy of facebook.py (from python-sdk/src/) - into this directory so it can be imported. -2. Don't forget to tick Login With Facebook on your facebook app's - dashboard and place the app's url wherever it is hosted -3. Place a random, unguessable string as a session secret below in - config dict. -4. Fill app id and app secret. -5. Change the application name in app.yaml. - -""" -import facebook -import webapp2 -import os -import jinja2 -import urllib2 - -from google.appengine.ext import db -from webapp2_extras import sessions - - -FACEBOOK_APP_ID = "your app id" -FACEBOOK_APP_SECRET = "your app secret" - -config = {} -config['webapp2_extras.sessions'] = dict(secret_key='') - - -class User(db.Model): - id = db.StringProperty(required=True) - created = db.DateTimeProperty(auto_now_add=True) - updated = db.DateTimeProperty(auto_now=True) - name = db.StringProperty(required=True) - profile_url = db.StringProperty(required=True) - access_token = db.StringProperty(required=True) - - -class BaseHandler(webapp2.RequestHandler): - """Provides access to the active Facebook user in self.current_user - - The property is lazy-loaded on first access, using the cookie saved - by the Facebook JavaScript SDK to determine the user ID of the active - user. See http://developers.facebook.com/docs/authentication/ for - more information. - """ - @property - def current_user(self): - if self.session.get("user"): - # User is logged in - return self.session.get("user") - else: - # Either used just logged in or just saw the first page - # We'll see here - cookie = facebook.get_user_from_cookie(self.request.cookies, - FACEBOOK_APP_ID, - FACEBOOK_APP_SECRET) - if cookie: - # Okay so user logged in. - # Now, check to see if existing user - user = User.get_by_key_name(cookie["uid"]) - if not user: - # Not an existing user so get user info - graph = facebook.GraphAPI(cookie["access_token"]) - profile = graph.get_object("me") - user = User( - key_name=str(profile["id"]), - id=str(profile["id"]), - name=profile["name"], - profile_url=profile["link"], - access_token=cookie["access_token"] - ) - user.put() - elif user.access_token != cookie["access_token"]: - user.access_token = cookie["access_token"] - user.put() - # User is now logged in - self.session["user"] = dict( - name=user.name, - profile_url=user.profile_url, - id=user.id, - access_token=user.access_token - ) - return self.session.get("user") - return None - - def dispatch(self): - """ - This snippet of code is taken from the webapp2 framework documentation. - See more at - http://webapp-improved.appspot.com/api/webapp2_extras/sessions.html - - """ - self.session_store = sessions.get_store(request=self.request) - try: - webapp2.RequestHandler.dispatch(self) - finally: - self.session_store.save_sessions(self.response) - - @webapp2.cached_property - def session(self): - """ - This snippet of code is taken from the webapp2 framework documentation. - See more at - http://webapp-improved.appspot.com/api/webapp2_extras/sessions.html - - """ - return self.session_store.get_session() - - -class HomeHandler(BaseHandler): - def get(self): - template = jinja_environment.get_template('example.html') - self.response.out.write(template.render(dict( - facebook_app_id=FACEBOOK_APP_ID, - current_user=self.current_user - ))) - - def post(self): - url = self.request.get('url') - file = urllib2.urlopen(url) - graph = facebook.GraphAPI(self.current_user['access_token']) - response = graph.put_photo(file, "Test Image") - photo_url = ("http://www.facebook.com/" - "photo.php?fbid={0}".format(response['id'])) - self.redirect(str(photo_url)) - - -class LogoutHandler(BaseHandler): - def get(self): - if self.current_user is not None: - self.session['user'] = None - - self.redirect('/') - -jinja_environment = jinja2.Environment( - loader=jinja2.FileSystemLoader(os.path.dirname(__file__)) -) - -app = webapp2.WSGIApplication( - [('/', HomeHandler), ('/logout', LogoutHandler)], - debug=True, - config=config -) diff --git a/examples/newsfeed/app.yaml b/examples/newsfeed/app.yaml deleted file mode 100644 index 0f248dbb..00000000 --- a/examples/newsfeed/app.yaml +++ /dev/null @@ -1,19 +0,0 @@ -application: facebook-example -version: 1 -runtime: python -api_version: 1 - -handlers: -- url: /static - static_dir: static - -- url: /favicon\.ico - static_files: static/favicon.ico - upload: static/favicon.ico - -- url: /robots\.txt - static_files: static/robots.txt - upload: static/robots.txt - -- url: /.* - script: facebookclient.py diff --git a/examples/newsfeed/facebookclient.py b/examples/newsfeed/facebookclient.py deleted file mode 100644 index 3d8a1d3b..00000000 --- a/examples/newsfeed/facebookclient.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2010 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A Facebook stream client written against the Facebook Graph API.""" -import datetime -import facebook -import os -import os.path -import wsgiref.handlers - -from google.appengine.ext import db -from google.appengine.ext import webapp -from google.appengine.ext.webapp import util -from google.appengine.ext.webapp import template - - -FACEBOOK_APP_ID = "your app id" -FACEBOOK_APP_SECRET = "your app secret" - - -class User(db.Model): - id = db.StringProperty(required=True) - created = db.DateTimeProperty(auto_now_add=True) - updated = db.DateTimeProperty(auto_now=True) - name = db.StringProperty(required=True) - profile_url = db.StringProperty(required=True) - access_token = db.StringProperty(required=True) - - -class BaseHandler(webapp.RequestHandler): - """Provides access to the active Facebook user in self.current_user - - The property is lazy-loaded on first access, using the cookie saved - by the Facebook JavaScript SDK to determine the user ID of the active - user. See http://developers.facebook.com/docs/authentication/ for - more information. - """ - @property - def current_user(self): - """Returns the active user, or None if the user has not logged in.""" - if not hasattr(self, "_current_user"): - self._current_user = None - cookie = facebook.get_user_from_cookie( - self.request.cookies, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET) - if cookie: - # Store a local instance of the user data so we don't need - # a round-trip to Facebook on every request - user = User.get_by_key_name(cookie["uid"]) - if not user: - graph = facebook.GraphAPI(cookie["access_token"]) - profile = graph.get_object("me") - user = User(key_name=str(profile["id"]), - id=str(profile["id"]), - name=profile["name"], - profile_url=profile["link"], - access_token=cookie["access_token"]) - user.put() - elif user.access_token != cookie["access_token"]: - user.access_token = cookie["access_token"] - user.put() - self._current_user = user - return self._current_user - - @property - def graph(self): - """Returns a Graph API client for the current user.""" - if not hasattr(self, "_graph"): - if self.current_user: - self._graph = facebook.GraphAPI(self.current_user.access_token) - else: - self._graph = facebook.GraphAPI() - return self._graph - - def render(self, path, **kwargs): - args = dict(current_user=self.current_user, - facebook_app_id=FACEBOOK_APP_ID) - args.update(kwargs) - path = os.path.join(os.path.dirname(__file__), "templates", path) - self.response.out.write(template.render(path, args)) - - -class HomeHandler(BaseHandler): - def get(self): - if not self.current_user: - self.render("index.html") - return - try: - news_feed = self.graph.get_connections("me", "home") - except facebook.GraphAPIError: - self.render("index.html") - return - except: - news_feed = {"data": []} - for post in news_feed["data"]: - post["created_time"] = datetime.datetime.strptime( - post["created_time"], "%Y-%m-%dT%H:%M:%S+0000") + \ - datetime.timedelta(hours=7) - self.render("home.html", news_feed=news_feed) - - -class PostHandler(BaseHandler): - def post(self): - message = self.request.get("message") - if not self.current_user or not message: - self.redirect("/") - return - try: - self.graph.put_wall_post(message) - except: - pass - self.redirect("/") - - -def main(): - debug = os.environ.get("SERVER_SOFTWARE", "").startswith("Development/") - util.run_wsgi_app(webapp.WSGIApplication([ - (r"/", HomeHandler), - (r"/post", PostHandler), - ], debug=debug)) - - -if __name__ == "__main__": - main() diff --git a/examples/newsfeed/static/base.css b/examples/newsfeed/static/base.css deleted file mode 100644 index 4cb84c1c..00000000 --- a/examples/newsfeed/static/base.css +++ /dev/null @@ -1,162 +0,0 @@ -body { - background: white; - margin: 0; -} - -body, -input, -textarea { - color: #333; - font-family: "Lucida Grande", Tahoma, Verdana, Arial, sans-serif; - font-size: 13px; -} - -a { - color: #3b5998; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -img { - border: 0; -} - -table { - border: 0; - border-collapse: collapse; - border-spacing: 0; -} - -td { - border: 0; - padding: 0; -} - -#promo { - background: #eee; - padding: 8px; - border-bottom: 1px solid #ccc; - color: gray; - text-align: center; -} - -#body { - max-width: 800px; - margin: auto; - padding: 20px; -} - -#header h1 { - margin: 0; - padding: 0; - font-size: 15px; - line-height: 25px; -} - -#header .button { - float: right; -} - -#content { - clear: both; - margin-top: 15px; -} - -.clearfix:after { - clear: both; - content: "."; - display: block; - font-size: 0; - height: 0; - line-height: 0; - visibility: hidden; -} - -.clearfix { - display: block; - zoom: 1; -} - -.feed .entry { - padding-top: 9px; - border-top: 1px solid #eee; - margin-top: 9px; -} - -.feed .entry .profile { - float: left; - line-height: 0; -} - -.feed .entry .profile img { - width: 50px; - height: 50px; -} - -.feed .entry .body { - margin-left: 60px; -} - -.feed .entry .name { - font-weight: bold; -} - -.feed .entry .attachment { - font-size: 11px; - line-height: 15px; - margin-top: 8px; - margin-bottom: 8px; - color: gray; -} - -.feed .entry .attachment.nopicture { - border-left: 2px solid #ccc; - padding-left: 10px; -} - -.feed .entry .attachment .picture { - line-height: 0; - float: left; - padding-right: 10px; -} - -.feed .entry .attachment .picture img { - border: 1px solid #ccc; - padding: 3px; -} - -.feed .entry .attachment .picture a:hover img { - border-color: #3b5998; -} - -.feed .entry .info { - font-size: 11px; - line-height: 17px; - margin-top: 3px; - color: gray; -} - -.feed .entry .info.icon { - background-position: left center; - background-repeat: no-repeat; - padding-left: 20px; -} - -.feed .post .textbox { - margin-right: 6px; -} - -.feed .post .textbox textarea { - margin: 0; - border: 1px solid #bbb; - border-top-color: #aeaeae; - padding: 2px; - width: 100%; -} - -.feed .post .buttons { - text-align: right; -} diff --git a/examples/newsfeed/static/favicon.ico b/examples/newsfeed/static/favicon.ico deleted file mode 100644 index dfcd70791c577589f7eeaf2a94fee0e136e9ec9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x$gfiO1%0|?Ustf@@=KO@qbC=61I z?go(FEiJhy_&*e&i_c4Ph3o(F?)85VCPzQIo6+sTWk1L+ko!UO#`+8}UYZ+>;`Z;K zKESm92eCmIWcr055!g#nm87)^}*AhpPT0GUOq{zJ1nP|P4zKTJJ3jm!Pd zZ(sdS492D(7LT}SkRBL@rC$&qmwI$@n4QS!9wbJLdqHY&=?94ugJEt)w*Nmc6+dA7 lu>S!g149E4GcfY>0SgjPp@D>Xk;H&H_(7O&HUmEc0{|53Pul - - - - {% block title %}Facebook Client Example{% endblock %} - - {% block head %}{% endblock %} - - -
This application is a demo of the Facebook Graph API, the core part of the Facebook Platform. See source code »
-
{% block body %}{% endblock %}
-
- - - diff --git a/examples/newsfeed/templates/home.html b/examples/newsfeed/templates/home.html deleted file mode 100644 index ce40b82c..00000000 --- a/examples/newsfeed/templates/home.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "base.html" %} - -{% block body %} - -
- -
-
-
-
-
-
-
-
-
- - {% for post in news_feed.data %} -
-
-
-
- {{ post.from.name|escape }} - {% if post.message %}{{ post.message|escape }}{% endif %} -
- {% if post.caption or post.picture %} -
- {% if post.picture %} -
- {% endif %} - {% if post.name %} - - {% endif %} - {% if post.caption %} -
{{ post.caption|escape }}
- {% endif %} - {% if post.description %} -
{{ post.description|escape }}
- {% endif %} -
- {% endif %} -
- {{ post.created_time|timesince }} ago -
-
-
- {% endfor %} - -
-{% endblock %} diff --git a/examples/newsfeed/templates/index.html b/examples/newsfeed/templates/index.html deleted file mode 100644 index 06be7d31..00000000 --- a/examples/newsfeed/templates/index.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "base.html" %} - -{% block body %} -

This application is a simple Facebook client. It shows you your News Feed and enables you to post status messages back to your profile. It is designed to demonstrate the use of the Facebook Graph API, the core part of the Facebook Platform. To get started, log in to Facebook below:

- -

You can download the source code to this application on GitHub.

-{% endblock %} diff --git a/examples/oauth/app.yaml b/examples/oauth/app.yaml deleted file mode 100644 index 7768a409..00000000 --- a/examples/oauth/app.yaml +++ /dev/null @@ -1,8 +0,0 @@ -application: facebook-example -version: 1 -runtime: python -api_version: 1 - -handlers: -- url: /.* - script: facebookoauth.py diff --git a/examples/oauth/facebookoauth.py b/examples/oauth/facebookoauth.py deleted file mode 100644 index 7cb08809..00000000 --- a/examples/oauth/facebookoauth.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2010 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A barebones AppEngine application that uses Facebook for login. - -This application uses OAuth 2.0 directly rather than relying on Facebook's -JavaScript SDK for login. It also accesses the Facebook Graph API directly -rather than using the Python SDK. It is designed to illustrate how easy -it is to use the Facebook Platform without any third party code. - -See the "appengine" directory for an example using the JavaScript SDK. -Using JavaScript is recommended if it is feasible for your application, -as it handles some complex authentication states that can only be detected -in client-side code. -""" -import base64 -import cgi -import Cookie -import email.utils -import hashlib -import hmac -import logging -import os.path -import time -import urllib -import wsgiref.handlers - -from django.utils import simplejson as json -from google.appengine.ext import db -from google.appengine.ext import webapp -from google.appengine.ext.webapp import util -from google.appengine.ext.webapp import template - - -FACEBOOK_APP_ID = "your app id" -FACEBOOK_APP_SECRET = "your app secret" - - -class User(db.Model): - id = db.StringProperty(required=True) - created = db.DateTimeProperty(auto_now_add=True) - updated = db.DateTimeProperty(auto_now=True) - name = db.StringProperty(required=True) - profile_url = db.StringProperty(required=True) - access_token = db.StringProperty(required=True) - - -class BaseHandler(webapp.RequestHandler): - @property - def current_user(self): - """Returns the logged in Facebook user, or None if unconnected.""" - if not hasattr(self, "_current_user"): - self._current_user = None - user_id = parse_cookie(self.request.cookies.get("fb_user")) - if user_id: - self._current_user = User.get_by_key_name(user_id) - return self._current_user - - -class HomeHandler(BaseHandler): - def get(self): - path = os.path.join(os.path.dirname(__file__), "oauth.html") - args = dict(current_user=self.current_user) - self.response.out.write(template.render(path, args)) - - -class LoginHandler(BaseHandler): - def get(self): - verification_code = self.request.get("code") - args = dict(client_id=FACEBOOK_APP_ID, - redirect_uri=self.request.path_url) - if verification_code: - args["client_secret"] = FACEBOOK_APP_SECRET - args["code"] = verification_code - response = cgi.parse_qs(urllib.urlopen( - "https://graph.facebook.com/oauth/access_token?" + - urllib.urlencode(args)).read()) - access_token = response["access_token"][-1] - - # Download the user profile and cache a local instance of the - # basic profile info - profile = json.load(urllib.urlopen( - "https://graph.facebook.com/me?" + - urllib.urlencode(dict(access_token=access_token)))) - user = User(key_name=str(profile["id"]), id=str(profile["id"]), - name=profile["name"], access_token=access_token, - profile_url=profile["link"]) - user.put() - set_cookie(self.response, "fb_user", str(profile["id"]), - expires=time.time() + 30 * 86400) - self.redirect("/") - else: - self.redirect( - "https://graph.facebook.com/oauth/authorize?" + - urllib.urlencode(args)) - - -class LogoutHandler(BaseHandler): - def get(self): - set_cookie(self.response, "fb_user", "", expires=time.time() - 86400) - self.redirect("/") - - -def set_cookie(response, name, value, domain=None, path="/", expires=None): - """Generates and signs a cookie for the give name/value""" - timestamp = str(int(time.time())) - value = base64.b64encode(value) - signature = cookie_signature(value, timestamp) - cookie = Cookie.BaseCookie() - cookie[name] = "|".join([value, timestamp, signature]) - cookie[name]["path"] = path - if domain: - cookie[name]["domain"] = domain - if expires: - cookie[name]["expires"] = email.utils.formatdate( - expires, localtime=False, usegmt=True) - response.headers._headers.append(("Set-Cookie", cookie.output()[12:])) - - -def parse_cookie(value): - """Parses and verifies a cookie value from set_cookie""" - if not value: - return None - parts = value.split("|") - if len(parts) != 3: - return None - if cookie_signature(parts[0], parts[1]) != parts[2]: - logging.warning("Invalid cookie signature %r", value) - return None - timestamp = int(parts[1]) - if timestamp < time.time() - 30 * 86400: - logging.warning("Expired cookie %r", value) - return None - try: - return base64.b64decode(parts[0]).strip() - except: - return None - - -def cookie_signature(*parts): - """Generates a cookie signature. - - We use the Facebook app secret since it is different for every app (so - people using this example don't accidentally all use the same secret). - """ - hash = hmac.new(FACEBOOK_APP_SECRET, digestmod=hashlib.sha1) - for part in parts: - hash.update(part) - return hash.hexdigest() - - -def main(): - util.run_wsgi_app(webapp.WSGIApplication([ - (r"/", HomeHandler), - (r"/auth/login", LoginHandler), - (r"/auth/logout", LogoutHandler), - ])) - - -if __name__ == "__main__": - main() diff --git a/examples/oauth/oauth.html b/examples/oauth/oauth.html deleted file mode 100644 index 0df44dc1..00000000 --- a/examples/oauth/oauth.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Facebook OAuth Example - - - {% if current_user %} -

-

You are logged in as {{ current_user.name|escape }}

-

Log out

- {% else %} -

You are not yet logged into this site

-

Log in with Facebook

- {% endif %} - - From f0d0d62a9ffe77eaac3942139a00c71a247fea6b Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 21 Jun 2015 11:29:38 -0400 Subject: [PATCH 109/269] Remove App Engine mention from dosctring. --- facebook/__init__.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 867ffd64..65c2e874 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -22,15 +22,6 @@ http://developers.facebook.com/docs/api. You can download the Facebook JavaScript SDK at http://github.com/facebook/connect-js/. -If your application is using Google AppEngine's webapp framework, your -usage of this module might look like this: - -user = facebook.get_user_from_cookie(self.request.cookies, key, secret) -if user: - graph = facebook.GraphAPI(user["access_token"]) - profile = graph.get_object("me") - friends = graph.get_connections("me", "friends") - """ import hashlib From 3e01906995beb65f9ee97d11db9334ed31e3579e Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 21 Jun 2015 11:47:42 -0400 Subject: [PATCH 110/269] Add documentation explaining integrating SDK. Add new documentation explaining how to use the SDK with Flask (use example), Google App Engine (enable billing), and Tornado (use example). This is stub documentation, so pull requests are welcome! --- docs/index.rst | 1 + docs/integration.rst | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 docs/integration.rst diff --git a/docs/index.rst b/docs/index.rst index 7aeaafff..1f339a05 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,6 +6,7 @@ Facebook SDK for Python :maxdepth: 2 install + integration support api diff --git a/docs/integration.rst b/docs/integration.rst new file mode 100644 index 00000000..14038774 --- /dev/null +++ b/docs/integration.rst @@ -0,0 +1,18 @@ +========================================= +Integrating the SDK with Other Frameworks +========================================= + +Flask +===== +The examples directory contains an example of using the SDK in Flask. + + +Google App Engine +================= + +Because the SDK uses `requests` (which requires socket support in Google App +Engine), you will need to enable billing. + +Tornado +======= +The examples directory contains an example of using the SDK in Tornado. From 7fe15f0945ceb39c49b68c55793bcb3f940bf591 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 21 Jun 2015 12:01:37 -0400 Subject: [PATCH 111/269] Update installation instructions. Update installation instructions so that people stop installing 0.4.0 and then complaining when documentation from the unreleased version from Git does not work. --- docs/install.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/install.rst b/docs/install.rst index c57b8e2a..304e15c1 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -8,6 +8,21 @@ package is required. We recommend using `pip`_ and `virtualenv`_ to install the SDK. Please note that the SDK's Python package is called **facebook-sdk**: :: +Installing from Git +=================== + +For the newest features, you should install the SDK directly from Git. + + virtualenv facebookenv + source facebookenv/bin/activate + pip install -e git+https://github.com/pythonforfacebook/facebook-sdk.git#egg=facebook-sdk + +Installing a Released Version +============================= + +If your application requires maximum stability, you will want to use a version +of the SDK that has been officially released. + virtualenv facebookenv source facebookenv/bin/activate pip install facebook-sdk From 4416eab093df4fc2604d8169e3e53f3490d7674d Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 12 Jul 2015 18:29:33 -0400 Subject: [PATCH 112/269] Add 2.4 to valid API versions. --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 65c2e874..5f366e5d 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -76,7 +76,7 @@ class GraphAPI(object): def __init__(self, access_token=None, timeout=None, version=None): # The default version is only used if the version kwarg does not exist. default_version = "2.0" - valid_API_versions = ["2.0", "2.1", "2.2", "2.3"] + valid_API_versions = ["2.0", "2.1", "2.2", "2.3", "2.4"] self.access_token = access_token self.timeout = timeout From c1fc12e0d681fa67351aa637c6dd3791bf8bd543 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 12 Jul 2015 18:38:28 -0400 Subject: [PATCH 113/269] Simplify version testing. Convert valid_API_versions to a module-level constant in order to simplify adding new versions by making version tests more dynamic. --- facebook/__init__.py | 8 +++++--- test/test_facebook.py | 19 ++++--------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 5f366e5d..f612123f 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -43,6 +43,9 @@ __version__ = version.__version__ +VALID_API_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4"] + + class GraphAPI(object): """A client for the Facebook Graph API. @@ -76,7 +79,6 @@ class GraphAPI(object): def __init__(self, access_token=None, timeout=None, version=None): # The default version is only used if the version kwarg does not exist. default_version = "2.0" - valid_API_versions = ["2.0", "2.1", "2.2", "2.3", "2.4"] self.access_token = access_token self.timeout = timeout @@ -85,9 +87,9 @@ def __init__(self, access_token=None, timeout=None, version=None): version_regex = re.compile("^\d\.\d$") match = version_regex.search(str(version)) if match is not None: - if str(version) not in valid_API_versions: + if str(version) not in VALID_API_VERSIONS: raise GraphAPIError("Valid API versions are " + - str(valid_API_versions).strip('[]')) + str(VALID_API_VERSIONS).strip('[]')) else: self.version = "v" + str(version) else: diff --git a/test/test_facebook.py b/test/test_facebook.py index e739fab6..af0a6ba4 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -58,21 +58,10 @@ def test_no_version(self): self.assertNotEqual( graph.version, "", "Version should not be an empty string.") - def test_version_2_0(self): - graph = facebook.GraphAPI(version=2.0) - self.assertEqual(graph.get_version(), 2.0) - - def test_version_2_1(self): - graph = facebook.GraphAPI(version=2.1) - self.assertEqual(graph.get_version(), 2.1) - - def test_version_2_2(self): - graph = facebook.GraphAPI(version=2.2) - self.assertEqual(graph.get_version(), 2.2) - - def test_version_2_3(self): - graph = facebook.GraphAPI(version=2.3) - self.assertEqual(graph.get_version(), 2.3) + def test_valid_versions(self): + for version in facebook.VALID_API_VERSIONS: + graph = facebook.GraphAPI(version=version) + self.assertEqual(str(graph.get_version()), version) def test_invalid_version(self): self.assertRaises(facebook.GraphAPIError, From 8bb1348b62f61e2e45688901041849df08c8a9fe Mon Sep 17 00:00:00 2001 From: "Ivan R. Judson" Date: Mon, 13 Jul 2015 07:06:57 -0700 Subject: [PATCH 114/269] Testing code block --- docs/install.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/install.rst b/docs/install.rst index 304e15c1..383a7774 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -13,6 +13,7 @@ Installing from Git For the newest features, you should install the SDK directly from Git. +.. code-block:: virtualenv facebookenv source facebookenv/bin/activate pip install -e git+https://github.com/pythonforfacebook/facebook-sdk.git#egg=facebook-sdk From 2f25c5bfad68d8751a0e4858a5624ed954e56727 Mon Sep 17 00:00:00 2001 From: "Ivan R. Judson" Date: Mon, 13 Jul 2015 07:07:52 -0700 Subject: [PATCH 115/269] Fixed code block rst. --- docs/install.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/install.rst b/docs/install.rst index 383a7774..09ab9178 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -14,6 +14,7 @@ Installing from Git For the newest features, you should install the SDK directly from Git. .. code-block:: + virtualenv facebookenv source facebookenv/bin/activate pip install -e git+https://github.com/pythonforfacebook/facebook-sdk.git#egg=facebook-sdk @@ -24,6 +25,8 @@ Installing a Released Version If your application requires maximum stability, you will want to use a version of the SDK that has been officially released. +.. code-block:: + virtualenv facebookenv source facebookenv/bin/activate pip install facebook-sdk From 1674e643a110382cceabcf5eb39e4ebfe1391219 Mon Sep 17 00:00:00 2001 From: Tomasz Netczuk Date: Fri, 28 Nov 2014 20:43:08 +0100 Subject: [PATCH 116/269] Added test cases Test cases are based on official Facebook PHP SDK tests: https://github.com/facebookarchive/facebook-php-sdk/blob/master/tests/tests.php Thank you for your hard work! --- test/test_facebook.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/test_facebook.py b/test/test_facebook.py index af0a6ba4..ec535436 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -49,6 +49,13 @@ def test_get_app_access_token(self): # the following line with flake8 (hence the noqa comment). assert(isinstance(token, str) or isinstance(token, unicode)) # noqa + def test_get_deleted_app_access_token(self): + deleted_app_id = '174236045938435' + deleted_secret = '0073dce2d95c4a5c2922d1827ea0cca6' + self.assertRaisesRegexp(facebook.GraphAPIError, + 'Error validating application. Application has been deleted.', + facebook.get_app_access_token, + deleted_app_id, deleted_secret) class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" @@ -134,5 +141,29 @@ def test_extend_access_token(self): e.message, "fb_exchange_token parameter not specified") +class TestGraphApi(FacebookTestCase): + def test_bogus_access_token(self): + graph = facebook.GraphAPI(access_token='wrong_token') + self.assertRaisesRegexp(facebook.GraphAPIError, 'Invalid OAuth access token.', graph.get_object, 'me') + + def test_access_with_expired_access_token(self): + expired_token = 'AAABrFmeaJjgBAIshbq5ZBqZBICsmveZCZBi6O4w9HSTkFI73VMtmkL9j' \ + 'LuWsZBZC9QMHvJFtSulZAqonZBRIByzGooCZC8DWr0t1M4BL9FARdQwPWPnIqCiFQ' + graph = facebook.GraphAPI(access_token=expired_token) + self.assertRaisesRegexp(facebook.GraphAPIError, + 'Error validating access token: The session was ' + 'invalidated explicitly using an API call.', + graph.get_object, 'me') + + def test_with_only_params(self): + graph = facebook.GraphAPI() + jerry = graph.get_object('jerry') + self.assertTrue(jerry['id'], 'User ID should be public.') + self.assertTrue(jerry['name'], 'User\'s name should be public.') + self.assertTrue(jerry['first_name'], 'User\'s first name should be public.') + self.assertTrue(jerry['last_name'], 'User\'s last name should be public.') + self.assertTrue(jerry['link'], 'User\'s link should be public.') + self.assertTrue(jerry['username'], 'User\'s username should be public.') + if __name__ == '__main__': unittest.main() From 0105667545337037884c0f985e03c0ae641c3a81 Mon Sep 17 00:00:00 2001 From: Tomasz Netczuk Date: Sat, 29 Nov 2014 21:20:27 +0100 Subject: [PATCH 117/269] Fixed assertRaisesRegexp test (especially for Python 2.6) --- test/test_facebook.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index ec535436..976fe7e4 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -16,6 +16,7 @@ import facebook import os import unittest +import sys try: from urllib.parse import parse_qs, urlencode, urlparse @@ -34,6 +35,19 @@ def setUp(self): raise Exception("FACEBOOK_APP_ID and FACEBOOK_SECRET " "must be set as environmental variables.") + def assert_raises_multi_regex(self, expected_exception, expected_regexp, callable_obj=None, *args, **kwargs): + if sys.version < '2.7': + # There is no assertRaisesRegexp method in Python 2.6 unittest, so there is need to check it manually + self.assertRaises(expected_exception, callable_obj, *args, **kwargs) + try: + callable_obj(*args) + except facebook.GraphAPIError as error: + pass + self.assertEqual(error.message, expected_regexp) + elif sys.version < '3': + return self.assertRaisesRegexp(expected_exception, expected_regexp, callable_obj, *args, **kwargs) + else: + return self.assertRaisesRegex(expected_exception, expected_regexp, callable_obj, *args, **kwargs) class TestGetAppAccessToken(FacebookTestCase): """ @@ -52,10 +66,10 @@ def test_get_app_access_token(self): def test_get_deleted_app_access_token(self): deleted_app_id = '174236045938435' deleted_secret = '0073dce2d95c4a5c2922d1827ea0cca6' - self.assertRaisesRegexp(facebook.GraphAPIError, - 'Error validating application. Application has been deleted.', - facebook.get_app_access_token, - deleted_app_id, deleted_secret) + self.assert_raises_multi_regex(facebook.GraphAPIError, + 'Error validating application. Application has been deleted.', + facebook.get_app_access_token, + deleted_app_id, deleted_secret) class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" @@ -144,16 +158,16 @@ def test_extend_access_token(self): class TestGraphApi(FacebookTestCase): def test_bogus_access_token(self): graph = facebook.GraphAPI(access_token='wrong_token') - self.assertRaisesRegexp(facebook.GraphAPIError, 'Invalid OAuth access token.', graph.get_object, 'me') + self.assert_raises_multi_regex(facebook.GraphAPIError, 'Invalid OAuth access token.', graph.get_object, 'me') def test_access_with_expired_access_token(self): expired_token = 'AAABrFmeaJjgBAIshbq5ZBqZBICsmveZCZBi6O4w9HSTkFI73VMtmkL9j' \ 'LuWsZBZC9QMHvJFtSulZAqonZBRIByzGooCZC8DWr0t1M4BL9FARdQwPWPnIqCiFQ' graph = facebook.GraphAPI(access_token=expired_token) - self.assertRaisesRegexp(facebook.GraphAPIError, - 'Error validating access token: The session was ' - 'invalidated explicitly using an API call.', - graph.get_object, 'me') + self.assert_raises_multi_regex(facebook.GraphAPIError, + 'Error validating access token: The session was ' + 'invalidated explicitly using an API call.', + graph.get_object, 'me') def test_with_only_params(self): graph = facebook.GraphAPI() From 785fb7e54805d04fbd4024952e6aa72bd602b76e Mon Sep 17 00:00:00 2001 From: Tomasz Netczuk Date: Sat, 29 Nov 2014 21:25:01 +0100 Subject: [PATCH 118/269] Final fix for Python 2.6 tests. --- test/test_facebook.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 976fe7e4..06d63208 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -42,7 +42,6 @@ def assert_raises_multi_regex(self, expected_exception, expected_regexp, callabl try: callable_obj(*args) except facebook.GraphAPIError as error: - pass self.assertEqual(error.message, expected_regexp) elif sys.version < '3': return self.assertRaisesRegexp(expected_exception, expected_regexp, callable_obj, *args, **kwargs) From a5ee174cc53053ecb9625166f719de704878f377 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 31 Aug 2015 00:42:21 -0400 Subject: [PATCH 119/269] Rewrite assert_raises_multi_regex helper method. Do not bother to try checking Python system version, just always use Python 2.6 workaround. --- test/test_facebook.py | 52 ++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 06d63208..2488d86f 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -35,18 +35,20 @@ def setUp(self): raise Exception("FACEBOOK_APP_ID and FACEBOOK_SECRET " "must be set as environmental variables.") - def assert_raises_multi_regex(self, expected_exception, expected_regexp, callable_obj=None, *args, **kwargs): - if sys.version < '2.7': - # There is no assertRaisesRegexp method in Python 2.6 unittest, so there is need to check it manually - self.assertRaises(expected_exception, callable_obj, *args, **kwargs) - try: - callable_obj(*args) - except facebook.GraphAPIError as error: - self.assertEqual(error.message, expected_regexp) - elif sys.version < '3': - return self.assertRaisesRegexp(expected_exception, expected_regexp, callable_obj, *args, **kwargs) - else: - return self.assertRaisesRegex(expected_exception, expected_regexp, callable_obj, *args, **kwargs) + def assert_raises_multi_regex( + self, expected_exception, expected_regexp, callable_obj=None, + *args, **kwargs): + """ + Custom function to backport assertRaisesRegexp to all supported + versions of Python. + + """ + self.assertRaises(expected_exception, callable_obj, *args, **kwargs) + try: + callable_obj(*args) + except facebook.GraphAPIError as error: + self.assertEqual(error.message, expected_regexp) + class TestGetAppAccessToken(FacebookTestCase): """ @@ -65,10 +67,15 @@ def test_get_app_access_token(self): def test_get_deleted_app_access_token(self): deleted_app_id = '174236045938435' deleted_secret = '0073dce2d95c4a5c2922d1827ea0cca6' - self.assert_raises_multi_regex(facebook.GraphAPIError, - 'Error validating application. Application has been deleted.', - facebook.get_app_access_token, - deleted_app_id, deleted_secret) + deleted_error_message = ( + "Error validating application. Application has been deleted.") + + self.assert_raises_multi_regex( + facebook.GraphAPIError, + deleted_error_message, + facebook.get_app_access_token, + deleted_app_id, + deleted_secret) class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" @@ -156,17 +163,20 @@ def test_extend_access_token(self): class TestGraphApi(FacebookTestCase): def test_bogus_access_token(self): + invalid_token_error_message = "Invalid OAuth access token." + graph = facebook.GraphAPI(access_token='wrong_token') - self.assert_raises_multi_regex(facebook.GraphAPIError, 'Invalid OAuth access token.', graph.get_object, 'me') + self.assert_raises_multi_regex( + facebook.GraphAPIError, + invalid_token_error_message, + graph.get_object, + "me") def test_access_with_expired_access_token(self): expired_token = 'AAABrFmeaJjgBAIshbq5ZBqZBICsmveZCZBi6O4w9HSTkFI73VMtmkL9j' \ 'LuWsZBZC9QMHvJFtSulZAqonZBRIByzGooCZC8DWr0t1M4BL9FARdQwPWPnIqCiFQ' graph = facebook.GraphAPI(access_token=expired_token) - self.assert_raises_multi_regex(facebook.GraphAPIError, - 'Error validating access token: The session was ' - 'invalidated explicitly using an API call.', - graph.get_object, 'me') + self.assertRaises(facebook.GraphAPIError, graph.get_object, 'me') def test_with_only_params(self): graph = facebook.GraphAPI() From 477d791065e87edb068399b60cffb3e5fd7a5eb0 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 31 Aug 2015 00:47:29 -0400 Subject: [PATCH 120/269] Remove broken test cases & reorganize tests. Remove "test_with_only_params" test that no longer worked (it's not possible to query Facebook users by their username). Move remaining tests into new AccessToken test case. --- test/test_facebook.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 2488d86f..07a0693b 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -146,22 +146,21 @@ def test_auth_url(self): self.assertEqual(actual_query, expected_query) -class TestExtendAccessToken(FacebookTestCase): - """ - Test if extend_access_token requests the correct endpoint. - - Note that this only tests whether extend_access_token returns the correct - error message when called without a proper user-access token. - """ +class TestAccessToken(FacebookTestCase): def test_extend_access_token(self): + """ + Test if extend_access_token requests the correct endpoint. + + Note that this only tests whether extend_access_token returns the + correct error message when called without a proper user-access token. + + """ 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") - -class TestGraphApi(FacebookTestCase): def test_bogus_access_token(self): invalid_token_error_message = "Invalid OAuth access token." @@ -178,15 +177,6 @@ def test_access_with_expired_access_token(self): graph = facebook.GraphAPI(access_token=expired_token) self.assertRaises(facebook.GraphAPIError, graph.get_object, 'me') - def test_with_only_params(self): - graph = facebook.GraphAPI() - jerry = graph.get_object('jerry') - self.assertTrue(jerry['id'], 'User ID should be public.') - self.assertTrue(jerry['name'], 'User\'s name should be public.') - self.assertTrue(jerry['first_name'], 'User\'s first name should be public.') - self.assertTrue(jerry['last_name'], 'User\'s last name should be public.') - self.assertTrue(jerry['link'], 'User\'s link should be public.') - self.assertTrue(jerry['username'], 'User\'s username should be public.') if __name__ == '__main__': unittest.main() From fca7fc6179556d0bb11450a556c55862deffe7bd Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Mon, 31 Aug 2015 00:50:19 -0400 Subject: [PATCH 121/269] Re-PEP8 test_facebook.py. --- test/test_facebook.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_facebook.py b/test/test_facebook.py index 07a0693b..47fa36e1 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -16,7 +16,6 @@ import facebook import os import unittest -import sys try: from urllib.parse import parse_qs, urlencode, urlparse @@ -77,6 +76,7 @@ def test_get_deleted_app_access_token(self): deleted_app_id, deleted_secret) + class TestAPIVersion(FacebookTestCase): """Test if using the correct version of Graph API.""" def test_no_version(self): @@ -172,8 +172,9 @@ def test_bogus_access_token(self): "me") def test_access_with_expired_access_token(self): - expired_token = 'AAABrFmeaJjgBAIshbq5ZBqZBICsmveZCZBi6O4w9HSTkFI73VMtmkL9j' \ - 'LuWsZBZC9QMHvJFtSulZAqonZBRIByzGooCZC8DWr0t1M4BL9FARdQwPWPnIqCiFQ' + expired_token = ( + "AAABrFmeaJjgBAIshbq5ZBqZBICsmveZCZBi6O4w9HSTkFI73VMtmkL9jLuWs" + "ZBZC9QMHvJFtSulZAqonZBRIByzGooCZC8DWr0t1M4BL9FARdQwPWPnIqCiFQ") graph = facebook.GraphAPI(access_token=expired_token) self.assertRaises(facebook.GraphAPIError, graph.get_object, 'me') From 1b240e4f979a57c9f0446d4906dfada7a9242cac Mon Sep 17 00:00:00 2001 From: Stian Prestholdt Date: Thu, 23 Apr 2015 13:55:50 +0200 Subject: [PATCH 122/269] Fix parsed_signed_request Python 3 compatibility. The data that `base64.urlsafe_b64decode` creats are returned as a byte-string in Python 3. We pass this on to `json.loads` which only allows strings, and not byte-strings. So this commit fixes this by decoding data so that it becomes a normal string in Python 3. --- facebook/__init__.py | 6 +++++- test/test_facebook.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index f612123f..3eb1b6fb 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -26,6 +26,7 @@ import hashlib import hmac +import binascii import base64 import requests import json @@ -400,8 +401,11 @@ def parse_signed_request(signed_request, app_secret): except TypeError: # Signed request had a corrupted payload. return False + except binascii.Error: + # Signed request had a corrupted payload. + return False - data = json.loads(data) + data = json.loads(data.decode('ascii')) if data.get('algorithm', '').upper() != 'HMAC-SHA256': return False diff --git a/test/test_facebook.py b/test/test_facebook.py index 47fa36e1..4dd3759d 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -179,5 +179,36 @@ def test_access_with_expired_access_token(self): self.assertRaises(facebook.GraphAPIError, graph.get_object, 'me') +class TestParseSignedRequest(FacebookTestCase): + cookie = ( + "O0vd27uj4j6RxdIyMH-VhMwQpUkPgg_9665I9yGDecE." + "eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImNvZGUiOiJBUURjSXQ2YnhZ" + "M090T3BSRGtpT1k4UDNlOWgwYzZRNFFuMEFFQnVqR1M3ZEV5LXNtbUt5b3pD" + "dHdhZy1kRmVYNmRUbi12dVBfQVNtek5RbjlkakloZHJIa0VBMHlLMm16T0Ji" + "RS1memVoNUh0Vk5UbnpQUDV3Z2VmUkF1bjhvTkQ4S3I3aUd2a3A4Q2EzODJL" + "NWtqcVl1Z19QV1NUREhqMlY3T2NWaE1GQ2wyWkN2MFk5NnlLUDhfSVAtbnNL" + "b09kcFVLSU5LMks1SGgxUjZfMkdmMUs1OG5uSnd1bENuSVVRSlhSSU83VEd3" + "WFJWOVlfa1hzS0pmREpUVzNnTWJ1UGNGc3p0Vkx3MHpyV04yQXE3YWVLVFI2" + "MFNyeVgzMlBWZkhxNjlzYnUwcnJWLUZMZ2NvMUpBVWlYRlNaY2Q5cVF6WSIs" + "Imlzc3VlZF9hdCI6MTQ0MTUxNTY1OCwidXNlcl9pZCI6IjEwMTAxNDk2NTUz" + "NDg2NjExIn0") + + def test_parse_signed_request_when_erroneous(self): + result = facebook.parse_signed_request( + signed_request='corrupted.payload', + app_secret=self.secret) + self.assertFalse(result) + + def test_parse_signed_request_when_correct(self): + result = facebook.parse_signed_request( + signed_request=self.cookie, + app_secret=self.secret) + + self.assertTrue('issued_at' in result) + self.assertTrue('code' in result) + self.assertTrue('user_id' in result) + self.assertTrue('algorithm' in result) + + if __name__ == '__main__': unittest.main() From 15c454661cea66f56f6344ec2cbe7138354b3552 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 6 Sep 2015 01:12:04 -0400 Subject: [PATCH 123/269] Add additional assertion in signed request test. Verify that parse_signed_request returns True with valid payload. This avoids the test failing with a confusing TypeError if the cookie is invalid for some reason. --- test/test_facebook.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_facebook.py b/test/test_facebook.py index 4dd3759d..f66e41c8 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -204,6 +204,7 @@ def test_parse_signed_request_when_correct(self): signed_request=self.cookie, app_secret=self.secret) + self.assertTrue(result) self.assertTrue('issued_at' in result) self.assertTrue('code' in result) self.assertTrue('user_id' in result) From 55c0329b5f9ee8e3f8e916f1754633d8b254c18c Mon Sep 17 00:00:00 2001 From: Ryan Broyles Date: Tue, 3 Jun 2014 23:49:53 -0400 Subject: [PATCH 124/269] Allow passing integers to get_connections. Resolve #150. --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 3eb1b6fb..4d316d63 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -115,7 +115,7 @@ def get_objects(self, ids, **args): def get_connections(self, id, connection_name, **args): """Fetchs the connections for given object.""" return self.request( - self.version + "/" + id + "/" + connection_name, args) + "%s/%s/%s" % (self.version, id, connection_name), args) def put_object(self, parent_object, connection_name, **data): """Writes the given object to the graph, connected to the given parent. From adf6714b3cfc1bb9426f6148cfc083df3c0da88f Mon Sep 17 00:00:00 2001 From: muffl0n Date: Fri, 18 Sep 2015 14:21:50 +0200 Subject: [PATCH 125/269] add proxies parameter --- facebook/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 4d316d63..57ce503f 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -77,12 +77,14 @@ class GraphAPI(object): """ - def __init__(self, access_token=None, timeout=None, version=None): + def __init__(self, access_token=None, timeout=None, version=None, + proxies=None): # The default version is only used if the version kwarg does not exist. default_version = "2.0" self.access_token = access_token self.timeout = timeout + self.proxies = proxies if version: version_regex = re.compile("^\d\.\d$") @@ -243,6 +245,7 @@ def request( timeout=self.timeout, params=args, data=post_args, + proxies=self.proxies, files=files) except requests.HTTPError as e: response = json.loads(e.read()) From a66429639501c8db5af21f8b5324ba617aa8b277 Mon Sep 17 00:00:00 2001 From: muffl0n Date: Sat, 19 Sep 2015 16:05:31 +0200 Subject: [PATCH 126/269] use proxies in get_version also --- facebook/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 57ce503f..3a8f18b0 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -206,7 +206,8 @@ def get_version(self): "https://graph.facebook.com/" + self.version + "/me", params=args, - timeout=self.timeout) + timeout=self.timeout, + proxies=self.proxies) except requests.HTTPError as e: response = json.loads(e.read()) raise GraphAPIError(response) From c15cc56967b01ba82d693929b7d467ee4f4d769b Mon Sep 17 00:00:00 2001 From: muffl0n Date: Sat, 19 Sep 2015 16:06:14 +0200 Subject: [PATCH 127/269] add documentation for proxies parameter --- docs/api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 7db4ba45..770c31eb 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -28,10 +28,12 @@ You can read more about `Facebook's Graph API here`_. use`_. Valid API versions are ``2.0``, ``2.1`` and ``2.2``. The default version is ``2.0`` and is used if the version keyword argument is not provided. +* ``proxies`` - A ``dict`` with proxy-settings that Requests should use. `See Requests documentation`_. .. _Read more about access tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens .. _See more here: http://docs.python-requests.org/en/latest/user/quickstart/#timeouts .. _version of Facebook's Graph API to use: https://developers.facebook.com/docs/apps/versions +.. _See Requests documentation: http://www.python-requests.org/en/latest/user/advanced/#proxies **Example** From 0f31fe22686c6f0f8da22311815e130236de5566 Mon Sep 17 00:00:00 2001 From: tuxos Date: Tue, 22 Sep 2015 13:17:35 +0200 Subject: [PATCH 128/269] Fix Getting error type --- facebook/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/facebook/__init__.py b/facebook/__init__.py index 4d316d63..bce82135 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -335,6 +335,8 @@ def __init__(self, result): # OAuth 2.0 Draft 00 try: self.message = result["error"]["message"] + if not self.type: + self.type = result["error"].get('type', '') except: # REST server style try: From e4138ae179b06b20ba15fbb90982e30669f326d1 Mon Sep 17 00:00:00 2001 From: kz26 Date: Sun, 1 Mar 2015 11:11:49 -0500 Subject: [PATCH 129/269] Implement debug_access_token() https://developers.facebook.com/docs/facebook-login/access-tokens#debug --- facebook/__init__.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/facebook/__init__.py b/facebook/__init__.py index 4d316d63..bb64a7bf 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -319,6 +319,23 @@ def extend_access_token(self, app_id, app_secret): return self.request("oauth/access_token", args=args) + def debug_access_token(self, token, app_id, app_secret): + """ + Gets information about a user access token issued by an app. See + + + We can generate the app access token by concatenating the app + id and secret: + + """ + args = { + "input_token": token, + "access_token": "%s|%s" % (app_id, app_secret) + } + return self.request("/debug_token", args=args) + class GraphAPIError(Exception): def __init__(self, result): @@ -439,3 +456,7 @@ def get_access_token_from_code(code, redirect_uri, app_id, app_secret): def get_app_access_token(app_id, app_secret): return GraphAPI().get_app_access_token(app_id, app_secret) + + +def debug_access_token(token, app_id, app_secret): + return GraphAPI().debug_access_token(token, app_id, app_secret) From 12fcfc3c99ecb07345e937612e5e81505f4b0a34 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 26 Sep 2015 10:57:12 -0400 Subject: [PATCH 130/269] Remove deprecated standalone methods. --- facebook/__init__.py | 17 ++--------------- test/test_facebook.py | 11 +++++++---- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index bb64a7bf..4d6ac356 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -387,8 +387,8 @@ def get_user_from_cookie(cookies, app_id, app_secret): if not parsed_request: return None try: - result = get_access_token_from_code(parsed_request["code"], "", - app_id, app_secret) + result = GraphAPI().get_access_token_from_code( + parsed_request["code"], "", app_id, app_secret) except GraphAPIError: return None result["uid"] = parsed_request["user_id"] @@ -447,16 +447,3 @@ def auth_url(app_id, canvas_url, perms=None, **kwargs): kvps['scope'] = ",".join(perms) kvps.update(kwargs) return url + urlencode(kvps) - - -def get_access_token_from_code(code, redirect_uri, app_id, app_secret): - return GraphAPI().get_access_token_from_code( - code, redirect_uri, app_id, app_secret) - - -def get_app_access_token(app_id, app_secret): - return GraphAPI().get_app_access_token(app_id, app_secret) - - -def debug_access_token(token, app_id, app_secret): - return GraphAPI().debug_access_token(token, app_id, app_secret) diff --git a/test/test_facebook.py b/test/test_facebook.py index f66e41c8..5ec10a5c 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -58,7 +58,8 @@ class TestGetAppAccessToken(FacebookTestCase): """ def test_get_app_access_token(self): - token = facebook.get_app_access_token(self.app_id, self.secret) + token = facebook.GraphAPI().get_app_access_token( + self.app_id, self.secret) # Since "unicode" does not exist in Python 3, we cannot check # the following line with flake8 (hence the noqa comment). assert(isinstance(token, str) or isinstance(token, unicode)) # noqa @@ -72,7 +73,7 @@ def test_get_deleted_app_access_token(self): self.assert_raises_multi_regex( facebook.GraphAPIError, deleted_error_message, - facebook.get_app_access_token, + facebook.GraphAPI().get_app_access_token, deleted_app_id, deleted_secret) @@ -107,8 +108,10 @@ def test_invalid_format(self): class TestFQL(FacebookTestCase): def test_fql(self): - graph = facebook.GraphAPI(access_token=facebook.get_app_access_token( - self.app_id, self.secret), version=2.0) + graph = facebook.GraphAPI(version=2.0) + graph.access_token = graph.get_app_access_token( + self.app_id, self.secret) + # Ensure that version is below 2.1. Facebook has stated that FQL is # not present in this or future versions of the Graph API. if graph.get_version() < 2.1: From e58a57ed18c03910332c9e009aac0a5e61fb2602 Mon Sep 17 00:00:00 2001 From: tuxos Date: Mon, 28 Sep 2015 15:43:15 +0200 Subject: [PATCH 131/269] Add Exception class field for error code if exists --- facebook/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 5c155e9e..3fcd13ad 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -327,6 +327,7 @@ def extend_access_token(self, app_id, app_secret): class GraphAPIError(Exception): def __init__(self, result): self.result = result + self.code = None try: self.type = result["error_code"] except: @@ -339,8 +340,9 @@ def __init__(self, result): # OAuth 2.0 Draft 00 try: self.message = result["error"]["message"] + self.code = result["error"].get("code") if not self.type: - self.type = result["error"].get('type', '') + self.type = result["error"].get("type", "") except: # REST server style try: From 2fcff2025de6946c765f0142fd75e477d57636b3 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Oct 2015 14:01:59 -0400 Subject: [PATCH 132/269] Add version 2.5 to valid API versions. See https://developers.facebook.com/blog/post/2015/10/07/graph-api-v2.5/. --- facebook/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 307b0431..e1e07857 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -44,7 +44,7 @@ __version__ = version.__version__ -VALID_API_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4"] +VALID_API_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5"] class GraphAPI(object): From e2afe1f2ddc387e893bc2e166ff8a6d48dcac4bc Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Oct 2015 14:05:26 -0400 Subject: [PATCH 133/269] Add additional Python classifiers in setup.py. Fix #244. Add classifiers for generic Python 2 and 3 support, as well as a specific classifier for 3.4. --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 1b444d0f..cff45dbf 100644 --- a/setup.py +++ b/setup.py @@ -18,9 +18,12 @@ long_description=open("README.rst").read(), classifiers=[ 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', ], install_requires=[ 'requests', From 6885d9bfe83055f0a27a0634652cb10b2bc680a2 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Oct 2015 14:07:38 -0400 Subject: [PATCH 134/269] Add Python 3.5 support. Add to setup.py classifiers and Travis-CI configuration file. --- .travis.yml | 1 + setup.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f702950a..ff707f0b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: python python: + - "3.5" - "3.4" - "3.3" - "2.7" diff --git a/setup.py b/setup.py index cff45dbf..9de3ceb5 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5' ], install_requires=[ 'requests', From 939813df5df500688d43234621a74d4a6fd7233d Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 11 Oct 2015 14:11:30 -0400 Subject: [PATCH 135/269] Change Travis CI configuration file indentation. Let's use 4 spaces because it's already standard in all of our other files. > If you confused about indentation, I feel bad for you, son, > I got 99 files, and I use 2 spaces in none. --- .travis.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff707f0b..ee2c8bdd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,19 @@ language: python python: - - "3.5" - - "3.4" - - "3.3" - - "2.7" - - "2.6" + - "3.5" + - "3.4" + - "3.3" + - "2.7" + - "2.6" install: - - "pip install flake8" - - "pip install ." + - "pip install flake8" + - "pip install ." script: - - "python -m test.test_facebook" - - "flake8 --ignore=E402,F401 examples" - - "flake8 facebook" - - "flake8 test" + - "python -m test.test_facebook" + - "flake8 --ignore=E402,F401 examples" + - "flake8 facebook" + - "flake8 test" env: - global: - - FACEBOOK_APP_ID=198798870326423 - - FACEBOOK_SECRET=2db4d76fe8a336cf292470c20a5a5684 + global: + - FACEBOOK_APP_ID=198798870326423 + - FACEBOOK_SECRET=2db4d76fe8a336cf292470c20a5a5684 From 380466c8e3cb804022a88b1de6fbbba1622c81a2 Mon Sep 17 00:00:00 2001 From: Kanat Bekt Date: Tue, 1 Dec 2015 17:34:34 -0600 Subject: [PATCH 136/269] Add 3.5 support in docs --- docs/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index 09ab9178..edbded5a 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -2,7 +2,7 @@ Installation ============ -The SDK currently supports Python 2.6, 2.7, 3.3, and 3.4. The `requests`_ +The SDK currently supports Python 2.6, 2.7, 3.3, 3.4, and 3.5. The `requests`_ package is required. We recommend using `pip`_ and `virtualenv`_ to install the SDK. Please note From dffacd6723619374e463d8acfae2c3f0fea71d01 Mon Sep 17 00:00:00 2001 From: Liang Dong Date: Mon, 21 Dec 2015 12:12:28 +0800 Subject: [PATCH 137/269] fix log out button --- examples/flask/app/templates/base.html | 5 ----- examples/flask/app/templates/index.html | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/flask/app/templates/base.html b/examples/flask/app/templates/base.html index 14414857..e7047ab7 100644 --- a/examples/flask/app/templates/base.html +++ b/examples/flask/app/templates/base.html @@ -52,10 +52,5 @@ {% block content %} {% endblock %}
- diff --git a/examples/flask/app/templates/index.html b/examples/flask/app/templates/index.html index 1c4dc8f1..bdf46ba3 100644 --- a/examples/flask/app/templates/index.html +++ b/examples/flask/app/templates/index.html @@ -7,5 +7,11 @@

Hello, {{ user['name'] }}.

Log out + +
{% endblock %} From fe4d83d793f7a2c7878f93e927187f26788370c3 Mon Sep 17 00:00:00 2001 From: Liang Dong Date: Mon, 21 Dec 2015 15:39:18 +0800 Subject: [PATCH 138/269] fix profile link --- examples/flask/app/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/flask/app/views.py b/examples/flask/app/views.py index 5da73dd0..529059ea 100644 --- a/examples/flask/app/views.py +++ b/examples/flask/app/views.py @@ -65,6 +65,8 @@ def get_current_user(): # Not an existing user so get info graph = GraphAPI(result['access_token']) profile = graph.get_object('me') + if 'link' not in profile: + profile['link'] = "" # Create the user and insert it into the database user = User(id=str(profile['id']), name=profile['name'], From d93a303fc76b0b4dfd2bf1d4dcbb58362562235b Mon Sep 17 00:00:00 2001 From: Taranjeet Date: Sat, 26 Dec 2015 16:09:07 +0530 Subject: [PATCH 139/269] fix minor typo fixes in docs as well as docstrings --- docs/api.rst | 2 +- facebook/__init__.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 770c31eb..06321731 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -22,7 +22,7 @@ You can read more about `Facebook's Graph API here`_. * ``access_token`` – A ``string`` that identifies a user, app, or page and can be used by the app to make graph API calls. `Read more about access tokens here`_. -* ``timeout`` - A ``float`` describing (in seconds) how long the client will +* ``timeout`` - A ``float`` describing (in seconds) how long the client will be waiting for a response from Facebook's servers. `See more here`_. * ``version`` - A ``string`` describing the `version of Facebook's Graph API to use`_. Valid API versions are ``2.0``, ``2.1`` and ``2.2``. The diff --git a/facebook/__init__.py b/facebook/__init__.py index e8402ce3..cfadcbbb 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -102,11 +102,11 @@ def __init__(self, access_token=None, timeout=None, version=None, self.version = "v" + default_version def get_object(self, id, **args): - """Fetchs the given object from the graph.""" + """Fetches the given object from the graph.""" return self.request(self.version + "/" + id, args) def get_objects(self, ids, **args): - """Fetchs all of the given object from the graph. + """Fetches all of the given object from the graph. We return a map from ID to object. If any of the IDs are invalid, we raise an exception. @@ -115,7 +115,7 @@ def get_objects(self, ids, **args): return self.request(self.version + "/", args) def get_connections(self, id, connection_name, **args): - """Fetchs the connections for given object.""" + """Fetches the connections for given object.""" return self.request( "%s/%s/%s" % (self.version, id, connection_name), args) @@ -127,7 +127,7 @@ def put_object(self, parent_object, connection_name, **data): graph.put_object("me", "feed", message="Hello, world") writes "Hello, world" to the active user's wall. Likewise, this - will comment on a the first post of the active user's feed: + will comment on the first post of the active user's feed: feed = graph.get_connections("me", "feed") post = feed["data"][0] From 613784995e635443e0ab5577fc621e92f9d48e64 Mon Sep 17 00:00:00 2001 From: dipanshunagar Date: Wed, 30 Dec 2015 21:44:15 +0530 Subject: [PATCH 140/269] Corrected syntax error on sample code Missing comma on line 158 --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 770c31eb..bc41b187 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -155,7 +155,7 @@ authenticated user's wall if no ``profile_id`` is specified. .. code-block:: python attachment = { - 'name': '' + 'name': '', 'link': '', 'caption': '', 'description': '', From ebf77b3e1f58ad9d30d038b504991749758c89d9 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 13 Feb 2016 14:50:16 -0500 Subject: [PATCH 141/269] Define URLs as constants. Thanks to @crohling, who submitted a pull request that included these changes that I never responded to. Close #79. --- facebook/__init__.py | 20 ++++++++++---------- test/test_facebook.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index cfadcbbb..1292a29a 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -43,7 +43,8 @@ __version__ = version.__version__ - +FACEBOOK_GRAPH_URL = "https://graph.facebook.com/" +FACEBOOK_OAUTH_DIALOG_URL = "https://www.facebook.com/dialog/oauth?" VALID_API_VERSIONS = ["2.0", "2.1", "2.2", "2.3", "2.4", "2.5"] @@ -202,12 +203,12 @@ def get_version(self): """Fetches the current version number of the Graph API being used.""" args = {"access_token": self.access_token} try: - response = requests.request("GET", - "https://graph.facebook.com/" + - self.version + "/me", - params=args, - timeout=self.timeout, - proxies=self.proxies) + response = requests.request( + "GET", + FACEBOOK_GRAPH_URL + self.version + "/me", + params=args, + timeout=self.timeout, + proxies=self.proxies) except requests.HTTPError as e: response = json.loads(e.read()) raise GraphAPIError(response) @@ -241,8 +242,7 @@ def request( try: response = requests.request(method or "GET", - "https://graph.facebook.com/" + - path, + FACEBOOK_GRAPH_URL + path, timeout=self.timeout, params=args, data=post_args, @@ -449,7 +449,7 @@ def parse_signed_request(signed_request, app_secret): def auth_url(app_id, canvas_url, perms=None, **kwargs): - url = "https://www.facebook.com/dialog/oauth?" + url = FACEBOOK_OAUTH_DIALOG_URL kvps = {'client_id': app_id, 'redirect_uri': canvas_url} if perms: kvps['scope'] = ",".join(perms) diff --git a/test/test_facebook.py b/test/test_facebook.py index 5ec10a5c..cfabaf20 100644 --- a/test/test_facebook.py +++ b/test/test_facebook.py @@ -128,7 +128,7 @@ def test_auth_url(self): perms = ['email', 'birthday'] redirect_url = 'https://localhost/facebook/callback/' - expected_url = 'https://www.facebook.com/dialog/oauth?' + urlencode( + expected_url = facebook.FACEBOOK_OAUTH_DIALOG_URL + urlencode( dict(client_id=self.app_id, redirect_uri=redirect_url, scope=','.join(perms))) From f415fc32e25d506baaa65b59849b0ce99ce53126 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 27 Feb 2016 22:43:47 -0500 Subject: [PATCH 142/269] Remove link to deprecated JS SDK repository. Facebook stopped updating the repository for the JavaScript SDK a while ago, so having a link to it in the docstring is not useful. --- facebook/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/facebook/__init__.py b/facebook/__init__.py index 1292a29a..852ce864 100755 --- a/facebook/__init__.py +++ b/facebook/__init__.py @@ -19,8 +19,7 @@ This client library is designed to support the Graph API and the official Facebook JavaScript SDK, which is the canonical way to implement Facebook authentication. Read more about the Graph API at -http://developers.facebook.com/docs/api. You can download the Facebook -JavaScript SDK at http://github.com/facebook/connect-js/. +http://developers.facebook.com/docs/api. """ From 75441fd2dbab7afe83235cb50380d1c6a988f6a4 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 27 Feb 2016 22:47:15 -0500 Subject: [PATCH 143/269] Use file modes in image upload examples. Fix #257. Use file modes in the `open` call in the image upload examples so that they work properly on operating systems like Windows. --- docs/api.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index eb3ad939..f96aaba9 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -233,11 +233,11 @@ photo and its post. .. code-block:: python # Upload an image with a caption. - graph.put_photo(image=open('img.jpg'), message='Look at this cool photo!') + graph.put_photo(image=open('img.jpg', 'rb'), message='Look at this cool photo!') # Upload a photo to an album. - graph.put_photo(image=open("img.jpg"), album_path=album_id + "/photos") + graph.put_photo(image=open("img.jpg", 'rb'), album_path=album_id + "/photos") # Upload a profile photo for a Page. - graph.put_photo(image=open("img.jpg"), album_path=page_id + "/picture") + graph.put_photo(image=open("img.jpg", 'rb'), album_path=page_id + "/picture") delete_object ^^^^^^^^^^^^^ From 161ef6ebd9f8296517226bc763eafe573bce38ad Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Fri, 25 Mar 2016 12:52:07 -0400 Subject: [PATCH 144/269] Add language to code blocks in installation docs. Add a language to the code blocks in the installation documentation, in hopes that this will make them display on Read the Docs. --- docs/install.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index edbded5a..2c279206 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -13,7 +13,7 @@ Installing from Git For the newest features, you should install the SDK directly from Git. -.. code-block:: +.. code-block:: shell virtualenv facebookenv source facebookenv/bin/activate @@ -25,7 +25,7 @@ Installing a Released Version If your application requires maximum stability, you will want to use a version of the SDK that has been officially released. -.. code-block:: +.. code-block:: shell virtualenv facebookenv source facebookenv/bin/activate From eed322f0ddd8987ad3d0896d51bb7fe4d098faf5 Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sat, 26 Mar 2016 06:24:14 -0400 Subject: [PATCH 145/269] Add changelog. Add changelog for all versions past 0.3.0. --- docs/changes.rst | 34 ++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 2 files changed, 35 insertions(+) create mode 100644 docs/changes.rst diff --git a/docs/changes.rst b/docs/changes.rst new file mode 100644 index 00000000..69e7b209 --- /dev/null +++ b/docs/changes.rst @@ -0,0 +1,34 @@ +========= +Changelog +========= + +Version 1.0.0 +============= + + - Python 3 support. + - More comprehensive test coverage. + - Full Unicode support. + - Better exception handling. + - Vastly improved documentation. + +Version 0.4.0 (2012-10-15) +========================== + + - Add support for deleting application requests. + - Fix minor documentation error in README. + - Verify signed request parsing succeeded when creating OAuth token. + - Convert README to ReStructuredText. + +Version 0.3.2 (2012-07-28) +========================== + + - Add support for state parameters in auth dialog URLs. + - Fixes bug with Unicode app secrets. + - Add optional timeout support for faster API requests. + - Random PEP8 compliance fixes. + +Version 0.3.1 (2012-05-16) +========================== + + - Minor documentation updates. + - Removes the develop branch in favor of named feature branches. diff --git a/docs/index.rst b/docs/index.rst index 1f339a05..61da73f5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,7 @@ Facebook SDK for Python integration support api + changes This client library is designed to support the `Facebook Graph API`_ and the official `Facebook JavaScript SDK`_, which is the canonical way to implement From 99d4ff36aff7c06172233d3a7e40f793c21b8bdc Mon Sep 17 00:00:00 2001 From: Martey Dodoo Date: Sun, 27 Mar 2016 23:32:21 -0400 Subject: [PATCH 146/269] Remove references to "Python for Facebook". Update repository to new location. Replace references to "Python for Facebook SDK" with "Python Facebook SDK". --- README.rst | 2 +- docs/conf.py | 8 ++++---- docs/install.rst | 2 +- docs/support.rst | 3 +-- examples/flask/app/templates/base.html | 2 +- examples/flask/app/templates/login.html | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 95c99675..b0c6d2ed 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ If you have bugs or other issues specifically pertaining to this library, file them `here`_. Bugs with the Graph API should be filed on `Facebook's bugtracker`_. -.. _here: https://github.com/pythonforfacebook/facebook-sdk/issues +.. _here: https://github.com/mobolic/facebook-sdk/issues .. _Facebook's bugtracker: https://developers.facebook.com/bugs/ diff --git a/docs/conf.py b/docs/conf.py index 08231d9e..b64b6020 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,7 +43,7 @@ # General information about the project. project = u'Facebook SDK for Python' -copyright = u'2010 Facebook, 2010-2014 Python for Facebook developers' +copyright = u'2010 Facebook, 2016 Mobolic LLC' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -186,7 +186,7 @@ # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'FacebookSDKforPython.tex', u'Facebook SDK for Python Documentation', - u'Facebook, Python for Facebook developers', 'manual'), + u'Martey Dodoo', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -216,7 +216,7 @@ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'facebooksdkforpython', u'Facebook SDK for Python Documentation', - [u'Facebook, Python for Facebook developers'], 1) + [u'Martey Dodoo'], 1) ] # If true, show URL addresses after external links. @@ -230,7 +230,7 @@ # dir menu entry, description, category) texinfo_documents = [ ('index', 'FacebookSDKforPython', u'Facebook SDK for Python Documentation', - u'Facebook, Python for Facebook developers', 'FacebookSDKforPython', 'One line description of project.', + u'Martey Dodoo', 'FacebookSDKforPython', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/install.rst b/docs/install.rst index 2c279206..a0605bcd 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -17,7 +17,7 @@ For the newest features, you should install the SDK directly from Git. virtualenv facebookenv source facebookenv/bin/activate - pip install -e git+https://github.com/pythonforfacebook/facebook-sdk.git#egg=facebook-sdk + pip install -e git+https://github.com/mobolic/facebook-sdk.git#egg=facebook-sdk Installing a Released Version ============================= diff --git a/docs/support.rst b/docs/support.rst index 6bc19080..916852e3 100644 --- a/docs/support.rst +++ b/docs/support.rst @@ -15,7 +15,7 @@ Reporting Bugs Bugs with the SDK should be reported on the `issue tracker at Github`_. Bugs with Facebook's Graph API should be reported on `Facebook's bugtracker`_. -.. _issue tracker at Github: https://github.com/pythonforfacebook/facebook-sdk/issues +.. _issue tracker at Github: https://github.com/mobolic/facebook-sdk/issues .. _Facebook's bugtracker: https://developers.facebook.com/x/bugs/ Security Issues @@ -54,4 +54,3 @@ Update Tests and Documentation All non-trivial changes should include full test coverage. Please review the package's documentation to ensure that it is up to date with any changes. - diff --git a/examples/flask/app/templates/base.html b/examples/flask/app/templates/base.html index e7047ab7..0d3f07cd 100644 --- a/examples/flask/app/templates/base.html +++ b/examples/flask/app/templates/base.html @@ -4,7 +4,7 @@ - + diff --git a/examples/flask/app/templates/login.html b/examples/flask/app/templates/login.html index ad172929..a4269dfc 100644 --- a/examples/flask/app/templates/login.html +++ b/examples/flask/app/templates/login.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %}
-

Python for Facebook SDK

+

Python Facebook SDK

Flask example