[Python-modules-commits] [praw] 01/01: Imported Upstream version 3.3.0
Riley Baird
orthogonal-guest at moszumanska.debian.org
Sun Nov 29 09:36:39 UTC 2015
This is an automated email from the git hooks/post-receive script.
orthogonal-guest pushed a commit to branch upstream
in repository praw.
commit dba9360b0bbe689b863911f2defc7e82bacf9c9d
Author: Riley Baird <BM-2cVqnDuYbAU5do2DfJTrN7ZbAJ246S4XiX at bitmessage.ch>
Date: Sat Nov 28 16:43:34 2015 +1100
Imported Upstream version 3.3.0
---
CHANGES.rst | 36 +++++++
PKG-INFO | 9 +-
README.rst | 2 +-
docs/conf.py | 6 +-
docs/pages/comment_parsing.rst | 14 +++
docs/pages/configuration_files.rst | 32 ++++++-
docs/pages/contributor_guidelines.rst | 2 +-
docs/pages/faq.rst | 8 +-
docs/pages/getting_started.rst | 7 +-
docs/pages/oauth.rst | 6 ++
docs/pages/useful_scripts.rst | 18 +++-
praw.egg-info/PKG-INFO | 9 +-
praw.egg-info/SOURCES.txt | 6 ++
praw/__init__.py | 105 +++++++++++++++------
praw/errors.py | 50 +++++++---
praw/helpers.py | 14 +--
praw/internal.py | 13 ++-
praw/objects.py | 86 ++++++++++++++---
praw/praw.ini | 1 +
setup.py | 5 +-
tests/cassettes/test_friends_oauth.json | 1 +
tests/cassettes/test_get_edited_oauth.json | 1 +
tests/cassettes/test_hide_oauth.json | 1 +
.../test_set_access_credentials_with_list.json | 1 +
.../test_set_access_credentials_with_string.json | 1 +
tests/helper.py | 9 +-
tests/test_comments.py | 2 +
tests/test_config.py | 42 +++++++++
tests/test_oauth2_reddit.py | 65 +++++++++++++
tests/test_redditor.py | 34 ++++++-
tests/test_submission.py | 93 ++++++++++--------
tests/test_subreddit.py | 13 ++-
32 files changed, 544 insertions(+), 148 deletions(-)
diff --git a/CHANGES.rst b/CHANGES.rst
index c7f1563..2ba7ed3 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -22,6 +22,42 @@ formatted links that link to the relevant place in the code overview.
.. begin_changelog_body
+PRAW 3.3.0
+----------
+ * **[BUGFIX]** Fixed login password prompt issue on windows (#485).
+ * **[BUGFIX]** Fixed unicode user-agent issue (#483).
+ * **[BUGFIX]** Fix duplicate request issue with comment and submission streams
+ (#501).
+ * **[BUGFIX]** Stopped :meth:`praw.objects.Redditor.friend` from raising
+ LoginRequired when using OAuth.
+ * **[BUGFIX]** Stopped a json-parsing error from occuring in cases where
+ reddit's response to a request was an empty string. :meth:`request_json`
+ will now simply return that empty string.
+ * **[BUGFIX]** Fix AssertionError when hiding and unhiding under OAuth, raised
+ by stacked scope decorators.
+ * **[BUGFIX]** Fix AttributeError when hiding and unhiding under OAuth without
+ the "identity" scope, raised when PRAW tried to evict the user's /hidden
+ page from the cache.
+ * **[CHANGE]** Added messages to all PRAW exceptions (#491).
+ * **[CHANGE]** Made it easier to send JSON dumps instead of form-encoded data
+ for http requests. Some api-v1 endpoints require the request body to be in
+ the json format.
+ * **[CHANGE]** Moved and deprecated
+ :meth:`praw.objects.LoggedInRedditor.get_friends` to
+ :class:`praw.AuthenticatedReddit`, leaving a pointer in its place.
+ Previously, `get_friends` was difficult to access because the only instance
+ of `LoggedInRedditor` was the reddit session's `user` attribute, which is
+ only instantiated if the user has the "identity" scope. By moving
+ `get_friends` to the reddit session, it can be used without having to
+ manipulate a :class:`praw.objects.Redditor` intsance's class.
+ * **[CHANGE]** Removed support for Python 2.6 and Python 3.2 (#532).
+ * **[FEATURE]** Added support for adding "notes" to your friends. Users with
+ reddit Gold can set the ``note`` parameter of
+ :meth:`praw.objects.Redditor.friend`. 300 character max enforced by reddit.
+ * **[FEATURE]** New :meth:`praw.objects.Redditor.get_friend_info` to see info
+ about one of your friends. Includes their name, ID, when you added them, and
+ if you have reddit Gold, your note about them.
+
PRAW 3.2.1
----------
* **[BUGFIX]** Fixed "multiple values for argument" error when solving
diff --git a/PKG-INFO b/PKG-INFO
index 8a51383..0510d87 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: praw
-Version: 3.2.1
+Version: 3.3.0
Summary: PRAW, an acronym for `Python Reddit API Wrapper`, is a python package that allows for simple access to reddit's API.
Home-page: https://praw.readthedocs.org/
Author: Bryce Boe
@@ -55,7 +55,7 @@ Description: .. _main_page:
Installation
------------
- PRAW works with python 2.6, 2.7, 3.1, 3.2, 3.3, and 3.4. The recommended way to
+ PRAW is supported on python 2.7, 3.3, 3.4 and 3.5. The recommended way to
install is via `pip <https://pypi.python.org/pypi/pip>`_
.. code-block:: bash
@@ -132,11 +132,8 @@ Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.1
-Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Utilities
diff --git a/README.rst b/README.rst
index 69b06b4..a002fb6 100644
--- a/README.rst
+++ b/README.rst
@@ -47,7 +47,7 @@ This will display something similar to the following:
Installation
------------
-PRAW works with python 2.6, 2.7, 3.1, 3.2, 3.3, and 3.4. The recommended way to
+PRAW is supported on python 2.7, 3.3, 3.4 and 3.5. The recommended way to
install is via `pip <https://pypi.python.org/pypi/pip>`_
.. code-block:: bash
diff --git a/docs/conf.py b/docs/conf.py
index d0ce8a4..c810492 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -235,10 +235,10 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'PythonRedditAPIWrapper',
+ ('index', 'praw',
u'Python Reddit API Wrapper Documentation',
- u'Bryce Boe', 'PythonRedditAPIWrapper',
- 'One line description of project.', 'Miscellaneous'),
+ u'Bryce Boe', 'praw',
+ 'Python Reddit API Wrapper', 'Python'),
]
# Documents to append as an appendix to all manuals.
diff --git a/docs/pages/comment_parsing.rst b/docs/pages/comment_parsing.rst
index bb50c79..17dfa4d 100644
--- a/docs/pages/comment_parsing.rst
+++ b/docs/pages/comment_parsing.rst
@@ -126,3 +126,17 @@ The full program
if comment.body == "Hello" and comment.id not in already_done:
comment.reply(' world!')
already_done.add(comment.id)
+
+[deleted] comments
+------------------
+
+When a comment is deleted, in most cases, that comment will not be viewable with a
+browser nor the API. However, if a comment is made, and then a reply to that comment
+is made, and *then* the original comment is deleted, that comment will have its
+``body`` and ``author`` attributes be ``NoneType`` via the API. The same goes with
+removed comments, unless the authenticated account is a mod of the subreddit whose
+comments you are getting. If you are a mod, and said comments are removed comments,
+they are left intact.
+
+If a comment is made and then the account that left that comment is deleted, the
+comment body is left intact, while the ``author`` attribute becomes ``NoneType``.
\ No newline at end of file
diff --git a/docs/pages/configuration_files.rst b/docs/pages/configuration_files.rst
index 0d011e9..8164f2f 100644
--- a/docs/pages/configuration_files.rst
+++ b/docs/pages/configuration_files.rst
@@ -71,9 +71,9 @@ config file. Each site can overwrite any of these variables.
will pick up the environment variable for https_proxy, if it has been set.
* *log_requests:* An **integer** that determines the level of API call logging.
- * **0:** no logging
- * **1:** log only the request URIs
- * **2:** log the request URIs as well as any POST data
+ * **0:** no logging
+ * **1:** log only the request URIs
+ * **2:** log the request URIs as well as any POST data
* *oauth_domain:* A **string** that defines the *domain* where OAuth
authenticated requests are sent.
@@ -155,7 +155,31 @@ The default provided sites are:
'local' site in your *user*-level or *local*-level ``praw.ini`` file.
Additional sites can be added to represent other instances of reddit or simply
-provide an additional set of credentials for easy access to that account.
+provide an additional set of credentials for easy access to that account. This
+is done by adding ``[YOUR_SITE]`` to the ``praw.ini`` file and then calling it
+in :class:`praw.Reddit`. For example, you could add the following to your
+``praw.ini`` file:
+
+.. code-block:: text
+
+ [YOUR_SITE]
+ domain: www.myredditsite.com
+ ssl_domain: ssl.myredditsite.com
+ user: bboe
+ pswd: this_isn't_my_password
+ api_request_delay: 7.0
+
+From there, to specify the reddit instance of "YOUR_SITE", you would do something
+like this:
+
+.. code-block:: python
+
+ import praw
+
+ r = praw.Reddit(user_agent='Custom Site Example for PRAW',
+ site_name='YOUR_SITE')
+
+Of course, you can use any of the above Configuration Variables as well.
Example praw.ini file
^^^^^^^^^^^^^^^^^^^^^
diff --git a/docs/pages/contributor_guidelines.rst b/docs/pages/contributor_guidelines.rst
index be45f5b..251697d 100644
--- a/docs/pages/contributor_guidelines.rst
+++ b/docs/pages/contributor_guidelines.rst
@@ -33,7 +33,7 @@ Documentation
* All publicly available functions, classes and modules should have a
docstring.
-* Use correct terminology. A subreddits name is something like ' t3_xyfc7'.
+* Use correct terminology. A subreddits name is something like ' t5_xyfc7'.
The correct term for a subreddits "name" like
`python <http://www.reddit.com/r/python>`_ is its display name.
* When referring to any reddit. Refer to it as 'reddit'. When you are speaking
diff --git a/docs/pages/faq.rst b/docs/pages/faq.rst
index de791ba..64cbe65 100644
--- a/docs/pages/faq.rst
+++ b/docs/pages/faq.rst
@@ -71,7 +71,10 @@ Some commands take a while. Why?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PRAW follows the `api guidelines <https://github.com/reddit/reddit/wiki/API>`_
-which require a 2 second delay between each API call.
+which require a 2 second delay between each API call via CookieAuth. If you
+are exclusively using OAuth2, you are allowed to change this delay in your
+``praw.ini`` file to be a 1 second delay. This will be the default once
+CookieAuth is deprecated.
When I print a Comment only part of it is printed. How can I get the rest?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -114,7 +117,8 @@ Non-obvious behavior and other need to know
* All of the listings (list of stories on subreddit, etc.) are generators,
*not* lists. If you need them to be lists, an easy way is to call ``list()``
with your variable as the argument.
-* The default limit for fetching Things is 25. You can change this with the
+* The default limit for fetching Things is your `reddit preferences default
+ <https://www.reddit.com/prefs>`_, usually 25. You can change this with the
``limit`` param. If want as many Things as you can then set ``limit=None``.
* We can at most get 1000 results from every listing, this is an upstream
limitation by reddit. There is nothing we can do to go past this
diff --git a/docs/pages/getting_started.rst b/docs/pages/getting_started.rst
index c8f204e..32bc1fa 100644
--- a/docs/pages/getting_started.rst
+++ b/docs/pages/getting_started.rst
@@ -6,10 +6,9 @@ Getting Started
In this tutorial we'll go over everything needed to create a bot or program
using reddit's API through the Python Reddit API Wrapper (PRAW). We're going to
write a program that breaks down a redditor's karma by subreddit, just like the
-`reddit gold <https://www.reddit.com/gold/about>`_ feature. Unlike that, our
-program can break it down for any redditor, not just us. However, it will be
-less precise due to limitations in the reddit API, but we're getting ahead of
-ourselves.
+reddit feature. Unlike that, our program can break it down for any redditor,
+not just us. However, it will be less precise due to limitations in the reddit
+API, but we're getting ahead of ourselves.
This is a beginners tutorial to PRAW. We'll go over the hows and whys of
everything from getting started to writing your first program accessing reddit.
diff --git a/docs/pages/oauth.rst b/docs/pages/oauth.rst
index 8699db0..a486fb7 100644
--- a/docs/pages/oauth.rst
+++ b/docs/pages/oauth.rst
@@ -156,6 +156,12 @@ need to refresh the access token.
This returns a dict, where the ``access_token`` key has had its value updated.
Neither ``scope`` nor ``refresh_token`` will have changed.
+Note: In version 3.2.0 and higher, PRAW will automatically attempt to refresh
+the access token if a refresh token is available when it expires. For most
+personal-use scripts, this eliminates the need to use
+:meth:`.refresh_access_information` except when signing in.
+
+
.. _oauth_webserver:
An example webserver
diff --git a/docs/pages/useful_scripts.rst b/docs/pages/useful_scripts.rst
index 6250cc0..18886a1 100644
--- a/docs/pages/useful_scripts.rst
+++ b/docs/pages/useful_scripts.rst
@@ -11,6 +11,10 @@ this page to add in more.
a program useful to subreddit moderators, and ``subreddit_stats``, a tool
to compute submission / comment statistics for a subreddit.
+`prawoauth2`_ by `Avinash Sajjanshetty <https://github.com/avinassh>`_
+ ``prawoauth2`` is a helper library for PRAW which makes writing Reddit bots/apps using
+ OAuth2 super easy, simple and fun.
+
`AutoModerator`_ by `Deimos <https://github.com/deimos>`_
A bot for automating straightforward reddit moderation tasks and improving
upon the existing spam-filter.
@@ -106,10 +110,10 @@ this page to add in more.
The bot will then send them a private message with the date they specified.
`u/RemindMeBot <http://www.reddit.com/user/RemindMeBot>`_.
-`Massdrop Multi Bot <https://github.com/darkmio/Massdrop-Reddit-Bot>`_
- A bot which made Massdrop available for everyone and then grew into an
- assortment of different fixes for links that regularly get mistakenly used,
- like Gfycat, Massdrop and Subreddit-Names in titles.
+`RedditRover`_ by `DarkMio <https://github.com/DarkMio>`_
+ A plugin based Reddit Multi Bot Framework intended for new and advanced
+ programmers to host a wide variety of Reddit bots without mangling with
+ all the ins and outs of Reddit, PRAW and API limitations.
`Reddit Keyword Tracking Bot`_ by Jermell Beane
<requires Kivy> A bot that will watch any subreddits and email you with
@@ -134,6 +138,9 @@ this page to add in more.
`ButtsBot`_ by `Judson Dunaway-Barlow <https://github.com/jadunawa>`_
A silly bot that posts a picture of a (clothed) butt from the Astros team whenever somebody in the /r/Astros subreddit uses any of a few certain keywords in a comment.
+`GoodReads Bot`_ by `Avinash Sajjanshetty <https://github.com/avinassh>`_
+ A bot which powers `/u/goodreadsbot <https://www.reddit.com/u/goodreadsbot>`_ on Reddit, posts information of a book whenever someone posts a link to Goodreads.
+
**\<Your Script Here\>**
Edit `this page on github <https://github.com/praw-dev/praw/blob/master/
docs/pages/useful_scripts.rst>`_ to add your script to this list.
@@ -146,8 +153,10 @@ this page to add in more.
.. _`Butcher bot`: https://github.com/xiphirx/Butcher-Bot
.. _`ButtsBot`: https://github.com/jadunawa/ButtsBot
.. _`EVE Killmail Reddit Bot`: https://github.com/ArnoldM904/EK_Reddit_Bot
+.. _`GoodReads Bot`: https://github.com/avinassh/Reddit-GoodReads-Bot
.. _`Groompbot`: https://github.com/AndrewNeo/groompbot
.. _`NetflixBot`: https://github.com/alanwright/netflixbot
+.. _`prawoauth2`: https://github.com/avinassh/prawoauth2
.. _`PRAWtools`: https://github.com/praw-dev/prawtools
.. _`Reddit Keyword Tracking Bot`:
https://github.com/SwedishBotMafia/RScanBot.Gen
@@ -155,6 +164,7 @@ this page to add in more.
.. _`Reddit-to-Diigo-Copier`:
https://github.com/OdinsHat/Reddit-to-Diigo-Copier
.. _`RedditAgain`: https://github.com/karan/RedditAgain
+.. _`RedditRover` : https://github.com/DarkMio/RedditRover
.. _`RemindMeBot`: https://github.com/SIlver--/remindmebot-reddit
.. _`VideoLinkBot`: https://github.com/dmarx/VideoLinkBot
.. _`newsfrbot`: https://github.com/gardaud/newsfrbot
diff --git a/praw.egg-info/PKG-INFO b/praw.egg-info/PKG-INFO
index 8a51383..0510d87 100644
--- a/praw.egg-info/PKG-INFO
+++ b/praw.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: praw
-Version: 3.2.1
+Version: 3.3.0
Summary: PRAW, an acronym for `Python Reddit API Wrapper`, is a python package that allows for simple access to reddit's API.
Home-page: https://praw.readthedocs.org/
Author: Bryce Boe
@@ -55,7 +55,7 @@ Description: .. _main_page:
Installation
------------
- PRAW works with python 2.6, 2.7, 3.1, 3.2, 3.3, and 3.4. The recommended way to
+ PRAW is supported on python 2.7, 3.3, 3.4 and 3.5. The recommended way to
install is via `pip <https://pypi.python.org/pypi/pip>`_
.. code-block:: bash
@@ -132,11 +132,8 @@ Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.1
-Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Utilities
diff --git a/praw.egg-info/SOURCES.txt b/praw.egg-info/SOURCES.txt
index 5ce9b47..cd72d6d 100644
--- a/praw.egg-info/SOURCES.txt
+++ b/praw.egg-info/SOURCES.txt
@@ -43,6 +43,7 @@ tests/__init__.py
tests/helper.py
tests/test_authenticated_reddit.py
tests/test_comments.py
+tests/test_config.py
tests/test_decorators.py
tests/test_flair.py
tests/test_images.py
@@ -111,6 +112,7 @@ tests/cassettes/test_flair_csv_empty.json
tests/cassettes/test_flair_csv_many.json
tests/cassettes/test_flair_csv_optional_args.json
tests/cassettes/test_flair_csv_requires_user.json
+tests/cassettes/test_friends_oauth.json
tests/cassettes/test_front_page_comment_replies_are_none.json
tests/cassettes/test_get_access_information.json
tests/cassettes/test_get_access_information_with_invalid_code.json
@@ -123,6 +125,7 @@ tests/cassettes/test_get_contributors_private.json
tests/cassettes/test_get_contributors_public.json
tests/cassettes/test_get_contributors_public_exception.json
tests/cassettes/test_get_controversial.json
+tests/cassettes/test_get_edited_oauth.json
tests/cassettes/test_get_flair_list.json
tests/cassettes/test_get_front_page.json
tests/cassettes/test_get_hidden.json
@@ -165,6 +168,7 @@ tests/cassettes/test_get_unread_update_has_mail.json
tests/cassettes/test_get_upvoted_and_downvoted.json
tests/cassettes/test_get_wiki_page.json
tests/cassettes/test_get_wiki_pages.json
+tests/cassettes/test_hide_oauth.json
tests/cassettes/test_ignore_and_unignore_reports.json
tests/cassettes/test_inbox_comment_permalink.json
tests/cassettes/test_inbox_comment_replies_are_none.json
@@ -236,6 +240,8 @@ tests/cassettes/test_send_from_subreddit.json
tests/cassettes/test_send_invalid.json
tests/cassettes/test_send_privatemessage_oauth.json
tests/cassettes/test_set_access_credentials.json
+tests/cassettes/test_set_access_credentials_with_list.json
+tests/cassettes/test_set_access_credentials_with_string.json
tests/cassettes/test_set_individuals_flair_oauth.json
tests/cassettes/test_set_settings.json
tests/cassettes/test_set_settings_oauth.json
diff --git a/praw/__init__.py b/praw/__init__.py
index d76a6ae..b07fb05 100644
--- a/praw/__init__.py
+++ b/praw/__init__.py
@@ -50,19 +50,7 @@ from update_checker import update_check
from warnings import warn_explicit
-__version__ = '3.2.1'
-
-if os.environ.get('SERVER_SOFTWARE') is not None:
- # Google App Engine information
- # https://developers.google.com/appengine/docs/python/
- PLATFORM_INFO = os.environ.get('SERVER_SOFTWARE')
-else:
- # Standard platform information
- PLATFORM_INFO = platform.platform(True)
-
-UA_STRING = '%%s PRAW/%s Python/%s %s' % (__version__,
- sys.version.split()[0],
- PLATFORM_INFO)
+__version__ = '3.3.0'
MIN_IMAGE_SIZE = 128
MAX_IMAGE_SIZE = 512000
@@ -106,6 +94,7 @@ class Config(object): # pylint: disable=R0903
'domain': 'domain/%s/',
'duplicates': 'duplicates/%s/',
'edit': 'api/editusertext/',
+ 'edited': 'r/%s/about/edited/',
'flair': 'api/flair/',
'flairconfig': 'api/flairconfig/',
'flaircsv': 'api/flaircsv/',
@@ -113,6 +102,7 @@ class Config(object): # pylint: disable=R0903
'flairselector': 'api/flairselector/',
'flairtemplate': 'api/flairtemplate/',
'friend': 'api/friend/',
+ 'friend_v1': 'api/v1/me/friends/{user}',
'friends': 'prefs/friends/',
'gild_thing': 'api/v1/gold/gild/{fullname}/',
'gild_user': 'api/v1/gold/give/{username}/',
@@ -202,6 +192,24 @@ class Config(object): # pylint: disable=R0903
'wiki_contributors': 'r/%s/about/wikicontributors/'}
WWW_PATHS = set(['authorize'])
+ @staticmethod
+ def ua_string(praw_info):
+ """Return the user-agent string.
+
+ The user-agent string contains PRAW version and platform version info.
+
+ """
+ if os.environ.get('SERVER_SOFTWARE') is not None:
+ # Google App Engine information
+ # https://developers.google.com/appengine/docs/python/
+ info = os.environ.get('SERVER_SOFTWARE')
+ else:
+ # Standard platform information
+ info = platform.platform(True).encode('ascii', 'ignore')
+
+ return '{0} PRAW/{1} Python/{2} {3}'.format(
+ praw_info, __version__, sys.version.split()[0], info)
+
def __init__(self, site_name, **kwargs):
"""Initialize PRAW's configuration."""
def config_boolean(item):
@@ -251,7 +259,7 @@ class Config(object): # pylint: disable=R0903
self.refresh_token = obj.get('oauth_refresh_token') or None
self.store_json_result = config_boolean(obj.get('store_json_result'))
- if 'short_domain' in obj:
+ if 'short_domain' in obj and obj['short_domain']:
self._short_domain = 'http://' + obj['short_domain']
else:
self._short_domain = None
@@ -332,7 +340,7 @@ class BaseReddit(object):
**kwargs)
self.handler = handler or DefaultHandler()
self.http = Session()
- self.http.headers['User-Agent'] = UA_STRING % user_agent
+ self.http.headers['User-Agent'] = self.config.ua_string(user_agent)
self.http.validate_certs = self.config.validate_certs
# This `Session` object is only used to store request information that
@@ -605,6 +613,12 @@ class BaseReddit(object):
hook = self._json_reddit_objecter if as_objects else None
# Request url just needs to be available for the objecter to use
self._request_url = url # pylint: disable=W0201
+
+ if response == '':
+ # Some of the v1 urls don't return anything, even when they're
+ # successful.
+ return response
+
data = json.loads(response, object_hook=hook)
delattr(self, '_request_url')
# Update the modhash
@@ -690,7 +704,9 @@ class OAuth2Reddit(BaseReddit):
``redirect_uri``.
"""
- return all((self.client_id, self.client_secret, self.redirect_uri))
+ return all((self.client_id is not None,
+ self.client_secret is not None,
+ self.redirect_uri is not None))
@decorators.require_oauth
def refresh_access_information(self, refresh_token):
@@ -1206,7 +1222,7 @@ class AuthenticatedReddit(OAuth2Reddit, UnauthenticatedReddit):
elif self._authentication:
return 'LoggedIn reddit session (user: {0})'.format(self.user)
else:
- return 'Unauthenticated reddit sesssion'
+ return 'Unauthenticated reddit session'
def _url_update(self, url):
# When getting posts from a multireddit owned by the authenticated
@@ -1314,6 +1330,12 @@ class AuthenticatedReddit(OAuth2Reddit, UnauthenticatedReddit):
data = {'r': six.text_type(subreddit), 'link': link}
return self.request_json(self.config['flairselector'], data=data)
+ @decorators.restrict_access(scope='read', login=True)
+ def get_friends(self, **params):
+ """Return a UserList of Redditors with whom the user is friends."""
+ url = self.config['friends']
+ return self.request_json(url, params=params)[0]
+
@decorators.restrict_access(scope='identity', oauth_only=True)
def get_me(self):
"""Return a LoggedInRedditor object.
@@ -1387,7 +1409,8 @@ class AuthenticatedReddit(OAuth2Reddit, UnauthenticatedReddit):
pswd = password or self.config.pswd
if not pswd:
import getpass
- pswd = getpass.getpass('Password for %s: ' % user)
+ pswd = getpass.getpass('Password for {0}: '.format(user)
+ .encode('ascii', 'ignore'))
data = {'passwd': pswd,
'user': user}
@@ -1469,6 +1492,10 @@ class AuthenticatedReddit(OAuth2Reddit, UnauthenticatedReddit):
identity scopes
"""
+ if isinstance(scope, (list, tuple)):
+ scope = set(scope)
+ elif isinstance(scope, six.string_types):
+ scope = set(scope.split())
if not isinstance(scope, set):
raise TypeError('`scope` parameter must be a set')
self.clear_authentication()
@@ -1841,7 +1868,7 @@ class ModLogMixin(AuthenticatedReddit):
"""Return a get_content generator for moderation log items.
:param subreddit: Either a Subreddit object or the name of the
- subreddit to return the flair list for.
+ subreddit to return the modlog for.
:param mod: If given, only return the actions made by this moderator.
Both a moderator name or Redditor object can be used here.
:param action: If given, only return entries for the specified action.
@@ -1897,7 +1924,7 @@ class ModOnlyMixin(AuthenticatedReddit):
"""
Return a get_content generator of contributors for the given subreddit.
- If it's a public subreddit, then user/pswd authentication as a
+ If it's a public subreddit, then authentication as a
moderator of the subreddit is required. For protected/private
subreddits only access is required. See issue #246.
@@ -1919,6 +1946,21 @@ class ModOnlyMixin(AuthenticatedReddit):
return decorator(get_contributors_helper)(self, subreddit)
return get_contributors_helper(self, subreddit)
+ @decorators.restrict_access(scope='read', mod=True)
+ def get_edited(self, subreddit='mod', *args, **kwargs):
+ """Return a get_content generator of edited items.
+
+ :param subreddit: Either a Subreddit object or the name of the
+ subreddit to return the edited items for. Defaults to `mod` which
+ includes items for all the subreddits you moderate.
+
+ The additional parameters are passed directly into
+ :meth:`.get_content`. Note: the `url` parameter cannot be altered.
+
+ """
+ return self.get_content(self.config['edited'] %
+ six.text_type(subreddit), *args, **kwargs)
+
@decorators.restrict_access(scope='privatemessages', mod=True)
def get_mod_mail(self, subreddit='mod', *args, **kwargs):
"""Return a get_content generator for moderator messages.
@@ -1936,10 +1978,10 @@ class ModOnlyMixin(AuthenticatedReddit):
@decorators.restrict_access(scope='read', mod=True)
def get_mod_queue(self, subreddit='mod', *args, **kwargs):
- """Return a get_content_generator for the moderator queue.
+ """Return a get_content generator for the moderator queue.
:param subreddit: Either a Subreddit object or the name of the
- subreddit to return the flair list for. Defaults to `mod` which
+ subreddit to return the modqueue for. Defaults to `mod` which
includes items for all the subreddits you moderate.
The additional parameters are passed directly into
@@ -1951,10 +1993,10 @@ class ModOnlyMixin(AuthenticatedReddit):
@decorators.restrict_access(scope='read', mod=True)
def get_reports(self, subreddit='mod', *args, **kwargs):
- """Return a get_content generator of reported submissions.
+ """Return a get_content generator of reported items.
:param subreddit: Either a Subreddit object or the name of the
- subreddit to return the flair list for. Defaults to `mod` which
+ subreddit to return the reported items. Defaults to `mod` which
includes items for all the subreddits you moderate.
The additional parameters are passed directly into
@@ -1969,8 +2011,8 @@ class ModOnlyMixin(AuthenticatedReddit):
"""Return a get_content generator of spam-filtered items.
:param subreddit: Either a Subreddit object or the name of the
- subreddit to return the flair list for. Defaults to `mod` which
- includes items for all the subreddits you moderate.
+ subreddit to return the spam-filtered items for. Defaults to `mod`
+ which includes items for all the subreddits you moderate.
The additional parameters are passed directly into
:meth:`.get_content`. Note: the `url` parameter cannot be altered.
@@ -1988,11 +2030,11 @@ class ModOnlyMixin(AuthenticatedReddit):
@decorators.restrict_access(scope='read', mod=True)
def get_unmoderated(self, subreddit='mod', *args, **kwargs):
- """Return a get_content generator of unmoderated items.
+ """Return a get_content generator of unmoderated submissions.
:param subreddit: Either a Subreddit object or the name of the
- subreddit to return the flair list for. Defaults to `mod` which
- includes items for all the subreddits you moderate.
+ subreddit to return the unmoderated submissions for. Defaults to
+ `mod` which includes items for all the subreddits you moderate.
The additional parameters are passed directly into
:meth:`.get_content`. Note: the `url` parameter cannot be altered.
@@ -2480,7 +2522,10 @@ class ReportMixin(AuthenticatedReddit):
data = {'id': thing_id,
'executed': method}
response = self.request_json(self.config[method], data=data)
- self.evict(urljoin(self.user._url, 'hidden')) # pylint: disable=W0212
+
+ if self.user is not None:
+ self.evict(urljoin(self.user._url, # pylint: disable=W0212
+ 'hidden'))
return response
def unhide(self, thing_id):
diff --git a/praw/errors.py b/praw/errors.py
index f41a6f6..b869f78 100644
--- a/praw/errors.py
+++ b/praw/errors.py
@@ -41,12 +41,14 @@ class ClientException(PRAWException):
"""Base exception class for errors that don't involve the remote API."""
- def __init__(self, message):
+ def __init__(self, message=None):
"""Construct a ClientException.
- :params message: The error message to display.
+ :param message: The error message to display.
"""
+ if not message:
+ message = 'Clientside error'
super(ClientException, self).__init__()
self.message = message
@@ -68,7 +70,6 @@ class OAuthScopeRequired(ClientException):
:param function: The function that requires a scope.
:param scope: The scope required for the function.
-
:param message: A custom message to associate with the
exception. Default: `function` requires the OAuth2 scope `scope`
@@ -136,8 +137,9 @@ class ModeratorRequired(LoginRequired):
:param function: The function that requires moderator access.
"""
- msg = '`{0}` requires a moderator of the subreddit'.format(function)
- super(ModeratorRequired, self).__init__(msg)
+ message = ('`{0}` requires a moderator '
+ 'of the subreddit').format(function)
+ super(ModeratorRequired, self).__init__(message)
class ModeratorOrScopeRequired(LoginOrScopeRequired, ModeratorRequired):
@@ -176,15 +178,22 @@ class HTTPException(PRAWException):
"""Base class for HTTP related exceptions."""
- def __init__(self, _raw):
- """Construct a ClientException.
+ def __init__(self, _raw, message=None):
+ """Construct a HTTPException.
:params _raw: The internal request library response object. This object
is mapped to attribute `_raw` whose format may change at any time.
"""
+ if not message:
+ message = 'HTTP error'
super(HTTPException, self).__init__()
self._raw = _raw
+ self.message = message
+
+ def __str__(self):
+ """Return the message of the error."""
+ return self.message
class Forbidden(HTTPException):
@@ -203,6 +212,10 @@ class InvalidComment(PRAWException):
ERROR_TYPE = 'DELETED_COMMENT'
+ def __str__(self):
+ """Return the message of the error."""
+ return self.ERROR_TYPE
+
class InvalidSubmission(PRAWException):
@@ -210,6 +223,10 @@ class InvalidSubmission(PRAWException):
ERROR_TYPE = 'DELETED_LINK'
+ def __str__(self):
+ """Return the message of the error."""
+ return self.ERROR_TYPE
+
class InvalidSubreddit(PRAWException):
@@ -217,23 +234,34 @@ class InvalidSubreddit(PRAWException):
ERROR_TYPE = 'SUBREDDIT_NOEXIST'
+ def __str__(self):
+ """Return the message of the error."""
+ return self.ERROR_TYPE
+
class RedirectException(PRAWException):
"""Raised when a redirect response occurs that is not expected."""
- def __init__(self, request_url, response_url):
+ def __init__(self, request_url, response_url, message=None):
"""Construct a RedirectException.
:param request_url: The url requested.
:param response_url: The url being redirected to.
+ :param message: A custom message to associate with the exception.
"""
- super(RedirectException, self).__init__(
- 'Unexpected redirect from {0} to {1}'
- .format(request_url, response_url))
+ if not message:
+ message = ('Unexpected redirect '
+ 'from {0} to {1}').format(request_url, response_url)
+ super(RedirectException, self).__init__()
self.request_url = request_url
self.response_url = response_url
+ self.message = message
+
+ def __str__(self):
+ """Return the message of the error."""
+ return self.message
class OAuthException(PRAWException):
diff --git a/praw/helpers.py b/praw/helpers.py
index 9d55d87..42f9762 100644
--- a/praw/helpers.py
+++ b/praw/helpers.py
@@ -24,6 +24,7 @@ from __future__ import unicode_literals
import six
import sys
import time
+from collections import deque
from functools import partial
from timeit import default_timer as timer
from praw.errors import HTTPException
@@ -129,7 +130,7 @@ def _stream_generator(get_function, limit=None, verbosity=1):
start = timer()
try:
i = None
- params = {'count': count}
+ params = {'uniq': count}
count = (count + 1) % 100
if before:
params['before'] = before
@@ -235,15 +236,14 @@ def flatten_tree(tree, nested_attr='replies', depth_first=False):
rather than the default breadth-first manner.
"""
- stack = tree[:]
+ stack = deque(tree)
+ extend = stack.extend if depth_first else stack.extendleft
retval = []
while stack:
- item = stack.pop(0)
+ item = stack.popleft()
nested = getattr(item, nested_attr, None)
- if nested and depth_first:
- stack.extend(nested)
- elif nested:
- stack[0:0] = nested
+ if nested:
+ extend(nested)
retval.append(item)
return retval
diff --git a/praw/internal.py b/praw/internal.py
index 8aa67ce..1e3963c 100644
--- a/praw/internal.py
+++ b/praw/internal.py
@@ -158,10 +158,15 @@ def _prepare_request(reddit_session, url, params, data, auth, files,
# Most POST requests require adding `api_type` and `uh` to the data.
if data is True:
data = {}
- if not auth:
- data.setdefault('api_type', 'json')
- if reddit_session.modhash:
- data.setdefault('uh', reddit_session.modhash)
+
+ if isinstance(data, dict):
+ if not auth:
+ data.setdefault('api_type', 'json')
+ if reddit_session.modhash:
+ data.setdefault('uh', reddit_session.modhash)
+ else:
+ request.headers.setdefault('Content-Type', 'application/json')
+
request.data = data
request.files = files
return request
diff --git a/praw/objects.py b/praw/objects.py
index f1e27f4..51ea08a 100644
--- a/praw/objects.py
+++ b/praw/objects.py
@@ -137,11 +137,13 @@ class RedditContentObject(object):
# b) The object is not a WikiPage and the reddit_session has the
# `read` scope.
prev_use_oauth = self.reddit_session._use_oauth
- self.reddit_session._use_oauth = (
- isinstance(self, WikiPage) and
- self.reddit_session.has_scope('wikiread')) or \
- (not isinstance(self, WikiPage) and
- self.reddit_session.has_scope('read'))
+
+ wiki_page = isinstance(self, WikiPage)
+ scope = self.reddit_session.has_scope
+
+ self.reddit_session._use_oauth = wiki_page and scope('wikiread') or \
+ not wiki_page and scope('read')
+
try:
params = {'uniq': self._uniq} if self._uniq else {}
response = self.reddit_session.request_json(
@@ -355,7 +357,6 @@ class Hideable(RedditContentObject):
"""Interface for objects that can be hidden."""
- @restrict_access(scope='report')
def hide(self, _unhide=False):
"""Hide object in the context of the logged in user.
@@ -433,6 +434,11 @@ class Refreshable(RedditContentObject):
automatically be refreshed serverside. Refreshing a submission will
also refresh all its comments.
+ In the rare case of a submissions's comment[0] being deleted or
+ removed in between its original retrieval and refresh, or
+ inconsistencies between different endpoints resulting in this,
+ an IndexError will be thrown.
+
"""
unique = self.reddit_session._unique_count # pylint: disable=W0212
self.reddit_session._unique_count += 1 # pylint: disable=W0212
@@ -650,7 +656,14 @@ class Comment(Editable, Gildable, Inboxable, Moderatable, Refreshable,
@property
def replies(self):
- """Return a list of the comment replies to this comment."""
+ """Return a list of the comment replies to this comment.
+
+ If the comment is not from a submission, :meth:`replies` will
+ always be an empty list unless you call :meth:`refresh()
+ before calling :meth:`replies` due to a limitation in
+ reddit's API.
+
+ """
if self._replies is None or not self._has_fetched_replies:
response = self.reddit_session.request_json(self._fast_permalink)
if not response[1]['data']['children']:
@@ -841,14 +854,38 @@ class Redditor(Gildable, Messageable, Refreshable):
self._case_name = self.name
self.name = tmp
- def friend(self):
+ @restrict_access(scope='subscribe')
+ def friend(self, note=None, _unfriend=False):
"""Friend the user.
+ :param note: A personal note about the user. Requires reddit Gold.
+ :param _unfriend: Unfriend the user. Please use :meth:`unfriend`
+ instead of setting this parameter manually.
+
:returns: The json response from the server.
"""
self.reddit_session.evict(self.reddit_session.config['friends'])
- return _modify_relationship('friend')(self.reddit_session.user, self)
+
+ # Requests through password auth use /api/friend
+ # Requests through oauth use /api/v1/me/friends/%username%
+ if not self.reddit_session.is_oauth_session():
+ modifier = _modify_relationship('friend', unlink=_unfriend)
+ data = {'note': note} if note else {}
+ return modifier(self.reddit_session.user, self, **data)
+
+ url = self.reddit_session.config['friend_v1'].format(user=self.name)
+ # This endpoint wants the data to be a string instead of an actual
+ # dictionary, although it is not required to have any content for adds.
+ # Unfriending does require the 'id' key.
+ if _unfriend:
+ data = {'id': self.name}
+ else:
+ # We cannot send a null or empty note string.
+ data = {'note': note} if note else {}
+ data = dumps(data)
+ method = 'DELETE' if _unfriend else 'PUT'
+ return self.reddit_session.request_json(url, data=data, method=method)
def get_disliked(self, *args, **kwargs):
"""Return a listing of the Submissions the user has downvoted.
@@ -879,6 +916,20 @@ class Redditor(Gildable, Messageable, Refreshable):
kwargs['_use_oauth'] = self.reddit_session.is_oauth_session()
return _get_redditor_listing('downvoted')(self, *args, **kwargs)
+ @restrict_access(scope='mysubreddits')
+ def get_friend_info(self):
+ """Return information about this friend, including personal notes.
+
+ The personal note can be added or overwritten with :meth:friend, but
+ only if the user has reddit Gold.
+
+ :returns: The json response from the server.
+
+ """
+ url = self.reddit_session.config['friend_v1'].format(user=self.name)
... 559 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/praw.git
More information about the Python-modules-commits
mailing list