From 2f4d3d92c5031574347176f6589fc8fe98b4aaba Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 21 Sep 2023 22:26:44 -0700 Subject: [PATCH 01/12] Bare bones implementation --- praw/config.py | 1 + praw/models/auth.py | 8 ++++++-- praw/reddit.py | 20 ++++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/praw/config.py b/praw/config.py index 5c0b6b3d5..f1a4c691e 100644 --- a/praw/config.py +++ b/praw/config.py @@ -119,6 +119,7 @@ def _fetch_or_not_set(self, key): # noqa: ANN001 def _initialize_attributes(self): # noqa: ANN001 self._short_url = self._fetch_default("short_url") or self.CONFIG_NOT_SET + self.window_size = self._fetch_default("window_size", default=600) self.check_for_async = self._config_boolean( self._fetch_default("check_for_async", default=True) ) diff --git a/praw/models/auth.py b/praw/models/auth.py index 9385a051c..8e65c9370 100644 --- a/praw/models/auth.py +++ b/praw/models/auth.py @@ -52,7 +52,10 @@ def authorize(self, code: str) -> str | None: authenticator = self._reddit._read_only_core._authorizer._authenticator authorizer = Authorizer(authenticator) authorizer.authorize(code) - authorized_session = session(authorizer) + authorized_session = session( + authorizer=authorizer, + window_size=self.config.window_size + ) self._reddit._core = self._reddit._authorized_core = authorized_session return authorizer.refresh_token @@ -77,7 +80,8 @@ def implicit(self, *, access_token: str, expires_in: int, scope: str) -> None: if not isinstance(authenticator, UntrustedAuthenticator): raise InvalidImplicitAuth implicit_session = session( - ImplicitAuthorizer(authenticator, access_token, expires_in, scope) + authorizer=ImplicitAuthorizer(authenticator, access_token, expires_in, scope), + window_size=self.config.window_size ) self._reddit._core = self._reddit._authorized_core = implicit_session diff --git a/praw/reddit.py b/praw/reddit.py index ab3e0f200..d53865935 100644 --- a/praw/reddit.py +++ b/praw/reddit.py @@ -546,7 +546,10 @@ def _prepare_common_authorizer(self, authenticator): # noqa: ANN001 else: self._core = self._read_only_core return - self._core = self._authorized_core = session(authorizer) + self._core = self._authorized_core = session( + authorizer=authorizer, + window_size=self.config.window_size + ) def _prepare_objector(self): # noqa: ANN001 mappings = { @@ -622,13 +625,19 @@ def _prepare_trusted_prawcore(self, requestor): # noqa: ANN001 self.config.redirect_uri, ) read_only_authorizer = ReadOnlyAuthorizer(authenticator) - self._read_only_core = session(read_only_authorizer) + self._read_only_core = session( + authorizer=read_only_authorizer, + window_size=self.config.window_size + ) if self.config.username and self.config.password: script_authorizer = ScriptAuthorizer( authenticator, self.config.username, self.config.password ) - self._core = self._authorized_core = session(script_authorizer) + self._core = self._authorized_core = session( + authorizer=script_authorizer, + window_size=self.config.window_size + ) else: self._prepare_common_authorizer(authenticator) @@ -637,7 +646,10 @@ def _prepare_untrusted_prawcore(self, requestor): # noqa: ANN001 requestor, self.config.client_id, self.config.redirect_uri ) read_only_authorizer = DeviceIDAuthorizer(authenticator) - self._read_only_core = session(read_only_authorizer) + self._read_only_core = session( + authorizer=read_only_authorizer, + window_size=self.config.window_size + ) self._prepare_common_authorizer(authenticator) @_deprecate_args("id", "url") From c5fa92e95df369f30b9f0914a708ab15663e3fed Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:24:04 -0700 Subject: [PATCH 02/12] Fix black --- praw/config.py | 2 +- praw/models/auth.py | 7 ++++--- praw/reddit.py | 12 ++++-------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/praw/config.py b/praw/config.py index f1a4c691e..d0d00ecb8 100644 --- a/praw/config.py +++ b/praw/config.py @@ -119,7 +119,6 @@ def _fetch_or_not_set(self, key): # noqa: ANN001 def _initialize_attributes(self): # noqa: ANN001 self._short_url = self._fetch_default("short_url") or self.CONFIG_NOT_SET - self.window_size = self._fetch_default("window_size", default=600) self.check_for_async = self._config_boolean( self._fetch_default("check_for_async", default=True) ) @@ -132,6 +131,7 @@ def _initialize_attributes(self): # noqa: ANN001 self.warn_additional_fetch_params = self._config_boolean( self._fetch_default("warn_additional_fetch_params", default=True) ) + self.window_size = self._fetch_default("window_size", default=600) self.kinds = { x: self._fetch(f"{x}_kind") for x in [ diff --git a/praw/models/auth.py b/praw/models/auth.py index 8e65c9370..350f923db 100644 --- a/praw/models/auth.py +++ b/praw/models/auth.py @@ -53,8 +53,7 @@ def authorize(self, code: str) -> str | None: authorizer = Authorizer(authenticator) authorizer.authorize(code) authorized_session = session( - authorizer=authorizer, - window_size=self.config.window_size + authorizer=authorizer, window_size=self.config.window_size ) self._reddit._core = self._reddit._authorized_core = authorized_session return authorizer.refresh_token @@ -80,7 +79,9 @@ def implicit(self, *, access_token: str, expires_in: int, scope: str) -> None: if not isinstance(authenticator, UntrustedAuthenticator): raise InvalidImplicitAuth implicit_session = session( - authorizer=ImplicitAuthorizer(authenticator, access_token, expires_in, scope), + authorizer=ImplicitAuthorizer( + authenticator, access_token, expires_in, scope + ), window_size=self.config.window_size ) self._reddit._core = self._reddit._authorized_core = implicit_session diff --git a/praw/reddit.py b/praw/reddit.py index d53865935..48ac5b863 100644 --- a/praw/reddit.py +++ b/praw/reddit.py @@ -547,8 +547,7 @@ def _prepare_common_authorizer(self, authenticator): # noqa: ANN001 self._core = self._read_only_core return self._core = self._authorized_core = session( - authorizer=authorizer, - window_size=self.config.window_size + authorizer=authorizer, window_size=self.config.window_size ) def _prepare_objector(self): # noqa: ANN001 @@ -626,8 +625,7 @@ def _prepare_trusted_prawcore(self, requestor): # noqa: ANN001 ) read_only_authorizer = ReadOnlyAuthorizer(authenticator) self._read_only_core = session( - authorizer=read_only_authorizer, - window_size=self.config.window_size + authorizer=read_only_authorizer, window_size=self.config.window_size ) if self.config.username and self.config.password: @@ -635,8 +633,7 @@ def _prepare_trusted_prawcore(self, requestor): # noqa: ANN001 authenticator, self.config.username, self.config.password ) self._core = self._authorized_core = session( - authorizer=script_authorizer, - window_size=self.config.window_size + authorizer=script_authorizer, window_size=self.config.window_size ) else: self._prepare_common_authorizer(authenticator) @@ -647,8 +644,7 @@ def _prepare_untrusted_prawcore(self, requestor): # noqa: ANN001 ) read_only_authorizer = DeviceIDAuthorizer(authenticator) self._read_only_core = session( - authorizer=read_only_authorizer, - window_size=self.config.window_size + authorizer=read_only_authorizer, window_size=self.config.window_size ) self._prepare_common_authorizer(authenticator) From 77101333591a6b1cb5034e1c171c599ab9adc595 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:35:23 -0700 Subject: [PATCH 03/12] Add doc note here --- docs/getting_started/configuration/options.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/getting_started/configuration/options.rst b/docs/getting_started/configuration/options.rst index 0d12a15ad..7a7f7454f 100644 --- a/docs/getting_started/configuration/options.rst +++ b/docs/getting_started/configuration/options.rst @@ -72,6 +72,7 @@ order to successfully access a third-party Reddit site: (default: ``t3_``). :subreddit_kind: The type prefix for subreddits on the :class:`.Reddit` instance (default: ``t5_``). +:window_size: The number of seconds between rate limit resets (default: 600). .. _misc_options: From 30019104f91e5056d5ad42bbb8cb288fee307b58 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:42:52 -0700 Subject: [PATCH 04/12] Black again --- praw/models/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/praw/models/auth.py b/praw/models/auth.py index 350f923db..a928eb332 100644 --- a/praw/models/auth.py +++ b/praw/models/auth.py @@ -82,7 +82,7 @@ def implicit(self, *, access_token: str, expires_in: int, scope: str) -> None: authorizer=ImplicitAuthorizer( authenticator, access_token, expires_in, scope ), - window_size=self.config.window_size + window_size=self.config.window_size, ) self._reddit._core = self._reddit._authorized_core = implicit_session From e4bb2ec49a8694c019f193957263c295cc28ac52 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:45:46 -0700 Subject: [PATCH 05/12] Update logging docs --- docs/getting_started/logging.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/getting_started/logging.rst b/docs/getting_started/logging.rst index e91dd30cf..ee6d4e7f3 100644 --- a/docs/getting_started/logging.rst +++ b/docs/getting_started/logging.rst @@ -10,22 +10,25 @@ Add the following to your code to log everything available: import logging - handler = logging.StreamHandler() - handler.setLevel(logging.DEBUG) + stream_handler = logging.StreamHandler() + stream_handler.setLevel(logging.DEBUG) + file_handler = logging.handlers.RotatingFileHandler('praw_log.txt', maxBytes=1024*1024*16, backupCount=5) + file_handler.setLevel(logging.DEBUG) for logger_name in ("praw", "prawcore"): logger = logging.getLogger(logger_name) logger.setLevel(logging.DEBUG) - logger.addHandler(handler) + logger.addHandler(stream_handler) + logger.addHandler(file_handler) When properly configured, HTTP requests that are issued should produce output similar to the following: .. code-block:: text - Fetching: GET https://oauth.reddit.com/api/v1/me + Fetching: GET https://oauth.reddit.com/api/v1/me at 1691743155.4952002 Data: None Params: {'raw_json': 1} - Response: 200 (876 bytes) + Response: 200 (876 bytes) (rst-45:rem-892:used-104 ratelimit) at 1691743156.3847592 Furthermore, any API ratelimits from POST actions that are handled will produce a log entry with a message similar to the following message: From 12ee566c646e15bac362fa506f7bf9caad2ff805 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:48:45 -0700 Subject: [PATCH 06/12] Add to changelog --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1320efeea..e140a0caa 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,6 +12,9 @@ Unreleased templates. - :meth:`~.SubredditRedditorFlairTemplates.reorder` to reorder a subreddit's redditor flair templates. +- :class:`.Reddit` has a new configurable parameter, ``window_size``. This tells PRAW + how long reddit's rate limit window is. This defaults to 600 seconds and shouldn't + need to be changed unless reddit changes the size of their rate limit window. 7.7.1 (2023/07/11) ------------------ From c021795875c62d7555b73b617bd240927cd84a84 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:50:29 -0700 Subject: [PATCH 07/12] Black --- docs/getting_started/logging.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/getting_started/logging.rst b/docs/getting_started/logging.rst index ee6d4e7f3..cf628fbdd 100644 --- a/docs/getting_started/logging.rst +++ b/docs/getting_started/logging.rst @@ -12,7 +12,9 @@ Add the following to your code to log everything available: stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.DEBUG) - file_handler = logging.handlers.RotatingFileHandler('praw_log.txt', maxBytes=1024*1024*16, backupCount=5) + file_handler = logging.handlers.RotatingFileHandler( + 'praw_log.txt', maxBytes=1024*1024*16, backupCount=5 + ) file_handler.setLevel(logging.DEBUG) for logger_name in ("praw", "prawcore"): logger = logging.getLogger(logger_name) From 1c4590b08a66f0bee4d6d8c1758d4381684a6c28 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Fri, 22 Sep 2023 20:52:28 -0700 Subject: [PATCH 08/12] How about this --- docs/getting_started/logging.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/logging.rst b/docs/getting_started/logging.rst index cf628fbdd..62f5a9c9f 100644 --- a/docs/getting_started/logging.rst +++ b/docs/getting_started/logging.rst @@ -13,7 +13,7 @@ Add the following to your code to log everything available: stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.DEBUG) file_handler = logging.handlers.RotatingFileHandler( - 'praw_log.txt', maxBytes=1024*1024*16, backupCount=5 + "praw_log.txt", maxBytes=1024 * 1024 * 16, backupCount=5 ) file_handler.setLevel(logging.DEBUG) for logger_name in ("praw", "prawcore"): From ba1b87d76cf721c6a52f1500bab28ab51d3a0575 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Sun, 22 Sep 2024 17:39:07 -0700 Subject: [PATCH 09/12] CR fixes --- CHANGES.rst | 3 +++ docs/getting_started/configuration/options.rst | 2 +- docs/getting_started/logging.rst | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index f655ed875..96d09c2b2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,6 +15,9 @@ Unreleased - :func:`.stream_generator` now accepts the ``continue_after_id`` parameter, which starts the stream after a given item ID. - Support for new share URL format created from Reddit's mobile apps. +- :class:`.Reddit` has a new configurable parameter, ``window_size``. This tells PRAW + how long reddit's rate limit window is. This defaults to 600 seconds and shouldn't + need to be changed unless reddit changes the size of their rate limit window. **Fixed** diff --git a/docs/getting_started/configuration/options.rst b/docs/getting_started/configuration/options.rst index 7a7f7454f..c99a4051c 100644 --- a/docs/getting_started/configuration/options.rst +++ b/docs/getting_started/configuration/options.rst @@ -72,7 +72,6 @@ order to successfully access a third-party Reddit site: (default: ``t3_``). :subreddit_kind: The type prefix for subreddits on the :class:`.Reddit` instance (default: ``t5_``). -:window_size: The number of seconds between rate limit resets (default: 600). .. _misc_options: @@ -98,6 +97,7 @@ These are options that do not belong in another category, but still play a part throwing an exception. :warn_comment_sort: When ``true``, log a warning when the ``comment_sort`` attribute of a submission is updated after ``_fetch()`` has been called (default: ``true``). +:window_size: The number of seconds between rate limit resets (default: 600). .. _custom_options: diff --git a/docs/getting_started/logging.rst b/docs/getting_started/logging.rst index 62f5a9c9f..0dc0d7e84 100644 --- a/docs/getting_started/logging.rst +++ b/docs/getting_started/logging.rst @@ -6,6 +6,19 @@ you have to configure and enable logging. Add the following to your code to log everything available: +.. code-block:: python + + import logging + + handler = logging.StreamHandler() + handler.setLevel(logging.DEBUG) + for logger_name in ("praw", "prawcore"): + logger = logging.getLogger(logger_name) + logger.setLevel(logging.DEBUG) + logger.addHandler(handler) + +Or you can use the following to write the logs to a file for longer running bots or scripts when you need to look back at what the bot did hours ago. + .. code-block:: python import logging From b8ee8e6f87ed97d09728333a43e0902f3ba19a90 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Sun, 22 Sep 2024 18:09:54 -0700 Subject: [PATCH 10/12] Fix a test --- praw/models/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/praw/models/auth.py b/praw/models/auth.py index af6bc4ce2..287920750 100644 --- a/praw/models/auth.py +++ b/praw/models/auth.py @@ -83,7 +83,7 @@ def implicit(self, *, access_token: str, expires_in: int, scope: str): authorizer=ImplicitAuthorizer( authenticator, access_token, expires_in, scope ), - window_size=self.config.window_size, + window_size=self._reddit.config.window_size, ) self._reddit._core = self._reddit._authorized_core = implicit_session From bf79ea2046d186f7729c6201b2fd9ef0b00841d4 Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Sun, 22 Sep 2024 20:16:25 -0700 Subject: [PATCH 11/12] Fix another test --- praw/models/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/praw/models/auth.py b/praw/models/auth.py index 287920750..45274e2d2 100644 --- a/praw/models/auth.py +++ b/praw/models/auth.py @@ -54,7 +54,7 @@ def authorize(self, code: str) -> str | None: authorizer = Authorizer(authenticator) authorizer.authorize(code) authorized_session = session( - authorizer=authorizer, window_size=self.config.window_size + authorizer=authorizer, window_size=self._reddit.config.window_size ) self._reddit._core = self._reddit._authorized_core = authorized_session return authorizer.refresh_token From c6a743c4a8426f47a97e5f74da47795a215e88fb Mon Sep 17 00:00:00 2001 From: Watchful1 Date: Sun, 22 Sep 2024 20:19:02 -0700 Subject: [PATCH 12/12] Format --- docs/getting_started/logging.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/getting_started/logging.rst b/docs/getting_started/logging.rst index 0dc0d7e84..71a613c08 100644 --- a/docs/getting_started/logging.rst +++ b/docs/getting_started/logging.rst @@ -17,7 +17,8 @@ Add the following to your code to log everything available: logger.setLevel(logging.DEBUG) logger.addHandler(handler) -Or you can use the following to write the logs to a file for longer running bots or scripts when you need to look back at what the bot did hours ago. +Or you can use the following to write the logs to a file for longer running bots or +scripts when you need to look back at what the bot did hours ago. .. code-block:: python