Skip to content

Commit

Permalink
lots of docstring tweaks, sphinx autodoc config
Browse files Browse the repository at this point in the history
  • Loading branch information
snarfed committed Nov 22, 2016
1 parent 44de757 commit a4ffea0
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 211 deletions.
6 changes: 6 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
'sphinx.ext.viewcode',
]

# autodoc settings
autoclass_content = 'both' # both class and __init__ docstrings
autodoc_member_order = 'bysource'
autodoc_default_flags = ['show-inheritance', 'members']
# 'no-private-members', 'no-special-members']

# Napoleon settings
# http://www.sphinx-doc.org/en/stable/ext/napoleon.html
napoleon_google_docstring = True
Expand Down
5 changes: 3 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,9 @@ Changelog

- Facebook:

- Fetch `Open Graph stories aka ``news.publish``
actions <https://developers.facebook.com/docs/reference/opengraph/action-type/news.publishes/>`__.
- Fetch `Open Graph
stories <https://developers.facebook.com/docs/reference/opengraph/action-type/news.publishes/>`__
aka ``news.publish`` actions.
- Many bug fixes for photo posts: better privacy detection, fix bug
that attached comments to wrong posts.

Expand Down
66 changes: 16 additions & 50 deletions docs/source/granary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,70 +4,36 @@ granary package
Reference documentation.

.. toctree::

granary


atom module
-------------------

atom
----
.. automodule:: granary.atom
:members:
:undoc-members:
:show-inheritance:

facebook module
-----------------------

facebook
--------
.. automodule:: granary.facebook
:members:
:undoc-members:
:show-inheritance:

flickr module
---------------------

flickr
------
.. automodule:: granary.flickr
:members:
:undoc-members:
:show-inheritance:

googleplus module
-------------------------

googleplus
----------
.. automodule:: granary.googleplus
:members:
:undoc-members:
:show-inheritance:

instagram module
------------------------

instagram
---------
.. automodule:: granary.instagram
:members:
:undoc-members:
:show-inheritance:

microformats2 module
----------------------------

microformats2
-------------
.. automodule:: granary.microformats2
:members:
:undoc-members:
:show-inheritance:

source module
---------------------

source
------
.. automodule:: granary.source
:members:
:undoc-members:
:show-inheritance:

twitter module
----------------------

