From 3daa0bab9e8efed0b58dbd2099f47d86501b2374 Mon Sep 17 00:00:00 2001 From: holm Date: Thu, 22 Mar 2012 14:49:55 +0100 Subject: [PATCH 01/22] Allow passing in list of fields to get. Also change to use urllib2. --- pyfb/client.py | 26 +++++++++++++++++--------- pyfb/pyfb.py | 12 ++++++------ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 8495911..148ea1e 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -2,6 +2,7 @@ The implementation of the Facebook Client """ +import urllib2 import urllib import auth from urlparse import parse_qsl @@ -42,9 +43,9 @@ def _make_request(self, url, **data): """ if not data: data = None - return urllib.urlopen(url, data).read() + return urllib2.urlopen(url, data).read() - def _make_auth_request(self, path, **data): + def _make_auth_request(self, path, params=None, **data): """ Makes a request to the facebook Graph API. This method requires authentication! @@ -53,13 +54,20 @@ def _make_auth_request(self, path, **data): if self.access_token is None: raise PyfbException("Must Be authenticated. Do you forgot to get the access token?") - token_url = "?access_token=%s" % self.access_token - url = "%s%s%s" % (self.GRAPH_URL, path, token_url) + if params is None: + params = {} + else: + for key, value in params.items(): + if value is None: + del params[key] + params["access_token"] = self.access_token + + url = "%s%s?%s" % (self.GRAPH_URL, path, urllib.urlencode(params)) if data: post_data = urllib.urlencode(data) else: post_data = None - return urllib.urlopen(url, post_data).read() + return urllib2.urlopen(url, post_data).read() def _make_object(self, name, data): """ @@ -148,11 +156,11 @@ def get_dialog_url(self, redirect_uri): url = "%s%s" % (self.DIALOG_BASE_URL, url_path) return url - def get_one(self, path, object_name): + def get_one(self, path, object_name, **params): """ Gets one object """ - data = self._make_auth_request(path) + data = self._make_auth_request(path, params) obj = self._make_object(object_name, data) if hasattr(obj, 'error'): @@ -160,7 +168,7 @@ def get_one(self, path, object_name): return obj - def get_list(self, id, path, object_name=None): + def get_list(self, id, path, object_name=None, **params): """ Gets A list of objects """ @@ -169,7 +177,7 @@ def get_list(self, id, path, object_name=None): if object_name is None: object_name = path path = "%s/%s" % (id, path.lower()) - return self.get_one(path, object_name).__dict__[object_name] + return self.get_one(path, object_name, **params).__dict__[object_name] def push(self, id, path, **data): """ diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index c1c273d..44d52a5 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -80,25 +80,25 @@ def set_permissions(self, permissions): """ self._client.permissions = permissions - def get_myself(self): + def get_myself(self, fields=None): """ Gets myself data """ - return self._client.get_one("me", "FBUser") + return self._client.get_one("me", "FBUser", fields=fields) - def get_user_by_id(self, id=None): + def get_user_by_id(self, id=None, fields=None): """ Gets an user by the id """ if id is None: id = "me" - return self._client.get_one(id, "FBUser") + return self._client.get_one(id, "FBUser", fields=fields) - def get_friends(self, id=None): + def get_friends(self, id=None, fields=None): """ Gets a list with your friends """ - return self._client.get_list(id, "Friends") + return self._client.get_list(id, "Friends", fields=fields) def get_statuses(self, id=None): """ From 5f4f591aef3c9419657ec1f01765aacc4e18ea7c Mon Sep 17 00:00:00 2001 From: holm Date: Thu, 22 Mar 2012 14:50:49 +0100 Subject: [PATCH 02/22] Bump version --- setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 54681d6..5836318 100644 --- a/setup.py +++ b/setup.py @@ -2,16 +2,16 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.2" +__version__ = "0.3.2b" setup( name='pyfb', version=__version__, description='A Python Interface to the Facebook Graph API', author='Juan Manuel GarcĂ­a', - author_email = "jmg.utn@gmail.com", - license = "GPL v3", - keywords = "Facebook Graph API Wrapper Python", + author_email="jmg.utn@gmail.com", + license="GPL v3", + keywords="Facebook Graph API Wrapper Python", url='http://code.google.com/p/pyfb/', packages=['pyfb'], install_requires=[ From 2ec6b85bc4c9f8e44732636af8d2a0855682a945 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 10:31:56 +0200 Subject: [PATCH 03/22] Major changes to try to clean up the module --- pyfb/client.py | 143 +++++++++++++++++-------------------------------- setup.py | 2 +- 2 files changed, 51 insertions(+), 94 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 148ea1e..c0703a5 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -2,25 +2,21 @@ The implementation of the Facebook Client """ -import urllib2 -import urllib -import auth from urlparse import parse_qsl from utils import Json2ObjectsFactory +import auth +import requests +import urllib +import urllib2 class FacebookClient(object): """ This class implements the interface to the Facebook Graph API """ - FACEBOOK_URL = "https://www.facebook.com/" - GRAPH_URL = "https://graph.facebook.com/" - API_URL = "https://api.facebook.com/" - - BASE_AUTH_URL = "%soauth/authorize?" % GRAPH_URL - DIALOG_BASE_URL = "%sdialog/feed?" % FACEBOOK_URL - FBQL_BASE_URL = "%smethod/fql.query?" % API_URL - BASE_TOKEN_URL = "%soauth/access_token?" % GRAPH_URL + FACEBOOK_DOMAIN = "www.facebook.com" + GRAPH_DOMAIN = "graph.facebook.com" + API_DOMAIN = "api.facebook.com" DEFAULT_REDIRECT_URI = "http://www.facebook.com/connect/login_success.html" DEFAULT_SCOPE = auth.ALL_PERMISSIONS @@ -30,74 +26,56 @@ class FacebookClient(object): factory = Json2ObjectsFactory() def __init__(self, app_id, access_token=None, raw_data=None): - self.app_id = app_id self.access_token = access_token self.raw_data = raw_data self.permissions = self.DEFAULT_SCOPE self.expires = None - def _make_request(self, url, **data): - """ - Makes a simple request. If not data is a GET else is a POST. - """ - if not data: - data = None - return urllib2.urlopen(url, data).read() + self.session = requests.Session() - def _make_auth_request(self, path, params=None, **data): + def _make_request(self, method="get", domain=API_DOMAIN, path=None, params=None, auth=True, **data): """ Makes a request to the facebook Graph API. This method requires authentication! Don't forgot to get the access token before use it. """ - if self.access_token is None: - raise PyfbException("Must Be authenticated. Do you forgot to get the access token?") - if params is None: params = {} else: for key, value in params.items(): if value is None: del params[key] - params["access_token"] = self.access_token - url = "%s%s?%s" % (self.GRAPH_URL, path, urllib.urlencode(params)) - if data: - post_data = urllib.urlencode(data) - else: - post_data = None - return urllib2.urlopen(url, post_data).read() + if auth: + if self.access_token is None: + raise PyfbException("Must Be authenticated. Do you forgot to get the access token?") - def _make_object(self, name, data): - """ - Uses the factory to make an object from a json - """ - if not self.raw_data: - return self.factory.make_object(name, data) - return self.factory.loads(data) + params["access_token"] = self.access_token + + url = "https://%s/%s" % (domain, path) + + response = self.session.request(method, url, params, data) + if response.status_code < 200 or response > 299: + raise PyfbException("Got response %s" % response.status_code) - def _get_url_path(self, dic): + return response.content - return urllib.urlencode(dic) + def _build_url(self, domain, path, params): + return "https://%s/%s?%s" % (domain, path, urllib.urlencode(params)) def _get_auth_url(self, params, redirect_uri): """ Returns the authentication url """ - if redirect_uri is None: - redirect_uri = self.DEFAULT_REDIRECT_URI params['redirect_uri'] = redirect_uri - url_path = self._get_url_path(params) - url = "%s%s" % (self.BASE_AUTH_URL, url_path) - return url + return self._build_url(self.FACEBOOK_DOMAIN, "oauth/authorize", params) def _get_permissions(self): - return ",".join(self.permissions) - def get_auth_token_url(self, redirect_uri): + def get_auth_token_url(self, redirect_uri=DEFAULT_REDIRECT_URI): """ Returns the authentication token url """ @@ -106,9 +84,10 @@ def get_auth_token_url(self, redirect_uri): "type": "user_agent", "scope": self._get_permissions(), } + return self._get_auth_url(params, redirect_uri) - def get_auth_code_url(self, redirect_uri): + def get_auth_code_url(self, redirect_uri=DEFAULT_REDIRECT_URI): """ Returns the url to get a authentication code """ @@ -116,57 +95,42 @@ def get_auth_code_url(self, redirect_uri): "client_id": self.app_id, "scope": self._get_permissions(), } - return self._get_auth_url(params, redirect_uri) - - def get_access_token(self, app_secret_key, secret_code, redirect_uri): - - if redirect_uri is None: - redirect_uri = self.DEFAULT_REDIRECT_URI - self.secret_key = app_secret_key + return self._get_auth_url(params, redirect_uri) - url_path = self._get_url_path({ + def get_access_token(self, app_secret_key, secret_code, redirect_uri=DEFAULT_REDIRECT_URI): + params = { "client_id": self.app_id, "client_secret" : app_secret_key, "redirect_uri" : redirect_uri, "code" : secret_code, - }) - url = "%s%s" % (self.BASE_TOKEN_URL, url_path) - - data = self._make_request(url) + } - if not "access_token" in data: - ex = self.factory.make_object('Error', data) - raise PyfbException(ex.error.message) + data = self._make_request(path="oauth/access_token", params=params, auth=False) - data = dict(parse_qsl(data)) self.access_token = data.get('access_token') - self.expires = data.get('expires') - return self.access_token + if not self.access_token: + raise PyfbException(data.get("error")) - def get_dialog_url(self, redirect_uri): + self.expires = data.get('expires') - if redirect_uri is None: - redirect_uri = self.DEFAULT_DIALOG_URI + return self.access_token - url_path = self._get_url_path({ + def get_dialog_url(self, redirect_uri=DEFAULT_DIALOG_URI): + params = { "app_id" : self.app_id, "redirect_uri": redirect_uri, - }) - url = "%s%s" % (self.DIALOG_BASE_URL, url_path) - return url + } + + return self._build_url(self.FACEBOOK_DOMAIN, "dialog/feed", params) def get_one(self, path, object_name, **params): """ Gets one object """ - data = self._make_auth_request(path, params) - obj = self._make_object(object_name, data) - - if hasattr(obj, 'error'): - raise PyfbException(obj.error.message) + data = self._make_request(path=path, params=params) - return obj + return self.factory.make_object(object_name, data) def get_list(self, id, path, object_name=None, **params): """ @@ -177,6 +141,7 @@ def get_list(self, id, path, object_name=None, **params): if object_name is None: object_name = path path = "%s/%s" % (id, path.lower()) + return self.get_one(path, object_name, **params).__dict__[object_name] def push(self, id, path, **data): @@ -186,14 +151,16 @@ def push(self, id, path, **data): if id is None: id = "me" path = "%s/%s" % (id, path) - self._make_auth_request(path, **data) + + self._make_request(method="put", path=path, **data) def delete(self, id): """ Deletes a object by id """ data = {"method": "delete"} - self._make_auth_request(id, **data) + + self._make_request(method="delete", path=id, **data) def _get_table_name(self, query): """ @@ -212,19 +179,9 @@ def execute_fql_query(self, query): Executes a FBQL query and return a list of objects """ table = self._get_table_name(query) - url_path = self._get_url_path({'query' : query, 'format' : 'json'}) - url = "%s%s" % (self.FBQL_BASE_URL, url_path) - data = self._make_request(url) + params = {'query' : query, 'format' : 'json'} + data = self._make_request(path="method/fql.query", params=params) return self.factory.make_objects_list(table, data) - class PyfbException(Exception): - """ - A PyFB Exception class - """ - - def __init__(self, value): - self.value = value - - def __str__(self): - return repr(self.value) + pass diff --git a/setup.py b/setup.py index 5836318..464357f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.2b" +__version__ = "0.3.2c" setup( name='pyfb', From 1a6df55a92415e647cafbbefa0a2b505a3279875 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 11:08:46 +0200 Subject: [PATCH 04/22] Some fixes --- pyfb/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index c0703a5..dee4256 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -34,7 +34,7 @@ def __init__(self, app_id, access_token=None, raw_data=None): self.session = requests.Session() - def _make_request(self, method="get", domain=API_DOMAIN, path=None, params=None, auth=True, **data): + def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=None, auth=True, **data): """ Makes a request to the facebook Graph API. This method requires authentication! @@ -56,7 +56,7 @@ def _make_request(self, method="get", domain=API_DOMAIN, path=None, params=None, url = "https://%s/%s" % (domain, path) response = self.session.request(method, url, params, data) - if response.status_code < 200 or response > 299: + if response.status_code < 200 or response.status_code > 299: raise PyfbException("Got response %s" % response.status_code) return response.content @@ -180,7 +180,7 @@ def execute_fql_query(self, query): """ table = self._get_table_name(query) params = {'query' : query, 'format' : 'json'} - data = self._make_request(path="method/fql.query", params=params) + data = self._make_request(domain=self.API_DOMAIN, path="method/fql.query", params=params) return self.factory.make_objects_list(table, data) class PyfbException(Exception): From a0e41cc7b393f93035b42226795e866418940271 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 11:23:31 +0200 Subject: [PATCH 05/22] Fix access token --- pyfb/client.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index dee4256..fdec78e 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -25,10 +25,9 @@ class FacebookClient(object): #A factory to make objects from a json factory = Json2ObjectsFactory() - def __init__(self, app_id, access_token=None, raw_data=None): + def __init__(self, app_id, access_token=None): self.app_id = app_id self.access_token = access_token - self.raw_data = raw_data self.permissions = self.DEFAULT_SCOPE self.expires = None @@ -108,10 +107,9 @@ def get_access_token(self, app_secret_key, secret_code, redirect_uri=DEFAULT_RED data = self._make_request(path="oauth/access_token", params=params, auth=False) - self.access_token = data.get('access_token') - if not self.access_token: - raise PyfbException(data.get("error")) + data = dict(parse_qsl(data)) + self.access_token = data.get('access_token') self.expires = data.get('expires') return self.access_token From b2b4fbb12172e69e28c8623fb2430d1b5998ec85 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 11:29:53 +0200 Subject: [PATCH 06/22] Remove the raw_data from pyfb also --- pyfb/pyfb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index 44d52a5..c65f624 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -13,9 +13,9 @@ class Pyfb(object): This class is Facade for FacebookClient """ - def __init__(self, app_id, access_token=None, raw_data=False): + def __init__(self, app_id, access_token=None): - self._client = FacebookClient(app_id, access_token, raw_data) + self._client = FacebookClient(app_id, access_token) def authenticate(self): """ From 29b922f416d934c70773076fea22f57fe24312e9 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 14:13:24 +0200 Subject: [PATCH 07/22] Simplify object factory --- pyfb/utils.py | 43 +++++++++++++------------------------------ setup.py | 2 +- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/pyfb/utils.py b/pyfb/utils.py index 49c1c88..344267e 100644 --- a/pyfb/utils.py +++ b/pyfb/utils.py @@ -4,15 +4,13 @@ This file provides utilities to the pyfb library """ -import simplejson - class NamedObject(object): """ Builds an object of a runtime generated class with a name passed by argument. """ def __new__(cls, name): - return type(str(name), (object, ), {}) + return type(str(name), (object,), {}) class Json2ObjectsFactory(object): @@ -23,35 +21,20 @@ class Json2ObjectsFactory(object): everything into an object. """ - def loads(self, data): - return simplejson.loads(data) - - def make_object(self, name, data): - raw = simplejson.loads(data) - return self._make_object(name, raw) - - def make_objects_list(self, name, data): - raw = simplejson.loads(data) - return self._make_objects_list(name, raw) - def _make_objects_list(self, name, values): - objs = [] - for data in values: - if isinstance(data, dict): - objs.append(self._make_object(name, data)) - else: - objs.append(data) - return objs - - def _make_object(self, name, dic): + return [self._make_object(name, value) for value in values] + + def _make_object_dict(self, name, dic): #Life's easy. For Python Programmers BTW ;-). obj = NamedObject(name) for key, value in dic.iteritems(): - if key == 'data': - key = obj.__name__ - if isinstance(value, list): - value = self._make_objects_list(key, value) - elif isinstance(value, dict): - value = self._make_object(key, value) - setattr(obj, key, value) + setattr(obj, key, self._make_object(name, obj)) return obj + + def make_object(self, name, obj): + if isinstance(obj, dict): + return self._make_object_dict(name, obj) + elif isinstance(obj, list): + return self._make_objects_list(name, obj) + else: + return obj diff --git a/setup.py b/setup.py index 464357f..3c8019a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.2c" +__version__ = "0.3.2d" setup( name='pyfb', From 0cc09a5e3dc05aec530b40bbc0a135cd54649aca Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 14:15:14 +0200 Subject: [PATCH 08/22] This weird hack is not needed anymore --- pyfb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfb/client.py b/pyfb/client.py index fdec78e..32fc60a 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -140,7 +140,7 @@ def get_list(self, id, path, object_name=None, **params): object_name = path path = "%s/%s" % (id, path.lower()) - return self.get_one(path, object_name, **params).__dict__[object_name] + return self.get_one(path, object_name, **params) def push(self, id, path, **data): """ From ee8d5e80e71059ca9510c4a2cd82b2ab681e1a02 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 14:31:03 +0200 Subject: [PATCH 09/22] Need to parse the json ourself --- pyfb/utils.py | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pyfb/utils.py b/pyfb/utils.py index 344267e..49359f3 100644 --- a/pyfb/utils.py +++ b/pyfb/utils.py @@ -4,6 +4,8 @@ This file provides utilities to the pyfb library """ +import simplejson + class NamedObject(object): """ Builds an object of a runtime generated class with a name @@ -32,6 +34,8 @@ def _make_object_dict(self, name, dic): return obj def make_object(self, name, obj): + obj = simplejson.loads(obj) + if isinstance(obj, dict): return self._make_object_dict(name, obj) elif isinstance(obj, list): diff --git a/setup.py b/setup.py index 3c8019a..38ae71c 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.2d" +__version__ = "0.3.3" setup( name='pyfb', From b5160ed9083733cfe16a5edc59877986104663a0 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 24 Apr 2012 14:37:19 +0200 Subject: [PATCH 10/22] Keep on working on this --- pyfb/client.py | 2 +- pyfb/utils.py | 21 ++++++++++++--------- setup.py | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 32fc60a..052dc3f 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -140,7 +140,7 @@ def get_list(self, id, path, object_name=None, **params): object_name = path path = "%s/%s" % (id, path.lower()) - return self.get_one(path, object_name, **params) + return self.get_one(path, object_name, **params).data def push(self, id, path, **data): """ diff --git a/pyfb/utils.py b/pyfb/utils.py index 49359f3..5cd3869 100644 --- a/pyfb/utils.py +++ b/pyfb/utils.py @@ -30,15 +30,18 @@ def _make_object_dict(self, name, dic): #Life's easy. For Python Programmers BTW ;-). obj = NamedObject(name) for key, value in dic.iteritems(): - setattr(obj, key, self._make_object(name, obj)) + setattr(obj, key, self._make_object(name, value)) return obj - def make_object(self, name, obj): - obj = simplejson.loads(obj) - - if isinstance(obj, dict): - return self._make_object_dict(name, obj) - elif isinstance(obj, list): - return self._make_objects_list(name, obj) + def _make_object(self, name, value): + if isinstance(value, dict): + return self._make_object_dict(name, value) + elif isinstance(value, list): + return self._make_objects_list(name, value) else: - return obj + return value + + def make_object(self, name, value): + value = simplejson.loads(value) + + return self._make_object(name, value) diff --git a/setup.py b/setup.py index 38ae71c..4df30a9 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.3" +__version__ = "0.3.4" setup( name='pyfb', From 2c9e1e67c0d22383e65fa647a53ae2b81a4f614d Mon Sep 17 00:00:00 2001 From: holm Date: Wed, 25 Apr 2012 08:25:51 +0200 Subject: [PATCH 11/22] Put error response content in exception also --- pyfb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfb/client.py b/pyfb/client.py index 052dc3f..b5c3a1e 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -56,7 +56,7 @@ def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=Non response = self.session.request(method, url, params, data) if response.status_code < 200 or response.status_code > 299: - raise PyfbException("Got response %s" % response.status_code) + raise PyfbException("Got response %s: %s" % (response.status_code, response.content)) return response.content From 285b3d971a22eb7490d8c166b21fd55f89d8d6f3 Mon Sep 17 00:00:00 2001 From: holm Date: Wed, 25 Apr 2012 08:26:26 +0200 Subject: [PATCH 12/22] Add dependency on requests also --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4df30a9..e420d27 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.4" +__version__ = "0.3.5" setup( name='pyfb', @@ -16,5 +16,6 @@ packages=['pyfb'], install_requires=[ 'simplejson', + "requests" ], ) From c45b646b35582b4f4677e145cf988b1c762aa519 Mon Sep 17 00:00:00 2001 From: holm Date: Wed, 25 Apr 2012 10:38:21 +0200 Subject: [PATCH 13/22] Parse errors and rethrow as exception with all information --- pyfb/client.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pyfb/client.py b/pyfb/client.py index b5c3a1e..6702d27 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -56,7 +56,14 @@ def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=Non response = self.session.request(method, url, params, data) if response.status_code < 200 or response.status_code > 299: - raise PyfbException("Got response %s: %s" % (response.status_code, response.content)) + content = self.factory.make_object("Error", response.content) + + if hasattr(content, "error"): + error = content.error + + raise PyfbStatusException(error.type, error.code, error.error_subcode, error.message) + else: + raise PyfbException(response.content) return response.content @@ -183,3 +190,11 @@ def execute_fql_query(self, query): class PyfbException(Exception): pass + +class PyfbStatusException(Exception): + + def __init__(self, type, code, subcode, message): + self.type = type + self.code = code + self.subcode = subcode + super(PyfbStatusException, self).__init__(message) From a4f7d30edb0b71f59f20dba2eb9a2a97bc9cab79 Mon Sep 17 00:00:00 2001 From: holm Date: Thu, 26 Apr 2012 22:27:05 +0200 Subject: [PATCH 14/22] Seems subcode is sometimes not set --- pyfb/client.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 6702d27..2bf7be2 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -61,7 +61,7 @@ def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=Non if hasattr(content, "error"): error = content.error - raise PyfbStatusException(error.type, error.code, error.error_subcode, error.message) + raise PyfbStatusException(error.type, error.code, getattr(error, "error_subcode"), error.message) else: raise PyfbException(response.content) diff --git a/setup.py b/setup.py index e420d27..bf1bd67 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.5" +__version__ = "0.3.6" setup( name='pyfb', From 6b0c5a0d3704be711e2099a705bae0649e8a3567 Mon Sep 17 00:00:00 2001 From: holm Date: Thu, 3 May 2012 11:11:39 +0200 Subject: [PATCH 15/22] Add default value for getattr --- pyfb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfb/client.py b/pyfb/client.py index 2bf7be2..065f49f 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -61,7 +61,7 @@ def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=Non if hasattr(content, "error"): error = content.error - raise PyfbStatusException(error.type, error.code, getattr(error, "error_subcode"), error.message) + raise PyfbStatusException(error.type, error.code, getattr(error, "error_subcode", None), error.message) else: raise PyfbException(response.content) From b75e51fd05c6f5bd3fa6c9d20d5c1516c1fc8738 Mon Sep 17 00:00:00 2001 From: holm Date: Mon, 23 Jul 2012 13:13:09 +0200 Subject: [PATCH 16/22] More generic version of publish --- pyfb/client.py | 10 ++++++++++ pyfb/pyfb.py | 4 ++-- setup.py | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 065f49f..ef9f597 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -149,6 +149,16 @@ def get_list(self, id, path, object_name=None, **params): return self.get_one(path, object_name, **params).data + def post(self, id, path, **data): + """ + Posts data to facebook + """ + if id is None: + id = "me" + path = "%s/%s" % (id, path) + + self._make_request(method="post", path=path, **data) + def push(self, id, path, **data): """ Pushes data to facebook diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index c65f624..398a02f 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -118,11 +118,11 @@ def get_comments(self, id=None): """ return self._client.get_list(id, "Comments") - def publish(self, message, id=None): + def publish(self, message, id=None, **kwargs): """ Publishes a message on the wall """ - self._client.push(id, "feed", message=message) + self._client.post(id, "feed", message=message, **kwargs) def comment(self, message, id=None): """ diff --git a/setup.py b/setup.py index bf1bd67..c291fe5 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.6" +__version__ = "0.3.7" setup( name='pyfb', From e3ec5354e7aa1acd61f604d6c44d7d1fea335f1b Mon Sep 17 00:00:00 2001 From: holm Date: Fri, 24 Aug 2012 15:54:51 +0200 Subject: [PATCH 17/22] Support creating actions in the client --- pyfb/pyfb.py | 6 ++++++ setup.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index 398a02f..d7d3295 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -124,6 +124,12 @@ def publish(self, message, id=None, **kwargs): """ self._client.post(id, "feed", message=message, **kwargs) + def action(self, namespace, action_type_name, object_type_name, object_url, id=None, **kwargs): + """ + Publishes a message on the wall + """ + self._client.post(id, "%s:%s" % (namespace, action_type_name), **{object_type_name: object_url}) + def comment(self, message, id=None): """ Publishes a message on the wall diff --git a/setup.py b/setup.py index c291fe5..2227819 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.7" +__version__ = "0.3.8" setup( name='pyfb', From 0541be6a5a750c015c10e8176594ba3e18c18e66 Mon Sep 17 00:00:00 2001 From: holm Date: Tue, 28 Aug 2012 10:49:26 +0200 Subject: [PATCH 18/22] Support limit and offset for friends --- pyfb/pyfb.py | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index d7d3295..20e8b05 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -94,11 +94,11 @@ def get_user_by_id(self, id=None, fields=None): id = "me" return self._client.get_one(id, "FBUser", fields=fields) - def get_friends(self, id=None, fields=None): + def get_friends(self, id=None, fields=None, limit=None, offset=None): """ Gets a list with your friends """ - return self._client.get_list(id, "Friends", fields=fields) + return self._client.get_list(id, "Friends", fields=fields, limit=limit, offset=offset) def get_statuses(self, id=None): """ diff --git a/setup.py b/setup.py index 2227819..b24362a 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from setuptools import setup, find_packages -__version__ = "0.3.8" +__version__ = "0.3.9" setup( name='pyfb', From 43ff5e84914c0a6c5da02e0fc4f8b140303c61c5 Mon Sep 17 00:00:00 2001 From: holm Date: Thu, 17 Jan 2013 12:41:01 -0800 Subject: [PATCH 19/22] Add timeout parameter --- pyfb/client.py | 4 ++-- pyfb/pyfb.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index ef9f597..3ad292a 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -25,13 +25,13 @@ class FacebookClient(object): #A factory to make objects from a json factory = Json2ObjectsFactory() - def __init__(self, app_id, access_token=None): + def __init__(self, app_id, access_token=None, timeout=30): self.app_id = app_id self.access_token = access_token self.permissions = self.DEFAULT_SCOPE self.expires = None - self.session = requests.Session() + self.session = requests.Session(timeout=timeout) def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=None, auth=True, **data): """ diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index 20e8b05..27d3d58 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -13,9 +13,9 @@ class Pyfb(object): This class is Facade for FacebookClient """ - def __init__(self, app_id, access_token=None): + def __init__(self, app_id, access_token=None, timeout=30): - self._client = FacebookClient(app_id, access_token) + self._client = FacebookClient(app_id, access_token, timeout=timeout) def authenticate(self): """ From d33290b2788f2cb3a0516cabfcafb3664aadd64f Mon Sep 17 00:00:00 2001 From: George Sakkis Date: Sun, 10 Feb 2013 12:18:50 +0100 Subject: [PATCH 20/22] Added get_picture_url method --- pyfb/client.py | 19 +++++++++++++++++++ pyfb/pyfb.py | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/pyfb/client.py b/pyfb/client.py index 3ad292a..197430a 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -129,6 +129,25 @@ def get_dialog_url(self, redirect_uri=DEFAULT_DIALOG_URI): return self._build_url(self.FACEBOOK_DOMAIN, "dialog/feed", params) + def get_picture_url(self, id, ssl=False, auth=False, size=None): + params = {} + if ssl: + params["return_ssl_resources"] = "1" + if auth: + params["access_token"] = self.access_token + if size is not None: + if isinstance(size, basestring): + params["type"] = size + else: + if isinstance(size, (int, long)): + width = height = size + else: + width, height = map(int, size) + params["width"] = str(width) + params["height"] = str(height) + + return self._build_url(self.GRAPH_DOMAIN, "%s/picture" % id, params) + def get_one(self, path, object_name, **params): """ Gets one object diff --git a/pyfb/pyfb.py b/pyfb/pyfb.py index 27d3d58..1ef4f58 100644 --- a/pyfb/pyfb.py +++ b/pyfb/pyfb.py @@ -66,6 +66,18 @@ def _show_in_browser(self, url): """ webbrowser.open(url) + def get_picture_url(self, id, ssl=False, auth=False, size=None): + """Returns the url of a profile picture. + + :param ssl: True if the picture needs to be returned over a secure connection. + :param auth: True if authentication is required. + :param size: Can be: + - a predefined string ("square", "small", "normal", "large") + - or a (width, height) tuple + - or a single integer for square picture (width == height) + """ + return self._client.get_picture_url(id, ssl=ssl, auth=auth, size=size) + def set_access_token(self, token): """ Sets the access token. Necessary to make the requests that requires autenthication From 9495ae6a2cef689380d5846f29dada44ea58ad40 Mon Sep 17 00:00:00 2001 From: Christian Holm Date: Tue, 8 Oct 2013 14:23:14 +0200 Subject: [PATCH 21/22] Be compatible with new versions of requests --- pyfb/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 197430a..9162fc0 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -22,7 +22,7 @@ class FacebookClient(object): DEFAULT_SCOPE = auth.ALL_PERMISSIONS DEFAULT_DIALOG_URI = "http://www.example.com/response/" - #A factory to make objects from a json + # A factory to make objects from a json factory = Json2ObjectsFactory() def __init__(self, app_id, access_token=None, timeout=30): @@ -31,7 +31,8 @@ def __init__(self, app_id, access_token=None, timeout=30): self.permissions = self.DEFAULT_SCOPE self.expires = None - self.session = requests.Session(timeout=timeout) + self.session = requests.Session() + self.session.timeout = timeout def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=None, auth=True, **data): """ From d54e43bef8a85fac200fd8e00f0a149e63822600 Mon Sep 17 00:00:00 2001 From: Christian Holm Date: Sat, 1 Feb 2014 16:27:05 +0100 Subject: [PATCH 22/22] Don't try to read content if there is none --- pyfb/client.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pyfb/client.py b/pyfb/client.py index 9162fc0..779c6ab 100755 --- a/pyfb/client.py +++ b/pyfb/client.py @@ -57,14 +57,15 @@ def _make_request(self, method="get", domain=GRAPH_DOMAIN, path=None, params=Non response = self.session.request(method, url, params, data) if response.status_code < 200 or response.status_code > 299: - content = self.factory.make_object("Error", response.content) + if response.content: + content = self.factory.make_object("Error", response.content) - if hasattr(content, "error"): - error = content.error + if hasattr(content, "error"): + error = content.error - raise PyfbStatusException(error.type, error.code, getattr(error, "error_subcode", None), error.message) - else: - raise PyfbException(response.content) + raise PyfbStatusException(error.type, error.code, getattr(error, "error_subcode", None), error.message) + + raise PyfbException(response.content) return response.content