twitter
-------
.. automodule:: granary.twitter
:members:
:undoc-members:
:show-inheritance:
6 changes: 4 additions & 2 deletions granary/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def activities_to_atom(activities, actor, title=None, request_url=None,
xml_base: the base URL, if any. Used in the top-level xml:base attribute.
rels: rel links to include. dict mapping string rel value to string URL.
Returns: unicode string with Atom XML
Returns:
unicode string with Atom XML
"""
# Strip query params from URLs so that we don't include access tokens, etc
host_url = (_remove_query_params(host_url) if host_url
Expand Down Expand Up @@ -116,7 +117,8 @@ def html_to_atom(html, url=None, fetch_author=False):
url: string URL html came from, optional
fetch_author: boolean, whether to make HTTP request to fetch rel-author link
Returns: unicode string with Atom XML
Returns:
unicode string with Atom XML
"""
if fetch_author:
assert url, 'fetch_author=True requires url!'
Expand Down
127 changes: 70 additions & 57 deletions granary/facebook.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# coding=utf-8
"""Facebook source class. Uses the Graph API.
https://developers.facebook.com/docs/graph-api/using-graph-api/
The Audience Targeting 'to' field is set to @public or @private based on whether
the Facebook object's 'privacy' field is 'EVERYONE' or anything else.
https://developers.facebook.com/docs/reference/api/privacy-parameter/
Retrieving @all activities from get_activities() (the default) currently returns
an incomplete set of activities, ie *NOT* exactly the same set as your Facebook
News Feed: https://www.facebook.com/help/327131014036297/
Retrieving @all activities from :meth:`get_activities()` (the default) currently
returns an incomplete set of activities, ie *NOT* exactly the same set as your
Facebook News Feed: https://www.facebook.com/help/327131014036297/
"""
"""
This is complicated, and I still don't fully understand how or why they differ,
but based on lots of experimenting and searching, it sounds like the current
state is that you just can't reproduce the News Feed via Graph API's /me/home,
Expand Down Expand Up @@ -376,7 +379,8 @@ def _merge_photos(self, posts):
Args:
posts: list of Facebook post object dicts
Returns: new list of post and photo object dicts
Returns:
new list of post and photo object dicts
"""
posts_by_obj_id = {}
for post in posts:
Expand Down Expand Up @@ -419,7 +423,8 @@ def _split_id_requests(self, api_call, ids):
api_call: string with %s placeholder for ids query param
ids: sequence of string ids
Returns: merged list of objects from the responses' 'data' fields
Returns:
merged list of objects from the responses' 'data' fields
"""
results = {}
for i in range(0, len(ids), MAX_IDS):
Expand Down Expand Up @@ -455,7 +460,8 @@ def get_event(self, event_id, owner_id=None):
id: string, site-specific event id
owner_id: string
Returns: dict, decoded ActivityStreams activity, or None if the event is not
Returns:
dict, decoded ActivityStreams activity, or None if the event is not
found or is owned by a different user than owner_id (if provided)
"""
event = None
Expand Down Expand Up @@ -802,7 +808,8 @@ def _get_person_tags(self, obj):
Args:
obj: ActivityStreams object
Returns: sequence of ActivityStreams tag objects with url, id, and optional
Returns:
sequence of ActivityStreams tag objects with url, id, and optional
displayName fields. The id field is a raw Facebook user id.
"""
people = {} # maps id to tag
Expand Down Expand Up @@ -886,7 +893,8 @@ def base_object(self, obj, verb=None, resolve_numeric_id=False):
resolve_numeric_id: if True, tries harder to populate the numeric_id field
by making an additional API call to look up the object if necessary.
Returns: dict, minimal ActivityStreams object. Usually has at least id,
Returns:
dict, minimal ActivityStreams object. Usually has at least id,
numeric_id, and url fields; may also have author.
"""
base_obj = super(Facebook, self).base_object(obj)
Expand Down Expand Up @@ -1377,7 +1385,8 @@ def event_to_activity(self, event, rsvps=None):
event: dict, a decoded JSON Facebook event
rsvps: list of JSON Facebook RSVPs
Returns: an ActivityStreams activity dict
Returns:
an ActivityStreams activity dict
"""
obj = self.event_to_object(event, rsvps=rsvps)
return {'object': obj,
Expand Down Expand Up @@ -1462,7 +1471,7 @@ def privacy_to_to(obj):
obj: dict, Facebook object (post, album, comment, etc)
Returns:
dict (ActivityStreams `to` object) or None if unknown
dict: ActivityStreams `to` object, or None if unknown
"""
privacy = obj.get('privacy')
if isinstance(privacy, dict):
Expand Down Expand Up @@ -1498,27 +1507,26 @@ def fql_stream_to_post(self, stream, actor=None):
actor: dict, a row from the FQL profile table
Returns:
a Graph API post dict
dict, Graph API post
Here's example code to query FQL and pass the results to this method:
Here's example code to query FQL and pass the results to this method::
resp = self.urlopen('https://graph.facebook.com/v2.0/fql?' + urllib.urlencode(
{'q': json.dumps({
'stream': '''\
SELECT actor_id, post_id, created_time, updated_time, attachment,
privacy, message, description
FROM stream
WHERE filter_key IN (
SELECT filter_key FROM stream_filter WHERE uid = me())
ORDER BY created_time DESC
LIMIT 50
''',
'actors': '''\
SELECT id, name, username, url, pic FROM profile WHERE id IN
(SELECT actor_id FROM #stream)
'''})}))
# resp = appengine_config.read('fql.json')
'stream': '''\\
SELECT actor_id, post_id, created_time, updated_time,
attachment, privacy, message, description
FROM stream
WHERE filter_key IN (
SELECT filter_key FROM stream_filter WHERE uid = me())
ORDER BY created_time DESC
LIMIT 50
''',
'actors': '''\\
SELECT id, name, username, url, pic FROM profile WHERE id IN
(SELECT actor_id FROM #stream)
'''})}))
results = {q['name']: q['fql_result_set'] for q in resp['data']}
actors = {a['id']: a for a in results['actors']}
posts = [self.fql_stream_to_post(row, actor=actors[row['actor_id']])
Expand Down Expand Up @@ -1556,6 +1564,7 @@ def parse_id(id, is_comment=False):
"""Parses a Facebook post or comment id.
Facebook ids come in different formats:
* Simple number, usually a user or post: 12
* Two numbers with underscore, usually POST_COMMENT or USER_POST: 12_34
* Three numbers with underscores, USER_POST_COMMENT: 12_34_56
Expand All @@ -1568,18 +1577,20 @@ def parse_id(id, is_comment=False):
* Four numbers with colons/underscore, USER:POST:SHARD_COMMENT: 12:34:63_56
* Five numbers with colons/underscore, USER:EVENT:UNKNOWN:UNKNOWN_UNKNOWN
Not currently supported! Examples:
111599105530674:998145346924699:10102446236688861:10207188792305341_998153510257216
111599105530674:195181727490727:10102446236688861:10205257726909910_195198790822354
111599105530674:998145346924699:10102446236688861:10207188792305341_998153510257216
111599105530674:195181727490727:10102446236688861:10205257726909910_195198790822354
Background:
* https://github.com/snarfed/bridgy/issues/305
* https://developers.facebook.com/bugs/786903278061433/
Args:
id: string or integer
is_comment: boolean
Returns: FacebookId. Some or all fields may be None.
Returns:
FacebookId: Some or all fields may be None.
"""
assert is_comment in (True, False), is_comment

Expand Down Expand Up @@ -1648,7 +1659,8 @@ def resolve_object_id(self, user_id, post_id, activity=None):
post_id: string Facebook post id
activity: optional AS activity representation of Facebook post
Returns: string Facebook object id or None
Returns:
string: Facebook object id or None
"""
assert user_id, user_id
assert post_id, post_id
Expand All @@ -1671,13 +1683,14 @@ def resolve_object_id(self, user_id, post_id, activity=None):
return str(resolved)

def urlopen(self, url, _as=dict, **kwargs):
"""Wraps urllib2.urlopen() and passes through the access token.
"""Wraps :func:`urllib2.urlopen()` and passes through the access token.
Args:
_as: if not None, parses the response as JSON and passes it through _as()
with this type. if None, returns the response object.
Returns: decoded JSON object or urlopen response object
Returns:
decoded JSON object or urlopen response object
"""
if not url.startswith('http'):
url = API_BASE + url
Expand Down Expand Up @@ -1726,17 +1739,17 @@ def _as(type, resp):
def urlopen_batch(self, urls):
"""Sends a batch of multiple API calls using Facebook's batch API.
Raises the appropriate urllib2.HTTPError if any individual call returns HTTP
status code 4xx or 5xx.
Raises the appropriate :class:`urllib2.HTTPError` if any individual call
returns HTTP status code 4xx or 5xx.
https://developers.facebook.com/docs/graph-api/making-multiple-requests
Args:
urls: sequence of string relative API URLs, e.g. ('me', 'me/accounts')
Returns: sequence of responses, either decoded JSON objects (when possible)
Returns:
sequence of responses, either decoded JSON objects (when possible)
or raw string bodies
"""
resps = self.urlopen_batch_full([{'relative_url': url} for url in urls])

Expand All @@ -1754,32 +1767,32 @@ def urlopen_batch_full(self, requests):
"""Sends a batch of multiple API calls using Facebook's batch API.
Similar to urlopen_batch(), but the requests arg and return value are dicts
with headers, HTTP status code, etc. Only raises urllib2.HTTPError if the
outer batch request itself returns an HTTP error.
with headers, HTTP status code, etc. Only raises :class:`urllib2.HTTPError`
if the outer batch request itself returns an HTTP error.
https://developers.facebook.com/docs/graph-api/making-multiple-requests
Args:
requests: sequence of dict requests in Facebook's batch format, except
that headers is a single dict, not a list of dicts.
[{'relative_url': 'me/feed',
'headers': {'ETag': 'xyz', ...},
},
...
]
that headers is a single dict, not a list of dicts, e.g.::
Returns: sequence of dict responses in Facebook's batch format, except that
body is JSON-decoded if possible, and headers is a single dict, not a list
of dicts.
[{'code': 200,
'headers': {'ETag': 'xyz', ...},
'body': {...},
},
...
]
[{'relative_url': 'me/feed',
'headers': {'ETag': 'xyz', ...},
},
...
]
Returns:
sequence of dict responses in Facebook's batch format, except that body is
JSON-decoded if possible, and headers is a single dict, not a list of
dicts, e.g.::
[{'code': 200,
'headers': {'ETag': 'xyz', ...},
'body': {...},
},
...
]
"""
for req in requests:
if 'method' not in req:
Expand Down

0 comments on commit a4ffea0

Please sign in to comment.