diff --git a/AUTHORS.rst b/AUTHORS.rst index 600ef10a7..b51d601cf 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -1,5 +1,5 @@ -Maintainers -=========== +Author/Maintainer +================= - Joel Payne `@LilSpazJoekp `_ diff --git a/CHANGES.rst b/CHANGES.rst index a5db5b187..560657ac2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -51,7 +51,6 @@ Unreleased * First official Async PRAW release! - 7.1.0.pre1 (2020/07/16) ----------------------- diff --git a/README.rst b/README.rst index a92364e50..03d1184b1 100644 --- a/README.rst +++ b/README.rst @@ -20,7 +20,7 @@ Async PRAW: The Asynchronous Python Reddit API Wrapper :alt: Contributor Covenant :target: https://github.com/praw-dev/asyncpraw/blob/master/CODE_OF_CONDUCT.md -Async PRAW, an acronym for "Asynchronous Python Reddit API Wrapper", is a Python package that +Async PRAW, an abbreviation for "Asynchronous Python Reddit API Wrapper", is a Python package that allows for simple access to Reddit's API. Async PRAW aims to be easy to use and internally follows all of `Reddit's API rules `_. With Async PRAW there's no need to @@ -147,4 +147,4 @@ License Async PRAW's source (v7.1.1+) is provided under the `Simplified BSD License `_. -* Copyright (c), 2020, Joel Payne +* Copyright ©, 2020, Joel Payne diff --git a/asyncpraw/__init__.py b/asyncpraw/__init__.py index 40fb190d0..550bd2acf 100644 --- a/asyncpraw/__init__.py +++ b/asyncpraw/__init__.py @@ -1,7 +1,6 @@ -""" -Python Reddit API Wrapper. +"""Asynchronous Python Reddit API Wrapper. -Async PRAW, an acronym for "Asynchronous Python Reddit API Wrapper", is a python package that +Async PRAW, an abbreviation for "Asynchronous Python Reddit API Wrapper", is a python package that allows for simple access to reddit's API. Async PRAW aims to be as easy to use as possible and is designed to follow all of reddit's API rules. You have to give a useragent, everything else is handled by Async PRAW so you needn't worry about diff --git a/asyncpraw/config.py b/asyncpraw/config.py index 9ca659a6b..c0de657d5 100644 --- a/asyncpraw/config.py +++ b/asyncpraw/config.py @@ -60,7 +60,11 @@ def _load_config(cls, config_interpolation: Optional[str] = None): @property def short_url(self) -> str: - """Return the short url or raise a ClientException when not set.""" + """Return the short url. + + :raises: :class:`.ClientException` if it is not set. + + """ if self._short_url is self.CONFIG_NOT_SET: raise ClientException("No short domain specified.") return self._short_url @@ -149,7 +153,7 @@ def _initialize_attributes(self): setattr(self, attribute, conversion(getattr(self, attribute))) except ValueError: raise ValueError( - f"An incorrect config type was given for option {attribute}. The " - f"expected type is {conversion.__name__}, but the given value is " - f"{getattr(self, attribute)}." + f"An incorrect config type was given for option {attribute}. The" + f" expected type is {conversion.__name__}, but the given value is" + f" {getattr(self, attribute)}." ) diff --git a/asyncpraw/exceptions.py b/asyncpraw/exceptions.py index ac616ee59..250f23692 100644 --- a/asyncpraw/exceptions.py +++ b/asyncpraw/exceptions.py @@ -1,4 +1,4 @@ -"""PRAW exception classes. +"""Async PRAW exception classes. Includes two main exceptions: :class:`.RedditAPIException` for when something goes wrong on the server side, and :class:`.ClientException` when something @@ -32,7 +32,7 @@ def error_message(self) -> str: return error_str def __init__(self, error_type: str, message: str, field: Optional[str] = None): - """Instantiate an error item. + """Initialize an error item. :param error_type: The error type set on Reddit's end. :param message: The associated message for the error. @@ -54,7 +54,10 @@ def __eq__(self, other: Union["RedditErrorItem", List[str]]): def __repr__(self): """Return repr(self).""" - return f"{self.__class__.__name__}(error_type={self.error_type!r}, message={self.message!r}, field={self.field!r})" + return ( + f"{self.__class__.__name__}(error_type={self.error_type!r}," + f" message={self.message!r}, field={self.field!r})" + ) def __str__(self): """Get the message returned from str(self).""" @@ -65,6 +68,7 @@ class APIException(AsyncPRAWException): """Old class preserved for alias purposes. .. deprecated:: 7.0 + Class :class:`.APIException` has been deprecated in favor of :class:`.RedditAPIException`. This class will be removed in Async PRAW 8.0. """ @@ -130,10 +134,10 @@ def field(self) -> str: def _get_old_attr(self, attrname): warn( - f"Accessing attribute ``{attrname}`` through APIException is deprecated. " - f"This behavior will be removed in Async PRAW 8.0. Check out " - f"https://praw.readthedocs.io/en/latest/package_info/praw7_migration.html " - f"to learn how to migrate your code.", + f"Accessing attribute ``{attrname}`` through APIException is deprecated." + " This behavior will be removed in Async PRAW 8.0. Check out" + " https://praw.readthedocs.io/en/latest/package_info/praw7_migration.html" + " to learn how to migrate your code.", category=DeprecationWarning, stacklevel=3, ) @@ -184,8 +188,8 @@ class InvalidFlairTemplateID(ClientException): def __init__(self, template_id: str): """Initialize the class.""" super().__init__( - f"The flair template id ``{template_id}`` is invalid. If you are trying " - f"to create a flair, please use the ``add`` method." + f"The flair template id ``{template_id}`` is invalid. If you are trying to" + " create a flair, please use the ``add`` method." ) @@ -193,7 +197,7 @@ class InvalidImplicitAuth(ClientException): """Indicate exceptions where an implicit auth type is used incorrectly.""" def __init__(self): - """Instantize the class.""" + """Initialize the class.""" super().__init__("Implicit authorization can only be used with installed apps.") @@ -226,8 +230,8 @@ def __init__(self, maximum_size: int, actual: int): self.maximum_size = maximum_size self.actual = actual super().__init__( - f"The media that you uploaded was too large (maximum size is " - f"{maximum_size} bytes, uploaded {actual} bytes)" + f"The media that you uploaded was too large (maximum size is {maximum_size}" + f" bytes, uploaded {actual} bytes)" ) @@ -238,9 +242,9 @@ class WebSocketException(ClientException): def original_exception(self) -> Exception: """Access the original_exception attribute (now deprecated).""" warn( - "Accessing the attribute original_exception is deprecated. Please" - " rewrite your code in such a way that this attribute does not" - " need to be used. It will be removed in Async PRAW 8.0.", + "Accessing the attribute original_exception is deprecated. Please rewrite" + " your code in such a way that this attribute does not need to be used. It" + " will be removed in Async PRAW 8.0.", category=DeprecationWarning, stacklevel=2, ) @@ -271,11 +275,11 @@ class MediaPostFailed(WebSocketException): """Indicate exceptions where media uploads failed..""" def __init__(self): - """Instantiate MediaPostFailed.""" + """Initialize MediaPostFailed.""" super().__init__( - "The attempted media upload action has failed. Possible causes" - " include the corruption of media files. Check that the media " - "file can be opened on your local machine.", + "The attempted media upload action has failed. Possible causes include the" + " corruption of media files. Check that the media file can be opened on" + " your local machine.", None, ) diff --git a/asyncpraw/models/comment_forest.py b/asyncpraw/models/comment_forest.py index 38bcdbba0..7857418c7 100644 --- a/asyncpraw/models/comment_forest.py +++ b/asyncpraw/models/comment_forest.py @@ -46,7 +46,7 @@ def __getitem__(self, index: int): first_comment = comments[0] Alternatively, the presence of this method enables one to iterate over - all top_level comments, like so: + all top level comments, like so: .. code-block:: python @@ -103,8 +103,8 @@ def _insert_comment(self, comment): self._comments.append(comment) else: assert comment.parent_id in self._submission._comments_by_id, ( - "PRAW Error occurred. Please file a bug report and include " - "the code that caused the error." + "Async PRAW Error occurred. Please file a bug report and include the" + " code that caused the error." ) parent = self._submission._comments_by_id[comment.parent_id] parent.replies._comments.append(comment) diff --git a/asyncpraw/models/front.py b/asyncpraw/models/front.py index 5801870ae..813c8ade4 100644 --- a/asyncpraw/models/front.py +++ b/asyncpraw/models/front.py @@ -1,5 +1,5 @@ """Provide the Front class.""" -from typing import TYPE_CHECKING, AsyncGenerator, Union +from typing import TYPE_CHECKING, AsyncIterator, Union from urllib.parse import urljoin from .listing.generator import ListingGenerator @@ -18,9 +18,7 @@ def __init__(self, reddit: "Reddit"): super().__init__(reddit, _data=None) self._path = "/" - def best( - self, **generator_kwargs: Union[str, int] - ) -> AsyncGenerator[Submission, None]: + def best(self, **generator_kwargs: Union[str, int]) -> AsyncIterator[Submission]: """Return a :class:`.ListingGenerator` for best items. Additional keyword arguments are passed in the initialization of diff --git a/asyncpraw/models/helpers.py b/asyncpraw/models/helpers.py index d3953ef27..a6d754720 100644 --- a/asyncpraw/models/helpers.py +++ b/asyncpraw/models/helpers.py @@ -60,7 +60,6 @@ def info(self, ids: List[str]) -> AsyncGenerator[LiveThread, None]: .. code-block:: python ids = ["3rgnbke2rai6hen7ciytwcxadi", - "LiveUpdateEvent_sw7bubeycai6hey4ciytwamw3a", "sw7bubeycai6hey4ciytwamw3a", "t8jnufucss07"] async for thread in reddit.live.info(ids): @@ -252,7 +251,9 @@ async def create( Any keyword parameters not provided, or set explicitly to None, will take on a default value assigned by the Reddit server. - .. seealso:: :meth:`~.SubredditModeration.update` for documentation + .. seealso:: + + :meth:`~.SubredditModeration.update` for documentation of other available settings. """ diff --git a/asyncpraw/models/inbox.py b/asyncpraw/models/inbox.py index b3aaf2389..705c54431 100644 --- a/asyncpraw/models/inbox.py +++ b/asyncpraw/models/inbox.py @@ -1,5 +1,5 @@ """Provide the Front class.""" -from typing import TYPE_CHECKING, AsyncGenerator, Dict, List, Union +from typing import TYPE_CHECKING, AsyncIterator, Dict, List, Union from ..const import API_PATH from .base import AsyncPRAWBase @@ -16,7 +16,7 @@ class Inbox(AsyncPRAWBase): def all( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Union["Message", "Comment"], None]: + ) -> AsyncIterator[Union["Message", "Comment"]]: """Return a :class:`.ListingGenerator` for all inbox comments and messages. Additional keyword arguments are passed in the initialization of @@ -62,7 +62,7 @@ async def collapse(self, items: List["Message"]): def comment_replies( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Comment", None]: + ) -> AsyncIterator["Comment"]: """Return a :class:`.ListingGenerator` for comment replies. Additional keyword arguments are passed in the initialization of @@ -138,11 +138,11 @@ async def mark_unread(self, items: List[Union["Comment", "Message"]]): def mentions( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Comment", None]: + ) -> AsyncIterator["Comment"]: r"""Return a :class:`.ListingGenerator` for mentions. A mention is :class:`.Comment` in which the authorized redditor is - named in its body like ``/u/redditor_name``. + named in its body like ``u/redditor_name``. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. @@ -179,7 +179,7 @@ async def message(self, message_id: str) -> "Message": def messages( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Message", None]: + ) -> AsyncIterator["Message"]: """Return a :class:`.ListingGenerator` for inbox messages. Additional keyword arguments are passed in the initialization of @@ -197,7 +197,7 @@ def messages( def sent( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Message", None]: + ) -> AsyncIterator["Message"]: """Return a :class:`.ListingGenerator` for sent messages. Additional keyword arguments are passed in the initialization of @@ -216,7 +216,7 @@ def sent( def stream( self, **stream_options: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Union["Comment", "Message"], None]: + ) -> AsyncIterator[Union["Comment", "Message"]]: """Yield new inbox items as they become available. Items are yielded oldest first. Up to 100 historical items will @@ -236,7 +236,7 @@ def stream( def submission_replies( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Comment", None]: + ) -> AsyncIterator["Comment"]: """Return a :class:`.ListingGenerator` for submission replies. Additional keyword arguments are passed in the initialization of @@ -286,7 +286,7 @@ def unread( self, mark_read: bool = False, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Union["Comment", "Message"], None]: + ) -> AsyncIterator[Union["Comment", "Message"]]: """Return a :class:`.ListingGenerator` for unread comments and messages. :param mark_read: Marks the inbox as read (default: False). diff --git a/asyncpraw/models/listing/mixins/base.py b/asyncpraw/models/listing/mixins/base.py index 20becebdc..45bf2d070 100644 --- a/asyncpraw/models/listing/mixins/base.py +++ b/asyncpraw/models/listing/mixins/base.py @@ -1,5 +1,5 @@ """Provide the BaseListingMixin class.""" -from typing import Any, AsyncGenerator, Dict, Union +from typing import Any, AsyncIterator, Dict, Union from urllib.parse import urljoin from ...base import AsyncPRAWBase @@ -21,7 +21,11 @@ class BaseListingMixin(AsyncPRAWBase): @staticmethod def _validate_time_filter(time_filter): - """Raise :py:class:`.ValueError` if ``time_filter`` is not valid.""" + """Validate ``time_filter``. + + :raises: :py:class:`.ValueError` if ``time_filter`` is not valid. + + """ if time_filter not in BaseListingMixin.VALID_TIME_FILTERS: valid_time_filters = ", ".join(BaseListingMixin.VALID_TIME_FILTERS) raise ValueError(f"time_filter must be one of: {valid_time_filters}") @@ -30,7 +34,7 @@ def controversial( self, time_filter: str = "all", **generator_kwargs: Union[str, int, Dict[str, str]], - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for controversial submissions. :param time_filter: Can be one of: all, day, hour, month, week, year @@ -50,13 +54,13 @@ def controversial( multireddit = await reddit.multireddit("samuraisam", "programming") multireddit.controversial("day") - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.controversial("month") - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.comments.controversial("year") - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.submissions.controversial("all") subreddit = await reddit.subreddit("all") @@ -70,7 +74,7 @@ def controversial( def hot( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for hot items. Additional keyword arguments are passed in the initialization of @@ -85,13 +89,13 @@ def hot( multireddit = await reddit.multireddit("samuraisam", "programming") multireddit.hot() - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.hot() - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.comments.hot() - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.submissions.hot() subreddit = await reddit.subreddit("all") @@ -104,7 +108,7 @@ def hot( def new( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for new items. Additional keyword arguments are passed in the initialization of @@ -119,13 +123,13 @@ def new( multireddit = await reddit.multireddit("samuraisam", "programming") multireddit.new() - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.new() - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.comments.new() - redditor = await reddit.redditor("spez", lazy=True) + redditor = await reddit.redditor("spez") redditor.submissions.new() subreddit = await reddit.subreddit("all") @@ -140,7 +144,7 @@ def top( self, time_filter: str = "all", **generator_kwargs: Union[str, int, Dict[str, str]], - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for top submissions. :param time_filter: Can be one of: all, day, hour, month, week, year diff --git a/asyncpraw/models/listing/mixins/gilded.py b/asyncpraw/models/listing/mixins/gilded.py index 13683ddac..b190ae219 100644 --- a/asyncpraw/models/listing/mixins/gilded.py +++ b/asyncpraw/models/listing/mixins/gilded.py @@ -1,5 +1,5 @@ """Provide the GildedListingMixin class.""" -from typing import Any, AsyncGenerator, Dict, Union +from typing import Any, AsyncIterator, Dict, Union from urllib.parse import urljoin from ...base import AsyncPRAWBase @@ -11,7 +11,7 @@ class GildedListingMixin(AsyncPRAWBase): def gilded( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for gilded items. Additional keyword arguments are passed in the initialization of diff --git a/asyncpraw/models/listing/mixins/redditor.py b/asyncpraw/models/listing/mixins/redditor.py index de3e1e744..27aafdec1 100644 --- a/asyncpraw/models/listing/mixins/redditor.py +++ b/asyncpraw/models/listing/mixins/redditor.py @@ -1,5 +1,5 @@ """Provide the RedditorListingMixin class.""" -from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, Union +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, Union from urllib.parse import urljoin from ....util.cache import cachedproperty @@ -36,7 +36,7 @@ def comments(self) -> SubListing: r"""Provide an instance of :class:`.SubListing` for comment access. For example, to output the first line of all new comments by - ``/u/spez`` try: + ``u/spez`` try: .. code-block:: python @@ -52,7 +52,7 @@ def submissions(self) -> SubListing: """Provide an instance of :class:`.SubListing` for submission access. For example, to output the title's of top 100 of all time submissions - for ``/u/spez`` try: + for ``u/spez`` try: .. code-block:: python @@ -65,13 +65,16 @@ def submissions(self) -> SubListing: def downvoted( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for items the user has downvoted. - May raise ``asyncprawcore.Forbidden`` after issuing the request if the - user is not authorized to access the list. Note that because this - function returns a :class:`.ListingGenerator` the exception may not - occur until sometime after this function has returned. + :raises: ``asyncprawcore.Forbidden`` if the user is not authorized to access the + list. + + .. note:: + + Since this function returns a :class:`.ListingGenerator` the exception + may not occur until sometime after this function has returned. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. @@ -91,13 +94,16 @@ def downvoted( def gildings( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for items the user has gilded. - May raise ``asyncprawcore.Forbidden`` after issuing the request if the - user is not authorized to access the list. Note that because this - function returns a :class:`.ListingGenerator` the exception may not - occur until sometime after this function has returned. + :raises: ``asyncprawcore.Forbidden`` if the user is not authorized to access the + list. + + .. note:: + + Since this function returns a :class:`.ListingGenerator` the exception + may not occur until sometime after this function has returned. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. @@ -117,13 +123,16 @@ def gildings( def hidden( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for items the user has hidden. - May raise ``asyncprawcore.Forbidden`` after issuing the request if the - user is not authorized to access the list. Note that because this - function returns a :class:`.ListingGenerator` the exception may not - occur until sometime after this function has returned. + :raises: ``asyncprawcore.Forbidden`` if the user is not authorized to access the + list. + + .. note:: + + Since this function returns a :class:`.ListingGenerator` the exception + may not occur until sometime after this function has returned. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. @@ -143,13 +152,16 @@ def hidden( def saved( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for items the user has saved. - May raise ``asyncprawcore.Forbidden`` after issuing the request if the - user is not authorized to access the list. Note that because this - function returns a :class:`.ListingGenerator` the exception may not - occur until sometime after this function has returned. + :raises: ``asyncprawcore.Forbidden`` if the user is not authorized to access the + list. + + .. note:: + + Since this function returns a :class:`.ListingGenerator` the exception + may not occur until sometime after this function has returned. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. @@ -169,13 +181,16 @@ def saved( def upvoted( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Any, None]: + ) -> AsyncIterator[Any]: """Return a :class:`.ListingGenerator` for items the user has upvoted. - May raise ``asyncprawcore.Forbidden`` after issuing the request if the - user is not authorized to access the list. Note that because this - function returns a :class:`.ListingGenerator` the exception may not - occur until sometime after this function has returned. + :raises: ``asyncprawcore.Forbidden`` if the user is not authorized to access the + list. + + .. note:: + + Since this function returns a :class:`.ListingGenerator` the exception + may not occur until sometime after this function has returned. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. diff --git a/asyncpraw/models/listing/mixins/rising.py b/asyncpraw/models/listing/mixins/rising.py index 3c669f4a1..851bdf92c 100644 --- a/asyncpraw/models/listing/mixins/rising.py +++ b/asyncpraw/models/listing/mixins/rising.py @@ -1,5 +1,5 @@ """Provide the RisingListingMixin class.""" -from typing import TYPE_CHECKING, AsyncGenerator, Dict, Union +from typing import TYPE_CHECKING, AsyncIterator, Dict, Union from urllib.parse import urljoin from ...base import AsyncPRAWBase @@ -14,7 +14,7 @@ class RisingListingMixin(AsyncPRAWBase): def random_rising( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Submission", None]: + ) -> AsyncIterator["Submission"]: """Return a :class:`.ListingGenerator` for random rising submissions. Additional keyword arguments are passed in the initialization of @@ -35,7 +35,7 @@ def random_rising( def rising( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Submission", None]: + ) -> AsyncIterator["Submission"]: """Return a :class:`.ListingGenerator` for rising submissions. Additional keyword arguments are passed in the initialization of diff --git a/asyncpraw/models/listing/mixins/submission.py b/asyncpraw/models/listing/mixins/submission.py index b2f3430d9..d80f8763b 100644 --- a/asyncpraw/models/listing/mixins/submission.py +++ b/asyncpraw/models/listing/mixins/submission.py @@ -1,5 +1,5 @@ """Provide the SubmissionListingMixin class.""" -from typing import TYPE_CHECKING, AsyncGenerator, Dict, Union +from typing import TYPE_CHECKING, AsyncIterator, Dict, Union from ....const import API_PATH from ...base import AsyncPRAWBase @@ -14,7 +14,7 @@ class SubmissionListingMixin(AsyncPRAWBase): def duplicates( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Submission", None]: + ) -> AsyncIterator["Submission"]: """Return a :class:`.ListingGenerator` for the submission's duplicates. Additional keyword arguments are passed in the initialization of @@ -28,6 +28,7 @@ def duplicates( async for duplicate in submission.duplicates(): # process each duplicate + ... .. seealso:: :meth:`~.upvote` diff --git a/asyncpraw/models/listing/mixins/subreddit.py b/asyncpraw/models/listing/mixins/subreddit.py index b1b4c957f..ced4aa23b 100644 --- a/asyncpraw/models/listing/mixins/subreddit.py +++ b/asyncpraw/models/listing/mixins/subreddit.py @@ -1,5 +1,5 @@ """Provide the SubredditListingMixin class.""" -from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, Optional, Union +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, Optional, Union from urllib.parse import urljoin from ....util.cache import cachedproperty @@ -29,7 +29,7 @@ def __init__(self, subreddit: "Subreddit"): def __call__( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Comment", None]: + ) -> AsyncIterator["Comment"]: """Return a :class:`.ListingGenerator` for the Subreddit's comments. Additional keyword arguments are passed in the initialization of @@ -55,7 +55,7 @@ def comments(self) -> CommentHelper: """Provide an instance of :class:`.CommentHelper`. For example, to output the author of the 25 most recent comments of - ``/r/redditdev`` execute: + ``r/redditdev`` execute: .. code-block:: python diff --git a/asyncpraw/models/mod_action.py b/asyncpraw/models/mod_action.py index a3ab2daf5..d77403cf4 100644 --- a/asyncpraw/models/mod_action.py +++ b/asyncpraw/models/mod_action.py @@ -1,10 +1,8 @@ """Provide the ModAction class.""" -from typing import TYPE_CHECKING +from typing import Union from .base import AsyncPRAWBase - -if TYPE_CHECKING: # pragma: no cover - from .reddit.redditor import Redditor +from .reddit.redditor import Redditor class ModAction(AsyncPRAWBase): @@ -13,10 +11,8 @@ class ModAction(AsyncPRAWBase): @property def mod(self) -> "Redditor": """Return the Redditor who the action was issued by.""" - from asyncpraw.models import Redditor - return Redditor(self._reddit, name=self._mod) # pylint: disable=no-member @mod.setter - def mod(self, value: "Redditor"): + def mod(self, value: Union[str, "Redditor"]): self._mod = value # pylint: disable=attribute-defined-outside-init diff --git a/asyncpraw/models/reddit/base.py b/asyncpraw/models/reddit/base.py index f856e3c8f..9d3b41e3c 100644 --- a/asyncpraw/models/reddit/base.py +++ b/asyncpraw/models/reddit/base.py @@ -14,7 +14,7 @@ class RedditBase(AsyncPRAWBase): """Base class that represents actual Reddit objects.""" def __deepcopy__(self, memodict={}): - """Skip copying _reddit attribute when deepcopying.""" + """Skip copying ``_reddit`` attribute when preforming a deep copy.""" return type(self)( self._reddit, _data=deepcopy({k: v for k, v in self.__dict__.items() if k != "_reddit"}), @@ -40,8 +40,8 @@ def __getattr__(self, attribute: str) -> Any: """Return the value of `attribute`.""" if not attribute.startswith("_") and not self._fetched: raise AttributeError( - "{0!r} object has no attribute {1!r}. {0!r} object has not been fetched, " - "did you forget to execute '.load()'?".format( + "{0!r} object has no attribute {1!r}. {0!r} object has not been" + " fetched, did you forget to execute '.load()'?".format( self.__class__.__name__, attribute ) ) @@ -103,8 +103,8 @@ def _reset_attributes(self, *attributes): async def load(self): """Re-fetches the object. - This is used to explicitly fetch the object from reddit. This method can be used - on any :class:`.RedditBase` object. + This is used to explicitly fetch or re-fetch the object from reddit. This method + can be used on any :class:`.RedditBase` object. .. code-block:: python diff --git a/asyncpraw/models/reddit/collections.py b/asyncpraw/models/reddit/collections.py index 09119c637..ff58e9ba0 100644 --- a/asyncpraw/models/reddit/collections.py +++ b/asyncpraw/models/reddit/collections.py @@ -6,6 +6,7 @@ from ...util.cache import cachedproperty from ..base import AsyncPRAWBase from .base import RedditBase +from .redditor import Redditor from .submission import Submission from .subreddit import Subreddit @@ -163,13 +164,13 @@ def __len__(self) -> int: def __setattr__(self, attribute: str, value: Any): """Objectify author, subreddit, and sorted_links attributes.""" if attribute == "author_name": - self.author = value + self.author = Redditor(self._reddit, name=attribute) elif attribute == "sorted_links": value = self._reddit._objector.objectify(value) super().__setattr__(attribute, value) def _fetch_info(self): - return ("collection", {}, self._info_params) + return "collection", {}, self._info_params async def _fetch_data(self): name, fields, params = self._fetch_info() @@ -185,12 +186,11 @@ async def _fetch(self): # causes Reddit to return something that looks like an error # but with no content. raise ClientException( - f"Error during fetch. Check collection ID {self.collection_id!r} is " - f"correct." + f"Error during fetch. Check collection ID {self.collection_id!r} is" + " correct." ) other = type(self)(self._reddit, _data=data) - other.author_name = await self._reddit.redditor(other.author_name) self.__dict__.update(other.__dict__) self._fetched = True diff --git a/asyncpraw/models/reddit/comment.py b/asyncpraw/models/reddit/comment.py index 24cdb2036..b06aa97a4 100644 --- a/asyncpraw/models/reddit/comment.py +++ b/asyncpraw/models/reddit/comment.py @@ -13,11 +13,11 @@ UserContentMixin, ) from .redditor import Redditor +from .subreddit import Subreddit if TYPE_CHECKING: # pragma: no cover from ... import Reddit from .submission import Submission - from .subreddit import Subreddit # noqa: F401 class Comment(InboxableMixin, UserContentMixin, FullnameMixin, RedditBase): @@ -91,8 +91,8 @@ def _kind(self) -> str: def is_root(self) -> bool: """Return True when the comment is a top level comment. - .. note:: This property requires the comment to be fetched. Otherwise, an - ``AttributeError`` will be raised. + :raises: :py:class:`AttributeError` if the comment is not fetched. + """ parent_type = self.parent_id.split("_", 1)[0] return parent_type == self._reddit.config.kinds["submission"] @@ -198,13 +198,11 @@ def __setattr__( attribute = "_replies" elif attribute == "subreddit": if isinstance(value, str): - from .. import Subreddit - value = Subreddit(self._reddit, display_name=value) super().__setattr__(attribute, value) def _fetch_info(self): - return ("info", {}, {"id": self.fullname}) + return "info", {}, {"id": self.fullname} async def _fetch_data(self): name, fields, params = self._fetch_info() diff --git a/asyncpraw/models/reddit/live.py b/asyncpraw/models/reddit/live.py index ee3ca77f4..d502d0dc5 100644 --- a/asyncpraw/models/reddit/live.py +++ b/asyncpraw/models/reddit/live.py @@ -1,5 +1,5 @@ """Provide the LiveThread class.""" -from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Optional, Union from ...const import API_PATH from ...util.cache import cachedproperty @@ -26,9 +26,7 @@ def _handle_permissions(permissions): permissions = set(permissions) return ",".join(f"+{x}" for x in permissions) - def __call__( - self, - ) -> AsyncGenerator: # noqa: D202 + def __call__(self) -> AsyncIterator[Redditor]: """Return a :class:`.RedditorList` for live threads' contributors. Usage: @@ -80,9 +78,6 @@ async def invite( ): """Invite a redditor to be a contributor of the live thread. - :raises: :class:`asyncpraw.exceptions.APIException` if the invitation - already exists. - :param redditor: A redditor name (e.g., ``"spez"``) or :class:`~.Redditor` instance. :param permissions: When provided (not ``None``), permissions should @@ -90,6 +85,9 @@ async def invite( grant. An empty list ``[]`` indicates no permissions, and when not provided (``None``), indicates full permissions. + :raises: :class:`.RedditAPIException` if the invitation + already exists. + Usage: .. code-block:: python @@ -165,8 +163,9 @@ async def remove_invite(self, redditor: Union[str, Redditor]): await thread.contributor.remove_invite(redditor) await thread.contributor.remove_invite("t2_1w72") # with fullname - :seealso: :meth:`.LiveContributorRelationship.invite` to - invite a redditor to be a contributor of the live thread. + .. seealso:: + + :meth:`.LiveContributorRelationship.invite` to invite a redditor to be a contributor of the live thread. """ if isinstance(redditor, Redditor): @@ -410,7 +409,7 @@ def __init__( super().__init__(reddit, _data=_data) def _fetch_info(self): - return ("liveabout", {"id": self.id}, None) + return "liveabout", {"id": self.id}, None async def _fetch_data(self): name, fields, params = self._fetch_info() @@ -426,7 +425,7 @@ async def _fetch(self): def discussions( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Submission", None]: + ) -> AsyncIterator["Submission"]: """Get submissions linking to the thread. :param generator_kwargs: keyword arguments passed to @@ -469,7 +468,7 @@ async def report(self, type: str): # pylint: disable=redefined-builtin async def updates( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["LiveUpdate", None]: + ) -> AsyncIterator["LiveUpdate"]: """Return a :class:`.ListingGenerator` yields :class:`.LiveUpdate` s. :param generator_kwargs: keyword arguments passed to @@ -629,9 +628,7 @@ def __init__(self, live_thread: LiveThread): """ self.live_thread = live_thread - def updates( - self, **stream_options: Dict[str, Any] - ) -> AsyncGenerator["LiveUpdate", None]: + def updates(self, **stream_options: Dict[str, Any]) -> AsyncIterator["LiveUpdate"]: """Yield new updates to the live thread as they become available. :param skip_existing: Set to ``True`` to only fetch items created @@ -741,9 +738,9 @@ def __init__( update.author # "umbrae" """ if _data is not None: - # Since _data (part of JSON returned from reddit) have no - # thread ID, self._thread must be set by the caller of - # LiveUpdate(). See the code of LiveThread.updates() for example. + # Since _data (part of JSON returned from reddit) have no thread ID, + # self._thread must be set by the caller of LiveUpdate(). See the code of + # LiveThread.updates() for example. super().__init__(reddit, _data=_data, _fetched=True) elif thread_id and update_id: self.id = update_id @@ -794,11 +791,11 @@ async def remove(self): Usage: - .. code-block:: python + .. code-block:: python - thread = await reddit.live("ydwwxneu7vsa") - update = await thread.get_update("6854605a-efec-11e6-b0c7-0eafac4ff094") - await update.contrib.remove() + thread = await reddit.live("ydwwxneu7vsa") + update = await thread.get_update("6854605a-efec-11e6-b0c7-0eafac4ff094") + await update.contrib.remove() """ url = API_PATH["live_remove_update"].format(id=self.update.thread.id) @@ -815,9 +812,12 @@ async def strike(self): await update.contrib.strike() To check whether the update is stricken or not, use ``update.stricken`` - attribute. But note that accessing lazy attributes on updates - (includes ``update.stricken``) may raises ``AttributeError``. - See :class:`.LiveUpdate` for details. + attribute. + + .. note:: + + Accessing lazy attributes on updates (includes ``update.stricken``) + may raise :py:class:`AttributeError`. See :class:`.LiveUpdate` for details. """ url = API_PATH["live_strike"].format(id=self.update.thread.id) diff --git a/asyncpraw/models/reddit/mixins/__init__.py b/asyncpraw/models/reddit/mixins/__init__.py index 449971f85..8a0f6d512 100644 --- a/asyncpraw/models/reddit/mixins/__init__.py +++ b/asyncpraw/models/reddit/mixins/__init__.py @@ -203,8 +203,8 @@ async def send_removal_message( If ``type`` is "public", the new :class:`~.Comment` is returned. """ - # The API endpoint used to send removal messages is different - # for posts and comments, so the derived classes specify which one. + # The API endpoint used to send removal messages is different for posts and + # comments, so the derived classes specify which one. if self.REMOVAL_MESSAGE_API is None: raise NotImplementedError("ThingModerationMixin must be extended.") url = API_PATH[self.REMOVAL_MESSAGE_API] diff --git a/asyncpraw/models/reddit/mixins/gildable.py b/asyncpraw/models/reddit/mixins/gildable.py index 67307aef5..7fdf84047 100644 --- a/asyncpraw/models/reddit/mixins/gildable.py +++ b/asyncpraw/models/reddit/mixins/gildable.py @@ -72,7 +72,7 @@ async def award( await comment.award() submission = await reddit.submission("8dmv8z") - sawait ubmission.award() + await submission.award() To award the platinum award with the message 'Nice!' and reveal your username to the recipient do: diff --git a/asyncpraw/models/reddit/mixins/inboxable.py b/asyncpraw/models/reddit/mixins/inboxable.py index a22d68349..bcabad8e2 100644 --- a/asyncpraw/models/reddit/mixins/inboxable.py +++ b/asyncpraw/models/reddit/mixins/inboxable.py @@ -61,6 +61,7 @@ async def mark_read(self): async for message in inbox: # process unread messages + ... .. seealso:: :meth:`~.mark_unread` @@ -84,6 +85,7 @@ async def mark_unread(self): async for message in inbox: # process messages + ... .. seealso:: :meth:`~.mark_read` diff --git a/asyncpraw/models/reddit/mixins/messageable.py b/asyncpraw/models/reddit/mixins/messageable.py index 959c5110d..832c75790 100644 --- a/asyncpraw/models/reddit/mixins/messageable.py +++ b/asyncpraw/models/reddit/mixins/messageable.py @@ -16,16 +16,18 @@ async def message( message: str, from_subreddit: Optional[Union["Subreddit", str]] = None, ): - """ - Send a message to a redditor or a subreddit's moderators (mod mail). + """Send a message to a redditor or a subreddit's moderators (mod mail). :param subject: The subject of the message. :param message: The message content. - :param from_subreddit: A :class:`~.Subreddit` instance or string to - send the message from. When provided, messages are sent from - the subreddit rather than from the authenticated user. - Note that the authenticated user must be a moderator of the - subreddit and have the ``mail`` moderator permission. + :param from_subreddit: A :class:`~.Subreddit` instance or string to send the + message from. When provided, messages are sent from the subreddit rather + than from the authenticated user. + + .. note:: + + The authenticated user must be a moderator of the subreddit and have the + ``mail`` moderator permission. For example, to send a private message to ``u/spez``, try: diff --git a/asyncpraw/models/reddit/mixins/replyable.py b/asyncpraw/models/reddit/mixins/replyable.py index 686b09f3a..133cc2cc7 100644 --- a/asyncpraw/models/reddit/mixins/replyable.py +++ b/asyncpraw/models/reddit/mixins/replyable.py @@ -15,7 +15,7 @@ async def reply(self, body: str): A ``None`` value can be returned if the target is a comment or submission in a quarantined subreddit and the authenticated user has not opt-ed in to viewing the content. When this happens the - comment will be sucessfully created on Reddit and can be retried + comment will be successfully created on Reddit and can be retried by drawing the comment from the user's comment history. .. note:: Some items, such as locked submissions/comments or diff --git a/asyncpraw/models/reddit/mixins/reportable.py b/asyncpraw/models/reddit/mixins/reportable.py index fe5f6fbe8..e70f92eda 100644 --- a/asyncpraw/models/reddit/mixins/reportable.py +++ b/asyncpraw/models/reddit/mixins/reportable.py @@ -10,8 +10,8 @@ async def report(self, reason: str): :param reason: The reason for reporting. - Raises :class:`.RedditAPIException` if ``reason`` is longer than 100 - characters. + :raises: :class:`.RedditAPIException` if ``reason`` is longer than 100 + characters. Example usage: diff --git a/asyncpraw/models/reddit/modmail.py b/asyncpraw/models/reddit/modmail.py index 3687e3e57..9899f4539 100644 --- a/asyncpraw/models/reddit/modmail.py +++ b/asyncpraw/models/reddit/modmail.py @@ -142,7 +142,7 @@ def __init__( super().__init__(reddit, _data=_data) - self._info_params = {"markRead": "true"} if mark_read else None + self._info_params = {"markRead": True} if mark_read else None def _build_conversation_list(self, other_conversations): """Return a comma-separated list of conversation IDs.""" diff --git a/asyncpraw/models/reddit/more.py b/asyncpraw/models/reddit/more.py index b62a0b903..3264fa2e3 100644 --- a/asyncpraw/models/reddit/more.py +++ b/asyncpraw/models/reddit/more.py @@ -28,9 +28,9 @@ def __eq__(self, other: Union[str, "MoreComments"]) -> bool: def __lt__(self, other: "MoreComments") -> bool: """Provide a sort order on the MoreComments object.""" - # To work with heapq a "smaller" item is the one with the most comments - # We are intentionally making the biggest element the smallest element - # to turn the min-heap implementation in heapq into a max-heap. + # To work with heapq a "smaller" item is the one with the most comments. We are + # intentionally making the biggest element the smallest element to turn the + # min-heap implementation in heapq into a max-heap. return self.count > other.count def __repr__(self) -> str: @@ -50,8 +50,7 @@ async def _continue_comments(self, update): return self._comments async def _load_comment(self, comment_id): - submission_path = API_PATH["submission"].format(id=self.submission.id) - path = f"{submission_path}_/{comment_id}" + path = f"{API_PATH['submission'].format(id=self.submission.id)}_/{comment_id}" _, comments = await self._reddit.get( path, params={ diff --git a/asyncpraw/models/reddit/multi.py b/asyncpraw/models/reddit/multi.py index f4c16cbce..ad80143a1 100644 --- a/asyncpraw/models/reddit/multi.py +++ b/asyncpraw/models/reddit/multi.py @@ -159,6 +159,7 @@ async def copy(self, display_name: Optional[str] = None) -> "Multireddit": name and name as this multireddit. To copy the multireddit ``bboe/test`` with a name of ``testing``: + .. code-block:: python multireddit = await reddit.multireddit("bboe", "test") diff --git a/asyncpraw/models/reddit/poll.py b/asyncpraw/models/reddit/poll.py index 91e8fa935..01570334e 100644 --- a/asyncpraw/models/reddit/poll.py +++ b/asyncpraw/models/reddit/poll.py @@ -15,10 +15,10 @@ class PollOption(AsyncPRAWBase): poll_data = submission.poll_data - # By index -- print the first option + # By index -- print the first option print(poll_data.options[0]) - # By ID -- print the option with ID "576797" + # By ID -- print the option with ID "576797" print(poll_data.option("576797")) **Typical Attributes** @@ -110,7 +110,8 @@ def option(self, option_id: str) -> PollOption: :param option_id: The ID of a poll option, as a ``str``. :returns: The specified :class:`.PollOption`. - Raises ``KeyError`` if no option exists with the specified ID. + :raises: :py:class:`KeyError` if no option exists with the specified ID. + """ for option in self.options: if option.id == option_id: diff --git a/asyncpraw/models/reddit/redditor.py b/asyncpraw/models/reddit/redditor.py index e974c849f..796ed98d9 100644 --- a/asyncpraw/models/reddit/redditor.py +++ b/asyncpraw/models/reddit/redditor.py @@ -163,7 +163,7 @@ async def _fetch_username(self, fullname): async def _fetch_info(self): if hasattr(self, "_fullname"): self.name = await self._fetch_username(self._fullname) - return ("user_about", {"user": self.name}, None) + return "user_about", {"user": self.name}, None async def _fetch_data(self): name, fields, params = await self._fetch_info() @@ -399,7 +399,7 @@ def submissions( Keyword arguments are passed to :func:`.stream_generator`. - For example to retrieve all new submissions made by redditor + For example, to retrieve all new submissions made by redditor ``spez``, try: .. code-block:: python diff --git a/asyncpraw/models/reddit/removal_reasons.py b/asyncpraw/models/reddit/removal_reasons.py index eac9120ed..dedefe674 100644 --- a/asyncpraw/models/reddit/removal_reasons.py +++ b/asyncpraw/models/reddit/removal_reasons.py @@ -1,5 +1,5 @@ """Provide the Removal Reason class.""" -from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Optional, Union from warnings import warn from ...const import API_PATH @@ -45,7 +45,7 @@ def _warn_reason_id(reason_id_value: Optional[str], id_value: Optional[str]): warn( "Parameter ``reason_id`` is deprecated. Either use positional" ' arguments (reason_id="x" -> "x") or change the parameter ' - 'name to ``id`` (resaon_id="x" -> id="x"). The parameter will' + 'name to ``id`` (reason_id="x" -> id="x"). The parameter will' " be removed in Async PRAW 8.", category=DeprecationWarning, stacklevel=3, @@ -213,7 +213,7 @@ def __init__(self, subreddit: "Subreddit"): self.subreddit = subreddit self._reddit = subreddit._reddit - async def __aiter__(self) -> AsyncGenerator[RemovalReason, None]: + async def __aiter__(self) -> AsyncIterator[RemovalReason]: """Return a list of Removal Reasons for the subreddit. This method is used to discover all removal reasons for a @@ -256,7 +256,7 @@ async def add(self, message: str, title: str) -> RemovalReason: .. code-block:: python subreddit = await reddit.subreddit("NAME") - await subreddit.mod.removal_reasons.add(message="Foobar", "title="Test") + await subreddit.mod.removal_reasons.add(message="Foobar", title="Test") """ data = {"message": message, "title": title} diff --git a/asyncpraw/models/reddit/rules.py b/asyncpraw/models/reddit/rules.py index 9185ab86c..98ea10d0d 100644 --- a/asyncpraw/models/reddit/rules.py +++ b/asyncpraw/models/reddit/rules.py @@ -83,9 +83,9 @@ def __init__( raise ValueError("Either short_name or _data needs to be given.") if short_name: self.short_name = short_name - # Note: The subreddit parameter can be None, because the objector - # does not know this info. In that case, it is the responsibility of - # the caller to set the `subreddit` property on the returned value. + # Note: The subreddit parameter can be None, because the objector does not know + # this info. In that case, it is the responsibility of the caller to set the + # `subreddit` property on the returned value self.subreddit = subreddit super().__init__(reddit, _data=_data) @@ -169,6 +169,7 @@ async def __call__(self) -> List[Rule]: :returns: A list of instances of :class:`.Rule`. .. deprecated:: 7.1 + Use the iterator by removing the call to :class:`.SubredditRules`. For example, in order to use the iterator: @@ -179,12 +180,10 @@ async def __call__(self) -> List[Rule]: print(rule) """ warn( - "Calling SubredditRules to get a list of rules is deprecated. " - "Remove the parentheses to use the iterator. View the " - "Async PRAW documentation on how to change the code in order to use the" - "iterator (https://asyncpraw.readthedocs.io/en/latest/code_overview" - "/other/subredditrules.html#asyncpraw.models.reddit.rules." - "SubredditRules.__call__).", + "Calling SubredditRules to get a list of rules is deprecated. Remove the" + " parentheses to use the iterator. View the Async PRAW documentation on how" + " to change the code in order to use the iterator" + " (https://asyncpraw.readthedocs.io/en/latest/code_overview/other/subredditrules.html#asyncpraw.models.reddit.rules.SubredditRules.__call__).", category=DeprecationWarning, stacklevel=2, ) @@ -297,7 +296,7 @@ class RuleModeration: """ def __init__(self, rule: Rule): - """Instantize the RuleModeration class.""" + """Initialize the RuleModeration class.""" self.rule = rule async def delete(self): @@ -393,7 +392,7 @@ class SubredditRulesModeration: """ def __init__(self, subreddit_rules: SubredditRules): - """Instantize the SubredditRulesModeration class.""" + """Initialize the SubredditRulesModeration class.""" self.subreddit_rules = subreddit_rules async def add( diff --git a/asyncpraw/models/reddit/submission.py b/asyncpraw/models/reddit/submission.py index a4f569ea5..4b6053f6a 100644 --- a/asyncpraw/models/reddit/submission.py +++ b/asyncpraw/models/reddit/submission.py @@ -111,12 +111,13 @@ async def contest_mode(self, state: bool = True): (default: True). Contest mode have the following effects: - * The comment thread will default to being sorted randomly. - * Replies to top-level comments will be hidden behind - "[show replies]" buttons. - * Scores will be hidden from non-moderators. - * Scores accessed through the API (mobile apps, bots) will be - obscured to "1" for non-moderators. + + * The comment thread will default to being sorted randomly. + * Replies to top-level comments will be hidden behind "[show replies]" + buttons. + * Scores will be hidden from non-moderators. + * Scores accessed through the API (mobile apps, bots) will be obscured to + "1" for non-moderators. Example usage: @@ -181,7 +182,9 @@ async def nsfw(self): submission = await subreddit.submit("nsfw test", selftext="nsfw") await submission.mod.nsfw() - .. seealso:: :meth:`~.sfw` + .. seealso:: + + :meth:`~.sfw` """ await self.thing._reddit.post( @@ -204,7 +207,9 @@ async def set_original_content(self): submission = await subreddit.submit("oc test", selftext="original") await submission.mod.set_original_content() - .. seealso:: :meth:`.unset_original_content` + .. seealso:: + + :meth:`.unset_original_content` """ data = { @@ -229,7 +234,9 @@ async def sfw(self): submission = await reddit.submission(id="5or86n", lazy=True) await submission.mod.sfw() - .. seealso:: :meth:`~.nsfw` + .. seealso:: + + :meth:`~.nsfw` """ await self.thing._reddit.post( @@ -249,7 +256,9 @@ async def spoiler(self): submission = await reddit.submission(id="5or86n", lazy=True) await submission.mod.spoiler() - .. seealso:: :meth:`~.unspoiler` + .. seealso:: + + :meth:`~.unspoiler` """ await self.thing._reddit.post( @@ -318,7 +327,9 @@ async def unset_original_content(self): submission = await subreddit.submit("oc test", selftext="original") await submission.mod.unset_original_content() - .. seealso:: :meth:`.set_original_content` + .. seealso:: + + :meth:`.set_original_content` """ data = { @@ -340,11 +351,13 @@ async def unspoiler(self): .. code-block:: python - sub await reddit.subreddit("test") + sub = await reddit.subreddit("test") submission = await sub.submit("not spoiler", selftext="spoiler") await submission.mod.unspoiler() - .. seealso:: :meth:`~.spoiler` + .. seealso:: + + :meth:`~.spoiler` """ await self.thing._reddit.post( @@ -468,6 +481,7 @@ async def comments(self) -> CommentForest: comment_list = await comments.list() for comment in comment_list: # do stuff with comment + ... Sort order and comment limit can be set with the ``comment_sort`` and ``comment_limit`` attributes before comments are fetched, including @@ -480,6 +494,7 @@ async def comments(self) -> CommentForest: comment_list = await comments.list() for comment in comment_list: # do stuff with comment + ... .. note:: The appropriate values for ``comment_sort`` include ``confidence``, ``controversial``, ``new``, ``old``, ``q&a``, @@ -532,7 +547,7 @@ def mod(self) -> SubmissionModeration: def shortlink(self) -> str: """Return a shortlink to the submission. - For example http://redd.it/eorhm is a shortlink for + For example, https://redd.it/eorhm is a shortlink for https://www.reddit.com/r/announcements/comments/eorhm/reddit_30_less_typing/. """ @@ -647,7 +662,9 @@ async def hide(self, other_submissions: Optional[List["Submission"]] = None): submission = await reddit.submission(id="5or86n", lazy=True) await submission.hide() - .. seealso:: :meth:`~.unhide` + .. seealso:: + + :meth:`~.unhide` """ for submissions in self._chunk(other_submissions, 50): @@ -667,7 +684,9 @@ async def unhide(self, other_submissions: Optional[List["Submission"]] = None): submission = await reddit.submission(id="5or86n") await submission.unhide() - .. seealso:: :meth:`~.hide` + .. seealso:: + + :meth:`~.hide` """ for submissions in self._chunk(other_submissions, 50): @@ -713,7 +732,9 @@ async def crosspost( cross_post = await submission.crosspost(subreddit="learnprogramming", send_replies=False) - .. seealso:: :meth:`~.hide` + .. seealso:: + + :meth:`~.hide` """ if title is None: diff --git a/asyncpraw/models/reddit/subreddit.py b/asyncpraw/models/reddit/subreddit.py index 07dbe7c41..c35a2eddb 100644 --- a/asyncpraw/models/reddit/subreddit.py +++ b/asyncpraw/models/reddit/subreddit.py @@ -72,11 +72,12 @@ class Subreddit(MessageableMixin, SubredditListingMixin, FullnameMixin, RedditBa async for submission in subreddit.top("all"): print(submission) - Subreddits can be filtered from combined listings as follows. Note that - these filters are ignored by certain methods, including - :attr:`~asyncpraw.models.Subreddit.comments`, - :meth:`~asyncpraw.models.Subreddit.gilded`, and - :meth:`.SubredditStream.comments`. + Subreddits can be filtered from combined listings as follows. + + .. note:: + + These filters are ignored by certain methods, including :attr:`.comments`, :meth:`.gilded`, + and :meth:`.SubredditStream.comments`. .. code-block:: python @@ -239,7 +240,7 @@ def _kind(self) -> str: def banned(self): """Provide an instance of :class:`.SubredditRelationship`. - For example to ban a user try: + For example, to ban a user try: .. code-block:: python @@ -343,7 +344,7 @@ def flair(self): """Provide an instance of :class:`.SubredditFlair`. Use this attribute for interacting with a subreddit's flair. For - example to list all the flair for a subreddit which you have the + example, to list all the flair for a subreddit which you have the ``flair`` moderator permission on try: .. code-block:: python @@ -381,7 +382,7 @@ def mod(self): def moderator(self): """Provide an instance of :class:`.ModeratorRelationship`. - For example to add a moderator try: + For example, to add a moderator try: .. code-block:: python @@ -513,8 +514,8 @@ def stylesheet(self): subreddit = await reddit.subreddit("SUBREDDIT") stylesheet = await subreddit.stylesheet() - stylesheet += ".test{color:blue}" - await subreddit.stylesheet.update(stylesheet) + stylesheet.stylesheet += ".test{color:blue}" + await subreddit.stylesheet.update(stylesheet.stylesheet) """ return SubredditStylesheet(self) @@ -574,8 +575,15 @@ def __init__(self, reddit, display_name=None, _data=None): :param display_name: The name of the subreddit. .. note:: This class should not be initialized directly. Instead obtain - an instance via: ``await reddit.subreddit("subreddit_name")`` or lazily - ``await reddit.subreddit("subreddit_name")`` + an instance via: + + .. code-block:: python + + # to lazily load a subreddit instance + await reddit.subreddit("subreddit_name") + + # to fully load a subreddit instance + await reddit.subreddit("subreddit_name", fetch=True) """ if (display_name, _data).count(None) != 1: @@ -597,7 +605,7 @@ async def _convert_to_fancypants(self, markdown_text: str): return rte_body["output"] def _fetch_info(self): - return ("subreddit_about", {"subreddit": self}, None) + return "subreddit_about", {"subreddit": self}, None async def _fetch_data(self): name, fields, params = self._fetch_info() @@ -658,7 +666,6 @@ async def _submit_media(self, data, timeout, websocket_url=None): "Error establishing websocket connection.", ws_exception, ) - if ws_update.get("type") == "failed": raise MediaPostFailed url = ws_update["payload"]["redirect"] @@ -701,8 +708,8 @@ async def _upload_media( and mime_type.partition("/")[0] != expected_mime_prefix ): raise ClientException( - f"Expected a mimetype starting with {expected_mime_prefix!r} but got " - f"mimetype {mime_type!r} (from file extension {file_extension!r})." + f"Expected a mimetype starting with {expected_mime_prefix!r} but got" + f" mimetype {mime_type!r} (from file extension {file_extension!r})." ) img_data = {"filepath": file_name, "mimetype": mime_type} @@ -830,7 +837,7 @@ def search( For more information on building a search query see: https://www.reddit.com/wiki/search - For example to search all subreddits for ``praw`` try: + For example, to search all subreddits for ``praw`` try: .. code-block:: python @@ -859,7 +866,7 @@ async def sticky(self, number=1): :param number: Specify which sticky to return. 1 appears at the top (default: 1). - Raises ``asyncprawcore.NotFound`` if the sticky does not exist. + :raises: ``asyncprawcore.NotFound`` if the sticky does not exist. For example, to get the stickied post on the subreddit ``r/test``: @@ -924,16 +931,16 @@ async def submit( Either ``selftext`` or ``url`` can be provided, but not both. - For example to submit a URL to ``r/reddit_api_test`` do: + For example, to submit a URL to ``r/reddit_api_test`` do: .. code-block:: python - title = "PRAW documentation" + title = "Async PRAW documentation" url = "https://asyncpraw.readthedocs.io" subreddit = await reddit.subreddit("reddit_api_test") await subreddit.submit(title, url=url) - For example to submit a self post with inline media do: + For example, to submit a self post with inline media do: .. code-block:: python @@ -1049,10 +1056,9 @@ async def submit_gallery( (default: False). :returns: A :class:`.Submission` object for the newly created submission. - If ``image_path`` in ``images`` refers to a file that is not an image, PRAW will - raise a :class:`.ClientException`. + :raises: :class:`.ClientException` if ``image_path`` in ``images`` refers to a file that is not an image. - For example to submit an image gallery to ``r/reddit_api_test`` do: + For example, to submit an image gallery to ``r/reddit_api_test`` do: .. code-block:: python @@ -1170,8 +1176,7 @@ async def submit_image( :returns: A :class:`.Submission` object for the newly created submission, unless ``without_websockets`` is ``True``. - If ``image_path`` refers to a file that is not an image, Async PRAW will - raise a :class:`.ClientException`. + :raises: :class:`.ClientException` if ``image_path`` refers to a file that is not an image. .. note:: @@ -1188,7 +1193,7 @@ async def submit_image( program in a restricted network environment, or using a proxy that doesn't support WebSockets connections. - For example to submit an image to ``r/reddit_api_test`` do: + For example, to submit an image to ``r/reddit_api_test`` do: .. code-block:: python @@ -1276,14 +1281,13 @@ async def submit_poll( :returns: A :class:`~.Submission` object for the newly created submission. - For example to submit a poll to ``r/reddit_api_test`` do: + For example, to submit a poll to ``r/reddit_api_test`` do: .. code-block:: python title = "Do you like Async PRAW?" - options = ["Yes", "No"] subreddit = await reddit.subreddit("reddit_api_test") - await subreddit.submit_poll(title, selftext="", options=options, duration=3) + await subreddit.submit_poll(title, selftext="", options=["Yes", "No"], duration=3) """ data = { @@ -1360,8 +1364,7 @@ async def submit_video( :returns: A :class:`.Submission` object for the newly created submission, unless ``without_websockets`` is ``True``. - If ``video_path`` refers to a file that is not a video, Async PRAW will - raise a :class:`.ClientException`. + :raises: :class:`.ClientException` if ``video_path`` refers to a file that is not a video. .. note:: @@ -1378,7 +1381,7 @@ async def submit_video( program in a restricted network environment, or using a proxy that doesn't support WebSockets connections. - For example to submit a video to ``r/reddit_api_test`` do: + For example, to submit a video to ``r/reddit_api_test`` do: .. code-block:: python @@ -1454,9 +1457,9 @@ async def subscribe(self, other_subreddits=None): async def traffic(self): """Return a dictionary of the subreddit's traffic statistics. - Raises ``asyncprawcore.NotFound`` when the traffic stats aren't - available to the authenticated user, that is, they are not public and - the authenticated user is not a moderator of the subreddit. + :raises: ``asyncprawcore.NotFound`` when the traffic stats aren't + available to the authenticated user, that is, they are not public and + the authenticated user is not a moderator of the subreddit. The traffic method returns a dict with three keys. The keys are ``day``, ``hour`` and ``month``. Each key contains a list of lists with @@ -1563,7 +1566,7 @@ async def add(self, subreddit): await reddit.subreddit("all-redditdev-learnpython") - Raises ``asyncprawcore.NotFound`` when calling on a non-special + :raises: ``asyncprawcore.NotFound`` when calling on a non-special subreddit. """ @@ -1582,7 +1585,7 @@ async def remove(self, subreddit): :param subreddit: The subreddit to remove from the filter list. - Raises ``asyncprawcore.NotFound`` when calling on a non-special + :raises: ``asyncprawcore.NotFound`` when calling on a non-special subreddit. """ @@ -1603,7 +1606,7 @@ def link_templates(self): """Provide an instance of :class:`.SubredditLinkFlairTemplates`. Use this attribute for interacting with a subreddit's link flair - templates. For example to list all the link flair templates for a + templates. For example, to list all the link flair templates for a subreddit which you have the ``flair`` moderator permission on try: .. code-block:: python @@ -1620,7 +1623,7 @@ def templates(self): """Provide an instance of :class:`.SubredditRedditorFlairTemplates`. Use this attribute for interacting with a subreddit's flair - templates. For example to list all the flair templates for a subreddit + templates. For example, to list all the flair templates for a subreddit which you have the ``flair`` moderator permission on try: .. code-block:: python @@ -1750,8 +1753,8 @@ async def set(self, redditor, text="", css_class="", flair_template_id=None): """ if css_class and flair_template_id is not None: raise TypeError( - "Parameter `css_class` cannot be used in " - "conjunction with `flair_template_id`." + "Parameter `css_class` cannot be used in conjunction with" + " `flair_template_id`." ) data = {"name": str(redditor), "text": text} if flair_template_id is not None: @@ -1781,7 +1784,7 @@ async def update(self, flair_list, text="", css_class=""): :returns: List of dictionaries indicating the success or failure of each update. - For example to clear the flair text, and set the ``praw`` flair css + For example, to clear the flair text, and set the ``praw`` flair css class on a few users try: .. code-block:: python @@ -1926,7 +1929,7 @@ async def update( :param allowable_content: If specified, most be one of ``"all"``, ``"emoji"``, or ``"text"`` to restrict content to that type. If set to ``"emoji"`` then the ``"text"`` param must be a - valid emoji string, for example ``":snoo:"``. + valid emoji string, for example, ``":snoo:"``. :param max_emojis: (int) Maximum emojis in the flair (Reddit defaults this value to 10). :param fetch: Whether or not Async PRAW will fetch existing information on @@ -1936,7 +1939,7 @@ async def update( not provided will be reset to default (``None`` or ``False``) values. - For example to make a user flair template text_editable, try: + For example, to make a user flair template text_editable, try: .. code-block:: python @@ -1962,7 +1965,6 @@ async def update( "text_editable": text_editable, } if fetch: - _existing_data = [ template async for template in self if template["id"] == template_id ] @@ -2023,7 +2025,7 @@ async def add( :param allowable_content: If specified, most be one of ``"all"``, ``"emoji"``, or ``"text"`` to restrict content to that type. If set to ``"emoji"`` then the ``"text"`` param must be a - valid emoji string, for example ``":snoo:"``. + valid emoji string, for example, ``":snoo:"``. :param max_emojis: (int) Maximum emojis in the flair (Reddit defaults this value to 10). @@ -2107,7 +2109,7 @@ async def add( :param allowable_content: If specified, most be one of ``"all"``, ``"emoji"``, or ``"text"`` to restrict content to that type. If set to ``"emoji"`` then the ``"text"`` param must be a - valid emoji string, for example ``":snoo:"``. + valid emoji string, for example, ``":snoo:"``. :param max_emojis: (int) Maximum emojis in the flair (Reddit defaults this value to 10). @@ -2303,7 +2305,7 @@ def removal_reasons(self): """Provide an instance of :class:`.SubredditRemovalReasons`. Use this attribute for interacting with a subreddit's removal reasons. - For example to list all the removal reasons for a subreddit which you + For example, to list all the removal reasons for a subreddit which you have the ``posts`` moderator permission on, try: .. code-block:: python @@ -3251,6 +3253,7 @@ async def conversations( sub = await reddit.subreddit("all") async for conversation in sub.modmail.conversations(state="mod"): # do stuff with conversations + ... """ params = {} @@ -3318,6 +3321,7 @@ async def subreddits(self): sub = await reddit.subreddit("all") async for subreddit in sub.modmail.subreddits(): # do stuff with subreddit + ... """ response = await self.subreddit._reddit.get(API_PATH["modmail_subreddits"]) @@ -3403,7 +3407,7 @@ def submissions(self, **stream_options): high-volume streams, especially the r/all stream, may drop some submissions. - For example to retrieve all new submissions made to all of Reddit, try: + For example, to retrieve all new submissions made to all of Reddit, try: .. code-block:: python @@ -3533,10 +3537,7 @@ async def delete_banner_additional_image(self): await subreddit.stylesheet.delete_banner_additional_image() """ - data = { - "bannerPositionedImage": "", - "secondaryBannerPositionedImage": "", - } + data = {"bannerPositionedImage": "", "secondaryBannerPositionedImage": ""} await self._update_structured_styles(data) async def delete_banner_hover_image(self): @@ -3650,13 +3651,13 @@ async def upload(self, name, image_path): :returns: A dictionary containing a link to the uploaded image under the key ``img_src``. - Raises ``asyncprawcore.TooLarge`` if the overall request body is too + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3675,12 +3676,12 @@ async def upload_banner(self, image_path): :param image_path: A path to a jpeg or png image. - Raises ``asyncprawcore.TooLarge`` if the overall request body is too large. + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3701,12 +3702,12 @@ async def upload_banner_additional_image(self, image_path, align=None): :param align: Either ``left``, ``centered``, or ``right``. (default: ``left``). - Raises ``asyncprawcore.TooLarge`` if the overall request body is too large. + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3738,12 +3739,12 @@ async def upload_banner_hover_image(self, image_path): Fails if the Subreddit does not have an additional image defined - Raises ``asyncprawcore.TooLarge`` if the overall request body is too large. + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3764,12 +3765,12 @@ async def upload_header(self, image_path): :returns: A dictionary containing a link to the uploaded image under the key ``img_src``. - Raises ``asyncprawcore.TooLarge`` if the overall request body is too large. + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3788,12 +3789,12 @@ async def upload_mobile_header(self, image_path): :returns: A dictionary containing a link to the uploaded image under the key ``img_src``. - Raises ``asyncprawcore.TooLarge`` if the overall request body is too large. + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3812,12 +3813,12 @@ async def upload_mobile_icon(self, image_path): :returns: A dictionary containing a link to the uploaded image under the key ``img_src``. - Raises ``asyncprawcore.TooLarge`` if the overall request body is too large. + :raises: ``asyncprawcore.TooLarge`` if the overall request body is too large. - Raises :class:`.RedditAPIException` if there are other issues with the - uploaded image. Unfortunately the exception info might not be very - specific, so try through the website with the same image to see what - the problem actually might be. + :raises: :class:`.RedditAPIException` if there are other issues with the + uploaded image. Unfortunately the exception info might not be very + specific, so try through the website with the same image to see what + the problem actually might be. For example: @@ -3870,7 +3871,7 @@ async def __aiter__(self): .. code-block:: python subreddit = await reddit.subreddit("iama") - async for wikipage in sureddit.wiki: + async for wikipage in subreddit.wiki: print(wikipage) """ @@ -3895,8 +3896,7 @@ async def create(self, name, content, reason=None, **other_settings): .. code-block:: python subreddit = await reddit.subreddit("test") - await subreddit.wiki.create("praw_test", "wiki body text", reason="PRAW - Test Creation") + await subreddit.wiki.create("praw_test", "wiki body text", reason="PRAW Test Creation") """ name = name.replace(" ", "_").lower() @@ -3916,7 +3916,7 @@ def revisions(self, **generator_kwargs): subreddit = await reddit.subreddit("test") page = await subreddit.wiki.get_page("praw_test") - async for item in.page.revisions(): + async for item in page.revisions(): print(item) """ diff --git a/asyncpraw/models/reddit/widgets.py b/asyncpraw/models/reddit/widgets.py index 25f587514..417b0b7b1 100755 --- a/asyncpraw/models/reddit/widgets.py +++ b/asyncpraw/models/reddit/widgets.py @@ -231,13 +231,14 @@ class SubredditWidgets(AsyncPRAWBase): widgets.progressive_images = True async for widget in widgets.sidebar(): # do something + ... Access a subreddit's widgets with the following attributes: .. code-block:: python - print(await widgets.id_card() - print(await widgets.moderators_widget() + print(await widgets.id_card()) + print(await widgets.moderators_widget()) print([widget async for widget in widgets.sidebar()]) print([widget async for widget in widgets.topbar()]) @@ -356,10 +357,12 @@ def __getattr__(self, attr): """Return the value of `attr`.""" if not attr.startswith("_") and not self._fetched: raise AttributeError( - f"{self.__class__.__name__!r} object has no attribute {attr!r}, did you forget to run '.refresh()'?" + f"{self.__class__.__name__!r} object has no attribute {attr!r}, did you" + " forget to run '.refresh()'?" ) raise AttributeError( # pragma: no cover; I have no idea how to cover this - f"{self.__class__.__name__!r} object has no attribute {attr!r}, did you forget to run '.refresh()'?" + f"{self.__class__.__name__!r} object has no attribute {attr!r}, did you" + " forget to run '.refresh()'?" ) def __init__(self, subreddit): @@ -438,57 +441,57 @@ async def add_button_widget( Each button is either a text button or an image button. A text button looks like this: - .. code-block:: none + .. code-block:: text - { - "kind": "text", - "text": a string no longer than 30 characters, - "url": a valid URL, - "color": a 6-digit rgb hex color, e.g. `#AABBCC`, - "textColor": a 6-digit rgb hex color, e.g. `#AABBCC`, - "fillColor": a 6-digit rgb hex color, e.g. `#AABBCC`, - "hoverState": {...} - } + { + "kind": "text", + "text": a string no longer than 30 characters, + "url": a valid URL, + "color": a 6-digit rgb hex color, e.g. `#AABBCC`, + "textColor": a 6-digit rgb hex color, e.g. `#AABBCC`, + "fillColor": a 6-digit rgb hex color, e.g. `#AABBCC`, + "hoverState": {...} + } An image button looks like this: - .. code-block:: none + .. code-block:: text - { - "kind": "image", - "text": a string no longer than 30 characters, - "linkUrl": a valid URL, - "url": a valid URL of a reddit-hosted image, - "height": an integer, - "width": an integer, - "hoverState": {...} - } + { + "kind": "image", + "text": a string no longer than 30 characters, + "linkUrl": a valid URL, + "url": a valid URL of a reddit-hosted image, + "height": an integer, + "width": an integer, + "hoverState": {...} + } Both types of buttons have the field ``hoverState``. The field does not have to be included (it is optional). If it is included, it can be one of two types: text or image. A text ``hoverState`` looks like this: - .. code-block:: none + .. code-block:: text - { - "kind": "text", - "text": a string no longer than 30 characters, - "color": a 6-digit rgb hex color, e.g. `#AABBCC`, - "textColor": a 6-digit rgb hex color, e.g. `#AABBCC`, - "fillColor": a 6-digit rgb hex color, e.g. `#AABBCC` - } + { + "kind": "text", + "text": a string no longer than 30 characters, + "color": a 6-digit rgb hex color, e.g. `#AABBCC`, + "textColor": a 6-digit rgb hex color, e.g. `#AABBCC`, + "fillColor": a 6-digit rgb hex color, e.g. `#AABBCC` + } An image ``hoverState`` looks like this: - .. code-block:: none + .. code-block:: text - { - "kind": "image", - "url": a valid URL of a reddit-hosted image, - "height": an integer, - "width": an integer - } + { + "kind": "image", + "url": a valid URL of a reddit-hosted image, + "height": an integer, + "width": an integer + } .. note:: @@ -545,7 +548,7 @@ async def add_button_widget( "width": 200 } } - ] + ] styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"} new_widget = await widget_moderation.add_button_widget( "Things to click", "Click some of these *cool* links!", @@ -794,28 +797,28 @@ async def add_menu(self, data, **other_settings): :param data: A ``list`` of ``dict``\ s describing menu contents, as specified in `Reddit docs`_. As of this writing, the format is: - .. code-block:: none - - [ - { - "text": a string no longer than 20 characters, - "url": a valid URL - }, + .. code-block:: text - OR - - { - "children": [ - { + [ + { + "text": a string no longer than 20 characters, + "url": a valid URL + }, + + OR + + { + "children": [ + { + "text": a string no longer than 20 characters, + "url": a valid URL, + }, + ... + ], "text": a string no longer than 20 characters, - "url": a valid URL, - }, - ... - ], - "text": a string no longer than 20 characters, - }, - ... - ] + }, + ... + ] .. _Reddit docs: https://www.reddit.com/dev/api#POST_api_widget @@ -833,7 +836,7 @@ async def add_menu(self, data, **other_settings): {"text": "requests", "url": "http://python-requests.org"} ]}, {"text": "Reddit homepage", "url": "https://reddit.com"} - ] + ] new_widget = await widget_moderation.add_menu(menu_contents) """ @@ -927,7 +930,7 @@ async def reorder(self, new_order, section="sidebar"): .. code-block:: python subreddit = await reddit.subreddit("mysub") - widgets = [async for widget in subreddit.widgets] + widgets = [widget async for widget in subreddit.widgets] order = list(widgets.sidebar) order.reverse() await widgets.mod.reorder(order) @@ -1062,7 +1065,7 @@ class ButtonWidget(Widget, BaseList): "textColor": "#FFFF00", "fillColor": "#0000FF" }, - ] + ] styles = {"backgroundColor": "#FFFF66", "headerColor": "#3333EE"} button_widget = await widgets.mod.add_button_widget( "Things to click", "Click some of these *cool* links!", @@ -1497,7 +1500,7 @@ class Menu(Widget, BaseList): {"text": "requests", "url": "http://python-requests.org"} ]}, {"text": "Reddit homepage", "url": "https://reddit.com"} - ] + ] menu = await widgets.mod.add_menu(menu_contents) For more information on creation, see :meth:`.add_menu`. @@ -1850,6 +1853,7 @@ async def update(self, **kwargs): `Reddit documentation `_ or the document of the particular type of widget. + For example, update a text widget like so: .. code-block:: python diff --git a/asyncpraw/models/reddit/wikipage.py b/asyncpraw/models/reddit/wikipage.py index 72ebd7d09..b42f7da0b 100644 --- a/asyncpraw/models/reddit/wikipage.py +++ b/asyncpraw/models/reddit/wikipage.py @@ -96,7 +96,7 @@ async def update( :param other_settings: Additional keyword arguments to pass. :returns: The updated WikiPage settings. - To set the wikipage ``praw_test`` in ``/r/test`` to mod only and + To set the wikipage ``praw_test`` in ``r/test`` to mod only and disable it from showing in the page list, try: .. code-block:: python @@ -195,7 +195,10 @@ def __init__( def __repr__(self) -> str: """Return an object initialization representation of the instance.""" - return f"{self.__class__.__name__}(subreddit={self.subreddit!r}, name={self.name!r})" + return ( + f"{self.__class__.__name__}(subreddit={self.subreddit!r}," + f" name={self.name!r})" + ) def __str__(self) -> str: """Return a string representation of the instance.""" @@ -251,7 +254,7 @@ async def edit( async def revision(self, revision: str): """Return a specific version of this page by revision ID. - To view revision ``[ID]`` of ``"praw_test"`` in ``/r/test``: + To view revision ``[ID]`` of ``"praw_test"`` in ``r/test``: .. code-block:: python @@ -272,7 +275,7 @@ def revisions( Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. - To view the wiki revisions for ``"praw_test"`` in ``/r/test`` try: + To view the wiki revisions for ``"praw_test"`` in ``r/test`` try: .. code-block:: python diff --git a/asyncpraw/models/redditors.py b/asyncpraw/models/redditors.py index cd52c40be..e0ceba54e 100644 --- a/asyncpraw/models/redditors.py +++ b/asyncpraw/models/redditors.py @@ -1,7 +1,7 @@ """Provide the Redditors class.""" from itertools import islice from types import SimpleNamespace -from typing import TYPE_CHECKING, AsyncGenerator, Dict, Iterable, Union +from typing import TYPE_CHECKING, AsyncIterator, Dict, Iterable, Union import asyncprawcore @@ -23,7 +23,7 @@ class Redditors(AsyncPRAWBase): def new( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Subreddit", None]: + ) -> AsyncIterator["Subreddit"]: """Return a :class:`.ListingGenerator` for new Redditors. :returns: Redditor profiles, which are a type of :class:`.Subreddit`. @@ -35,7 +35,7 @@ def new( def popular( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Subreddit", None]: + ) -> AsyncIterator["Subreddit"]: """Return a :class:`.ListingGenerator` for popular Redditors. :returns: Redditor profiles, which are a type of :class:`.Subreddit`. @@ -49,7 +49,7 @@ def popular( def search( self, query: str, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Subreddit", None]: + ) -> AsyncIterator["Subreddit"]: r"""Return a :class:`.ListingGenerator` of Redditors for ``query``. :param query: The query string to filter Redditors by. @@ -66,7 +66,7 @@ def search( def stream( self, **stream_options: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator["Subreddit", None]: + ) -> AsyncIterator["Subreddit"]: """Yield new Redditors as they are created. Redditors are yielded oldest first. Up to 100 historical Redditors @@ -80,7 +80,7 @@ def stream( async def partial_redditors( self, ids: Iterable[str] - ) -> AsyncGenerator[PartialRedditor, None]: + ) -> AsyncIterator[PartialRedditor]: """Get user summary data by redditor IDs. :param ids: An iterable of redditor fullname IDs. diff --git a/asyncpraw/models/subreddits.py b/asyncpraw/models/subreddits.py index cd03c7e88..b6693f28b 100644 --- a/asyncpraw/models/subreddits.py +++ b/asyncpraw/models/subreddits.py @@ -1,5 +1,5 @@ """Provide the Subreddits class.""" -from typing import AsyncGenerator, Dict, List, Optional, Union +from typing import AsyncIterator, Dict, List, Optional, Union from warnings import warn from ..const import API_PATH @@ -18,7 +18,7 @@ def _to_list(subreddit_list): def default( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return a :class:`.ListingGenerator` for default subreddits. Additional keyword arguments are passed in the initialization of @@ -28,7 +28,7 @@ def default( self._reddit, API_PATH["subreddits_default"], **generator_kwargs ) - def gold(self, **generator_kwargs) -> AsyncGenerator[Subreddit, None]: + def gold(self, **generator_kwargs) -> AsyncIterator[Subreddit]: """Alias for :meth:`.premium` to maintain backwards compatibility.""" warn( "`subreddits.gold` has be renamed to `subreddits.premium`.", @@ -39,7 +39,7 @@ def gold(self, **generator_kwargs) -> AsyncGenerator[Subreddit, None]: def premium( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return a :class:`.ListingGenerator` for premium subreddits. Additional keyword arguments are passed in the initialization of @@ -51,7 +51,7 @@ def premium( def new( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return a :class:`.ListingGenerator` for new subreddits. Additional keyword arguments are passed in the initialization of @@ -63,7 +63,7 @@ def new( def popular( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return a :class:`.ListingGenerator` for popular subreddits. Additional keyword arguments are passed in the initialization of @@ -95,13 +95,13 @@ async def recommended( params = {"omit": self._to_list(omit_subreddits or [])} url = API_PATH["sub_recommended"].format(subreddits=self._to_list(subreddits)) return [ - await Subreddit(self._reddit, sub["sr_name"]) + Subreddit(self._reddit, sub["sr_name"]) for sub in await self._reddit.get(url, params=params) ] def search( self, query: str, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return a :class:`.ListingGenerator` of subreddits matching ``query``. Subreddits are searched by both their title and description. @@ -121,7 +121,7 @@ def search( async def search_by_name( self, query: str, include_nsfw: bool = True, exact: bool = False - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return list of Subreddits whose names begin with ``query``. :param query: Search for subreddits beginning with this string. @@ -138,13 +138,15 @@ async def search_by_name( async def search_by_topic( self, query: str - ) -> AsyncGenerator[ - Subreddit, None - ]: # pragma: no cover; TODO: not currently working + ) -> AsyncIterator[Subreddit]: # pragma: no cover; TODO: not currently working """Return list of Subreddits whose topics match ``query``. :param query: Search for subreddits relevant to the search topic. + .. note:: + + As of 09/01/2020, this endpoint always returns 404. + """ results = await self._reddit.get( API_PATH["subreddits_by_topic"], params={"query": query} @@ -155,7 +157,7 @@ async def search_by_topic( def stream( self, **stream_options: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Yield new subreddits as they are created. Subreddits are yielded oldest first. Up to 100 historical subreddits diff --git a/asyncpraw/models/user.py b/asyncpraw/models/user.py index 112ab30e7..caf91f3cf 100644 --- a/asyncpraw/models/user.py +++ b/asyncpraw/models/user.py @@ -1,5 +1,5 @@ """Provides the User class.""" -from typing import TYPE_CHECKING, AsyncGenerator, Dict, List, Optional, Union +from typing import TYPE_CHECKING, AsyncIterator, Dict, List, Optional, Union from ..const import API_PATH from ..models import Preferences @@ -63,8 +63,10 @@ async def blocked(self) -> List[Redditor]: def contributor_subreddits( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: - """Return a :class:`.ListingGenerator` of subreddits user is a contributor of. + ) -> AsyncIterator[Subreddit]: + """Return a :class:`.ListingGenerator` of contributor subreddits. + + These are subreddits that the user is a contributor of. Additional keyword arguments are passed in the initialization of :class:`.ListingGenerator`. @@ -117,8 +119,7 @@ async def me( ) -> Optional[Redditor]: # pylint: disable=invalid-name """Return a :class:`.Redditor` instance for the authenticated user. - In :attr:`~asyncpraw.Reddit.read_only` mode, this method returns - ``None``. + In :attr:`~asyncpraw.Reddit.read_only` mode, this method returns ``None``. :param use_cache: When true, and if this function has been previously called, returned the cached version (default: True). @@ -141,7 +142,7 @@ async def multireddits(self) -> List["Multireddit"]: def subreddits( self, **generator_kwargs: Union[str, int, Dict[str, str]] - ) -> AsyncGenerator[Subreddit, None]: + ) -> AsyncIterator[Subreddit]: """Return a :class:`.ListingGenerator` of subreddits the user is subscribed to. Additional keyword arguments are passed in the initialization of diff --git a/asyncpraw/models/util.py b/asyncpraw/models/util.py index 783c589f2..4521214d0 100644 --- a/asyncpraw/models/util.py +++ b/asyncpraw/models/util.py @@ -39,8 +39,11 @@ class ExponentialCounter: def __init__(self, max_counter: int): """Initialize an instance of ExponentialCounter. - :param max_counter: The maximum base value. Note that the computed - value may be 3.125% higher due to jitter. + :param max_counter: The maximum base value. + + .. note:: + + The computed value may be 3.125% higher due to jitter. """ self._base = 1 self._max = max_counter @@ -67,13 +70,12 @@ def permissions_string( all. When prefixed, the resulting string will simply be the joining of these inputs. When not prefixed, all permissions are considered to be additions, and all permissions in the ``known_permissions`` set that - aren't provided are considered to be removals. When None, the result is - ``+all``. + aren't provided are considered to be removals. When ``None``, the result is + ``"+all"``. :param known_permissions: A set of strings representing the available permissions. """ - to_set = [] if permissions is None: to_set = ["+all"] else: @@ -127,8 +129,7 @@ async def stream_generator( .. code-block:: python reply_function = reddit.inbox.comment_replies - reply_stream = asyncpraw.models.util.stream_generator(reply_function) - async for reply in reply_stream: + async for reply in asyncpraw.models.util.stream_generator(reply_function): print(reply) To pause a comment stream after six responses with no new diff --git a/asyncpraw/objector.py b/asyncpraw/objector.py index e50a6299d..386a76d64 100644 --- a/asyncpraw/objector.py +++ b/asyncpraw/objector.py @@ -181,9 +181,8 @@ def objectify( if data["json"]["data"]["id"].startswith( f"{self._reddit.config.kinds['submission']}_" ): - # With polls, Reddit returns a fullname but calls it an - # "id". This fixes this by coercing the fullname into an - # id. + # With polls, Reddit returns a fullname but calls it an "id". This + # fixes this by coercing the fullname into an id. data["json"]["data"]["id"] = data["json"]["data"]["id"].split( "_", 1 )[1] diff --git a/asyncpraw/reddit.py b/asyncpraw/reddit.py index db2ac6033..623de9dd7 100644 --- a/asyncpraw/reddit.py +++ b/asyncpraw/reddit.py @@ -82,15 +82,14 @@ def read_only(self, value: bool) -> None: """Set or unset the use of the ReadOnlyAuthorizer. :raises: :class:`ClientException` when attempting to unset ``read_only`` - and only the ReadOnlyAuthorizer is available. + and only the ReadOnlyAuthorizer is available. """ if value: self._core = self._read_only_core elif self._authorized_core is None: raise ClientException( - "read_only cannot be unset as only the " - "ReadOnlyAuthorizer is available." + "read_only cannot be unset as only the ReadOnlyAuthorizer is available." ) else: self._core = self._authorized_core @@ -100,6 +99,7 @@ def validate_on_submit(self) -> bool: """Get validate_on_submit. .. deprecated:: 7.0 + If property :attr:`.validate_on_submit` is set to False, the behavior is deprecated by Reddit. This attribute will be removed around May-June 2020. @@ -108,9 +108,9 @@ def validate_on_submit(self) -> bool: value = self._validate_on_submit if value is False: warn( - "Reddit will check for validation on all posts around " - "May-June 2020. It is recommended to check for validation" - " by setting reddit.validate_on_submit to True.", + "Reddit will check for validation on all posts around May-June 2020. It" + " is recommended to check for validation by setting" + " reddit.validate_on_submit to True.", category=DeprecationWarning, stacklevel=3, ) @@ -139,9 +139,9 @@ def __enter__(self): """ warn( - "Using this class as a synchronous context manager is deprecated and will " - "be removed in the next release. Use this class as an asynchronous context " - "manager instead.", + "Using this class as a synchronous context manager is deprecated and will" + " be removed in the next release. Use this class as an asynchronous context" + " manager instead.", category=DeprecationWarning, stacklevel=3, ) @@ -198,7 +198,13 @@ def __init__( .. code-block:: python - import json, aiohttp + import json + + import aiohttp + from asyncprawcore import Requestor + + from asyncpraw import Reddit + class JSONDebugRequestor(Requestor): async def request(self, *args, **kwargs): @@ -216,7 +222,7 @@ async def request(self, *args, **kwargs): .. code-block:: python async with Reddit(...) as reddit: - print(await reddit.user.me() + print(await reddit.user.me()) You can also call :meth:`.Reddit.close`: @@ -225,7 +231,7 @@ async def request(self, *args, **kwargs): reddit = Reddit(...) # do stuff with reddit ... - # then close the reqestor when done + # then close the requestor when done await reddit.close() """ @@ -241,24 +247,20 @@ async def request(self, *args, **kwargs): ) except configparser.NoSectionError as exc: help_message = ( - "You provided the name of a praw.ini " - "configuration which does not exist.\n\nFor help " - "with creating a Reddit instance, visit\n" - "https://asyncpraw.readthedocs.io/en/latest/code_overvi" - "ew/reddit_instance.html\n\n" - "For help on configuring Async PRAW, visit\n" - "https://asyncpraw.readthedocs.io/en/latest/getting_sta" - "rted/configuration.html" + "You provided the name of a praw.ini configuration which does not" + " exist.\n\nFor help with creating a Reddit instance," + " visit\nhttps://asyncpraw.readthedocs.io/en/latest/code_overview/reddit_instance.html\n\nFor" + " help on configuring Async PRAW," + " visit\nhttps://asyncpraw.readthedocs.io/en/latest/getting_started/configuration.html" ) if site_name is not None: exc.message += f"\n{help_message}" raise required_message = ( - "Required configuration setting {!r} missing. \n" - "This setting can be provided in a praw.ini file, " - "as a keyword argument to the `Reddit` class " - "constructor, or as an environment variable." + "Required configuration setting {!r} missing. \nThis setting can be" + " provided in a praw.ini file, as a keyword argument to the `Reddit` class" + " constructor, or as an environment variable." ) for attribute in ("client_id", "user_agent"): if getattr(self.config, attribute) in (self.config.CONFIG_NOT_SET, None): @@ -267,7 +269,9 @@ async def request(self, *args, **kwargs): ) if self.config.client_secret is self.config.CONFIG_NOT_SET: raise MissingRequiredAttributeException( - f"{required_message.format('client_secret')}\nFor installed applications this value must be set to None via a keyword argument to the `Reddit` class constructor." + f"{required_message.format('client_secret')}\nFor installed" + " applications this value must be set to None via a keyword argument" + " to the `Reddit` class constructor." ) self._check_for_update() self._prepare_objector() @@ -277,7 +281,11 @@ async def request(self, *args, **kwargs): """An instance of :class:`.Auth`. Provides the interface for interacting with installed and web - applications. See :ref:`auth_url` + applications. + + .. seealso:: + + :ref:`auth_url` """ @@ -299,7 +307,7 @@ async def request(self, *args, **kwargs): Provides the interface to a user's inbox which produces :class:`.Message`, :class:`.Comment`, and :class:`.Submission` - instances. For example to iterate through comments which mention the + instances. For example, to iterate through comments which mention the authorized user run: .. code-block:: python @@ -325,25 +333,25 @@ async def request(self, *args, **kwargs): """An instance of :class:`.MultiredditHelper`. Provides the interface to working with :class:`.Multireddit` - instances. For example you can obtain a :class:`.Multireddit` instance + instances. For example, you can obtain a :class:`.Multireddit` instance via: .. code-block:: python multireddit = await reddit.multireddit("samuraisam", "programming") - If you want to obtain a :class:`.Multireddit` instance you can do: + If you want to obtain a fetched :class:`.Multireddit` instance you can do: .. code-block:: python - multireddit = await reddit.multireddit("samuraisam", "programming") + multireddit = await reddit.multireddit("samuraisam", "programming", fetch=True) """ self.redditors = models.Redditors(self, None) """An instance of :class:`.Redditors`. - Provides the interface for Redditor discovery. For example + Provides the interface for Redditor discovery. For example, to iterate over the newest Redditors, run: .. code-block:: python @@ -357,7 +365,7 @@ async def request(self, *args, **kwargs): """An instance of :class:`.SubredditHelper`. Provides the interface to working with :class:`.Subreddit` - instances. For example to create a Subreddit run: + instances. For example, to create a Subreddit run: .. code-block:: python @@ -375,7 +383,7 @@ async def request(self, *args, **kwargs): await reddit.subreddit("redditdev", fetch=True) - Note that multiple subreddits can be combined and filtered views of + Multiple subreddits can be combined and filtered views of r/all can also be used just like a subreddit: .. code-block:: python @@ -388,7 +396,7 @@ async def request(self, *args, **kwargs): self.subreddits = models.Subreddits(self, None) """An instance of :class:`.Subreddits`. - Provides the interface for :class:`.Subreddit` discovery. For example + Provides the interface for :class:`.Subreddit` discovery. For example, to iterate over the set of default subreddits run: .. code-block:: python @@ -402,7 +410,7 @@ async def request(self, *args, **kwargs): """An instance of :class:`.User`. Provides the interface to the currently authorized - :class:`.Redditor`. For example to get the name of the current user + :class:`.Redditor`. For example, to get the name of the current user run: .. code-block:: python @@ -868,8 +876,8 @@ async def request( # TODO: Remove this exception after 2020-12-31 if no one has # filed a bug against it. raise Exception( - "Unexpected BadRequest without json body. Please file a " - "bug at https://github.com/praw-dev/asyncpraw/issues" + "Unexpected BadRequest without json body. Please file a bug at" + " https://github.com/praw-dev/asyncpraw/issues" ) from exception if set(data) == {"error", "message"}: raise diff --git a/docs/code_overview/exceptions.rst b/docs/code_overview/exceptions.rst index f38b2fd68..2f5c13cf7 100644 --- a/docs/code_overview/exceptions.rst +++ b/docs/code_overview/exceptions.rst @@ -3,7 +3,7 @@ Exceptions in Async PRAW In addition to exceptions under the ``asyncpraw.exceptions`` namespace shown below, exceptions might be raised that inherit from -``asyncprawcore.PrawcoreException``. Please see the following resource for +``asyncprawcore.AsyncPrawcoreException``. Please see the following resource for information on those exceptions: https://github.com/praw-dev/asyncprawcore/blob/master/asyncprawcore/exceptions.py diff --git a/docs/conf.py b/docs/conf.py index 191d4bd8d..a286c761b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,14 +7,14 @@ from asyncpraw import __version__ -copyright = "2020, Joel Payne" +copyright = "2021, Joel Payne" exclude_patterns = ["_build"] extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinxcontrib_trio"] html_static_path = ["_static"] html_theme = "sphinx_rtd_theme" html_theme_options = {"collapse_navigation": True} htmlhelp_basename = "Async PRAW" -intersphinx_mapping = {"python": ("https://docs.python.org/3.8", None)} +intersphinx_mapping = {"python": ("https://docs.python.org/3.9", None)} master_doc = "index" nitpicky = True project = "Async PRAW" diff --git a/docs/examples/lmgtfy_bot.py b/docs/examples/lmgtfy_bot.py index eded6f4c4..b3922cac7 100644 --- a/docs/examples/lmgtfy_bot.py +++ b/docs/examples/lmgtfy_bot.py @@ -4,12 +4,12 @@ import asyncpraw QUESTIONS = ["what is", "who is", "what are"] -REPLY_TEMPLATE = "[Let me google that for you](http://lmgtfy.com/?q={})" +REPLY_TEMPLATE = "[Let me google that for you](https://lmgtfy.com/?q={})" async def main(): reddit = asyncpraw.Reddit( - user_agent="LMGTFY (by /u/USERNAME)", + user_agent="LMGTFY (by u/USERNAME)", client_id="CLIENT_ID", client_secret="CLIENT_SECRET", username="USERNAME", diff --git a/docs/examples/obtain_refresh_token.py b/docs/examples/obtain_refresh_token.py index 33b755baa..27c36a24b 100755 --- a/docs/examples/obtain_refresh_token.py +++ b/docs/examples/obtain_refresh_token.py @@ -42,8 +42,8 @@ def send_message(client, message): async def main(): """Provide the program's entry point when directly executed.""" print( - "Go here while logged into the account you want to create a token for: " - "https://www.reddit.com/prefs/apps/" + "Go here while logged into the account you want to create a token for:" + " https://www.reddit.com/prefs/apps/" ) print( "Click the create an app button. Put something in the name field and select the" diff --git a/docs/getting_started/authentication.rst b/docs/getting_started/authentication.rst index e3ad07335..e67a8bd8d 100644 --- a/docs/getting_started/authentication.rst +++ b/docs/getting_started/authentication.rst @@ -67,14 +67,14 @@ is as simple as: reddit = asyncpraw.Reddit(client_id="SI8pN3DSbt0zor", client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", password="1guiwevlfo00esyy", - user_agent="testscript by /u/fakebot3", + user_agent="testscript by u/fakebot3", username="fakebot3") To verify that you are authenticated as the correct user run: .. code-block:: python - print(reddit.user.me()) + print(await reddit.user.me()) The output should contain the same name as you entered for ``username``. @@ -82,7 +82,7 @@ The output should contain the same name as you entered for ``username``. and ensure that that the username and password you are using are for the same user with which the application is associated: - .. code:: + .. code-block:: text OAuthException: invalid_grant error processing request @@ -98,7 +98,7 @@ A 2FA token can be used by joining it to the password with a colon: reddit = asyncpraw.Reddit(client_id="SI8pN3DSbt0zor", client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", password='1guiwevlfo00esyy:955413', - user_agent="testscript by /u/fakebot3", + user_agent="testscript by u/fakebot3", username="fakebot3") However, for such an app there is little benefit to using 2FA. The token @@ -121,9 +121,9 @@ A **code flow** application is useful for two primary purposes: accounts. * You have a personal-use script application and you either want to - * limit the access one of your Async PRAW-based programs has to Reddit - * avoid the hassle of 2FA (described above) - * not pass your username and password to Async PRAW (and thus not keep it in memory) + * limit the access one of your Async PRAW-based programs has to Reddit + * avoid the hassle of 2FA (described above) + * not pass your username and password to Async PRAW (and thus not keep it in memory) When registering your application you must provide a valid redirect URI. If you are running a website you will want to enter the appropriate callback URL and @@ -149,7 +149,7 @@ URL. You can do that as follows: reddit = asyncpraw.Reddit(client_id="SI8pN3DSbt0zor", client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", redirect_uri="http://localhost:8080", - user_agent="testscript by /u/fakebot3") + user_agent="testscript by u/fakebot3") print(reddit.auth.url(["identity"], "...", "permanent")) The above will output an authorization URL for a permanent token that has only @@ -164,8 +164,8 @@ token via: .. code-block:: python - print(reddit.auth.authorize(code)) - print(reddit.user.me()) + print(await reddit.auth.authorize(code)) + print(await reddit.user.me()) The first line of output is the ``refresh_token``. You can save this for later use (see :ref:`using_refresh_token`). @@ -189,7 +189,7 @@ part of the redirect. For the implicit flow call :meth:`.url` like so: .. code-block:: python - print(reddit.auth.url(["identity"], "...", implicit=True) + print(reddit.auth.url(["identity"], "...", implicit=True)) Then use :meth:`.implicit` to provide the authorization to the :class:`.Reddit` instance. @@ -271,7 +271,7 @@ of :class:`.Reddit` like so: client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", refresh_token="WeheY7PwgeCZj4S3QgUcLhKE5S2s4eAYdxM", user_agent="testscript by u/fakebot3") - print(reddit.auth.scopes()) + print(await reddit.auth.scopes()) The output from the above code displays which scopes are available on the :class:`.Reddit` instance. diff --git a/docs/getting_started/configuration.rst b/docs/getting_started/configuration.rst index 5dad7e162..ae2c07592 100644 --- a/docs/getting_started/configuration.rst +++ b/docs/getting_started/configuration.rst @@ -61,7 +61,7 @@ aiohttp and configuring Async PRAW like so: client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", password="1guiwevlfo00esyy", requestor_kwargs={"session": session}, # pass Session - user_agent="testscript by /u/fakebot3", + user_agent="testscript by u/fakebot3", username="fakebot3") @@ -95,7 +95,7 @@ export the certificate as a ``.pem`` file. Then configure Async PRAW like so: client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", password="1guiwevlfo00esyy", requestor_kwargs={"session": session}, # pass Session - user_agent="testscript by /u/fakebot3", + user_agent="testscript by u/fakebot3", username="fakebot3") diff --git a/docs/getting_started/configuration/options.rst b/docs/getting_started/configuration/options.rst index 0178d19c9..cb213c6e3 100644 --- a/docs/getting_started/configuration/options.rst +++ b/docs/getting_started/configuration/options.rst @@ -26,7 +26,7 @@ Basic Configuration Options :user_agent: (Required) A unique description of your application. The following format is recommended according to `Reddit's API Rules `_: - ``:: (by /u/:: (by u/)``. .. _oauth_options: diff --git a/docs/getting_started/configuration/prawini.rst b/docs/getting_started/configuration/prawini.rst index a7ce6f08d..e2fc9f208 100644 --- a/docs/getting_started/configuration/prawini.rst +++ b/docs/getting_started/configuration/prawini.rst @@ -80,7 +80,7 @@ site and can override whichever settings desired. Defining additional sites is a convenient way to store :ref:`OAuth credentials ` for various accounts, or distinct OAuth applications. For -example if you have three separate bots, you might create a site for each: +example, if you have three separate bots, you might create a site for each: .. _custom_site_example: .. code-block:: ini @@ -137,7 +137,7 @@ individual variables, for example: bot_name=MyBot bot_version=1.2.3 bot_author=MyUser - user_agent=script:%(bot_name)s:v%(bot_version)s (by /u/%(bot_author)s) + user_agent=script:%(bot_name)s:v%(bot_version)s (by u/%(bot_author)s) This uses basic interpolation thus :class:`.Reddit` need to be initialized as follows: diff --git a/docs/getting_started/configuration/reddit_initialization.rst b/docs/getting_started/configuration/reddit_initialization.rst index 2d302f0f8..64b72be2d 100644 --- a/docs/getting_started/configuration/reddit_initialization.rst +++ b/docs/getting_started/configuration/reddit_initialization.rst @@ -17,5 +17,5 @@ without using the ``bot3`` site, we would initialize :class:`.Reddit` as: reddit = asyncpraw.Reddit(client_id="SI8pN3DSbt0zor", client_secret="xaxkj7HNh8kwg8e5t4m6KvSrbTI", password="1guiwevlfo00esyy", - user_agent="testscript by /u/fakebot3", + user_agent="testscript by u/fakebot3", username="fakebot3") diff --git a/docs/getting_started/faq.rst b/docs/getting_started/faq.rst index a5fc8ce2c..bc3a46446 100644 --- a/docs/getting_started/faq.rst +++ b/docs/getting_started/faq.rst @@ -7,21 +7,21 @@ Q: How can I refresh a comment/subreddit/submission? A: There is two ways to do this: - * Directly calling the constructors will refresh the value: +* Directly calling the constructors will refresh the value: - .. code-block:: python + .. code-block:: python - await reddit.comment(id=comment.id) - await reddit.subreddit(display_name=subreddit.display_name) - await reddit.submission(id=submission.id) + await reddit.comment(id=comment.id) + await reddit.subreddit(display_name=subreddit.display_name) + await reddit.submission(id=submission.id) - * Calling :meth:`~.RedditBase.load`: +* Calling :meth:`~.RedditBase.load`: - .. code-block:: python + .. code-block:: python - await comment.load() - await subreddit.load() - await submission.load() + await comment.load() + await subreddit.load() + await submission.load() .. _faq2: @@ -49,7 +49,7 @@ Q: Help, I keep on getting redirected to ``/r/subreddit/login/``! Q2: I keep on getting this exception: -.. code-block:: none +.. code-block:: text asyncprawcore.exceptions.Redirect: Redirect to /r/subreddit/login/ (You may be trying to perform a non-read-only action via a read-only instance.) @@ -74,6 +74,7 @@ For example, the code block: subreddit = await reddit.subreddit('all') async for result in subreddit.search('https://google.com'): # do things with results + ... Will become this code block: @@ -82,3 +83,4 @@ Will become this code block: subreddit = await reddit.subreddit('all') async for result in subreddit.search('url:"https://google.com"'): # do things with results + ... diff --git a/docs/getting_started/quick_start.rst b/docs/getting_started/quick_start.rst index 3f48cf12e..f8ba4769f 100644 --- a/docs/getting_started/quick_start.rst +++ b/docs/getting_started/quick_start.rst @@ -167,7 +167,7 @@ calling ``subreddit`` on your :class:`.Reddit` instance. For example: .. code-block:: python # assume you have a Reddit instance bound to variable `reddit` - subreddit = await reddit.subreddit("redditdev") + subreddit = await reddit.subreddit("redditdev", fetch=True) print(subreddit.display_name) # Output: redditdev print(subreddit.title) # Output: reddit Development @@ -238,7 +238,7 @@ For example: print(redditor1.name) # Output: name of the redditor # assume you have a Reddit instance bound to variable `reddit` - redditor2 = await reddit.redditor("bboe") + redditor2 = await reddit.redditor("bboe", fetch=True) print(redditor2.link_karma) # Output: u/bboe's karma Obtain :class:`.Comment` Instances @@ -260,7 +260,7 @@ want to iterate over *all* comments as a flattened list you can call the ``comment_sort`` on the :class:`.Submission` instance prior to accessing ``comments`` (see: `/api/set_suggested_sort `_ for - possible values). For example to have comments sorted by ``new`` try + possible values). For example, to have comments sorted by ``new`` try something like: .. code-block:: python diff --git a/docs/package_info/asyncpraw_migration.rst b/docs/package_info/asyncpraw_migration.rst index aa60909ef..f356f95d6 100644 --- a/docs/package_info/asyncpraw_migration.rst +++ b/docs/package_info/asyncpraw_migration.rst @@ -1,18 +1,19 @@ Migrating to Async PRAW ======================= -With the conversion to async, there are few critical changes that had to be made. -This page outlines a few those changes. +With the conversion to async, there are few critical changes that had to be made. This +page outlines a few those changes. Network Requests ---------------- .. _network_requests: -Since Async PRAW will be operating in an asynchronous environment using -`aiohttp `_ and thus anytime it could make a network request -it needs to be awaited. The majority of all methods need to be awaited. - +Async PRAW utilizes `aiohttp `_ to make network requests to Reddit's API. Since aiohttp can +only be used in an asynchronous environment, all network requests need to be awaited. Due to this, most Async PRAW +methods need to be awaited as well. You can tell if a method needs awaited by looking at the docs. For example, +:meth:`.me` has the word ``await`` before ``me(use_cache: bool = True)`` in the header for that method +since that method makes a network request. Lazy Loading ------------ @@ -23,78 +24,113 @@ In PRAW, the majority of objects are lazily loaded and are not fetched until an attribute is accessed. With Async PRAW, objects can be fetched on initialization and some now do this by default. For example: -* PRAW: +- PRAW: + + .. code-block:: python + + # network request is not made and object is lazily loaded + submission = reddit.submission("id") + + # network request is made and object is fully fetched + print(submission.score) + +- Async PRAW: + + .. code-block:: python - .. code-block:: python + # network request made and object is fully loaded + submission = await reddit.submission("id") - submission = reddit.submission('id') # network request is not made and object is lazily loaded - print(submission.score) # network request is made and object is fully fetched + # network request is not made as object is already fully fetched + print(submission.score) -* Async PRAW: +Now, lazy loading is not gone completely and can still be done. For example, if you only +want to remove a post, you don't need the object fully fetched to do that. - .. code-block:: python +- PRAW: - submission = await reddit.submission('id') # network request made and object is fully loaded - print(submission.score) # network request is not made as object is already fully fetched + .. code-block:: python -Now, lazy loading is not gone completely and can still be done. For example, if you -only wanted to remove a post, you don't need the object fully fetched to do that. + # object is not fetched and is only removed + reddit.submission("id").mod.remove() -* PRAW +- Async PRAW: - .. code-block:: python + .. code-block:: python - reddit.submission('id').mod.remove() # object is not fetched and is only removed + # network request is not made and object is lazily loaded + submission = await reddit.submission("id", lazy=True) -* Async PRAW: + # object is not fetched and is only removed + await submission.mod.remove() - .. code-block:: python +The following objects are still lazily loaded by default: - submission = await reddit.submission('id', lazy=True) # network request is not made and object is lazily loaded - await submission.mod.remove() # object is not fetched and is only removed +* :class:`.Subreddit` +* :class:`.Redditor` +* :class:`.LiveThread` +* :class:`.Multireddit` -By default, only :class:`.Subreddit`, :class:`.Redditor`, :class:`.LiveThread`, -and :class:`.Multireddit` objects are still lazily loaded. You can pass ``fetch=True`` -in the initialization of the object to fully load it. Inversely, the following objects -are now fully fetched when initialized: :class:`.Submission`, :class:`.Comment`, -:class:`.WikiPage`, :class:`.RemovalReason`, :class:`.Collection`, :class:`.Emoji`, -:class:`.LiveUpdate`, :class:`.Rule`, and :class:`.Preferences`. You can pass -``lazy=True`` if you want to lazily loaded it. +You can pass ``fetch=True`` in their respective helper method to fully load it. + +Inversely, the following objects are now fully fetched when initialized: + +* :class:`.Submission` +* :class:`.Comment` +* :class:`.WikiPage` +* :class:`.RemovalReason` +* :class:`.Collection` +* :class:`.Emoji` +* :class:`.LiveUpdate` +* :class:`.Rule` +* :class:`.Preferences` + +You can pass ``lazy=True`` in their respective helper method if you want to lazily load it. In addition, there will be a ``load()`` method provided for manually fetching/refreshing objects that subclass :class:`.RedditBase`. If you need to later on access an attribute you need to call the ``.load()`` method first: - .. code-block:: python +.. code-block:: python - submission = await reddit.submission('id', lazy=True) # object is lazily loaded and no requests are made - ... - await submission.load() - print(submission.score) # network request is not made as object is already fully fetched + # object is lazily loaded and no requests are made + submission = await reddit.submission("id", lazy=True) + ... + # network request is made and item is fully fetched + await submission.load() + + # network request is not made as object is already fully fetched + print(submission.score) Getting items by Indices ------------------------ .. _objects_by_indices: -In PRAW you could get specific :class:`.WikiPage`, :class:`.RemovalReason`, :class:`.Emoji`, -:class:`.LiveUpdate`, and :class:`.Rule` objects by using string indices. This will no longer -work and has been converted to a ``.get_(item)`` method. Also, they are not lazily -loaded by default anymore. +In PRAW you could get specific :class:`.WikiPage`, :class:`.RemovalReason`, +:class:`.Emoji`, :class:`.LiveUpdate`, and :class:`.Rule` objects by using string +indices. This will no longer work and has been converted to a ``.get_(item)`` +method. Also, they are not lazily loaded by default anymore. + +- PRAW: + + .. code-block:: python -* PRAW: + # lazily creates a WikiPage instance + page = subreddit.wiki["page"] - .. code-block:: python + # network request is made and item is fully fetched + print(page.content_md) - page = subreddit.wiki['page'] # lazily creates a WikiPage instance - print(page.content_md) # network request is made and item is fully fetched +- Async PRAW: -* Async PRAW: + .. code-block:: python - .. code-block:: python + # network request made and object is fully loaded + page = await subreddit.wiki.get_page("page") - page = await subreddit.wiki.get_page('page') # network request made and object is fully loaded - print(page.content_md) # network request is not made as WikiPage is already fully fetched`` + # network request is not made as WikiPage is already fully fetched`` + print(page.content_md) - # using slices - rule = await subreddit.mod.rules.get_rule(slice(-3,None)) # to get the last 3 rules \ No newline at end of file + # using slices + rule = await subreddit.mod.rules.get_rule(slice(-3, None)) # to get the last 3 rules \ No newline at end of file diff --git a/docs/package_info/contributing.rst b/docs/package_info/contributing.rst index bd85f14da..05ce98cba 100644 --- a/docs/package_info/contributing.rst +++ b/docs/package_info/contributing.rst @@ -59,6 +59,7 @@ Without any configuration or modification, all the tests should pass. .. note:: Async PRAW uses a fork of `vcrpy` before you can run tests locally you must install the forked version. + .. code-block:: bash pip install https://github.com/LilSpazJoekp/vcrpy/archive/asyncpraw.zip diff --git a/docs/package_info/glossary.rst b/docs/package_info/glossary.rst index 598af56c4..881f5a4e3 100644 --- a/docs/package_info/glossary.rst +++ b/docs/package_info/glossary.rst @@ -22,30 +22,30 @@ Glossary Here is a list of the six different types of objects returned from reddit: - .. _fullname_t1: + .. _fullname_t1: - - ``t1`` These object represent :class:`.Comment`\ s. + - ``t1`` These object represent :class:`.Comment`\ s. - .. _fullname_t2: + .. _fullname_t2: - - ``t2`` These object represent :class:`.Redditor`\ s. + - ``t2`` These object represent :class:`.Redditor`\ s. - .. _fullname_t3: + .. _fullname_t3: - - ``t3`` These object represent :class:`.Submission`\ s. + - ``t3`` These object represent :class:`.Submission`\ s. - .. _fullname_t4: + .. _fullname_t4: - - ``t4`` These object represent :class:`.Message`\ s. + - ``t4`` These object represent :class:`.Message`\ s. - .. _fullname_t5: + .. _fullname_t5: - - ``t5`` These object represent :class:`.Subreddit`\ s. + - ``t5`` These object represent :class:`.Subreddit`\ s. - .. _fullname_t6: + .. _fullname_t6: - - ``t6`` These object represent ``Award``\ s, such as ``Reddit Gold`` or - ``Reddit Silver``. + - ``t6`` These object represent ``Award``\ s, such as ``Reddit Gold`` or + ``Reddit Silver``. .. _gild: diff --git a/docs/tutorials/reply_bot.rst b/docs/tutorials/reply_bot.rst index 51d17f7d0..5997421e8 100644 --- a/docs/tutorials/reply_bot.rst +++ b/docs/tutorials/reply_bot.rst @@ -14,7 +14,7 @@ With Async PRAW, there is now support for interacting with Reddit inside an asynchronous environment, most commonly, Discord bots. This tutorial will show you how to build a bot that monitors a particular -subreddit, `/r/AskReddit `_, for new +subreddit, `r/AskReddit `_, for new submissions containing simple questions and replies with an appropriate link to lmgtfy_ (Let Me Google That For You). @@ -87,16 +87,17 @@ subreddit. To indefinitely iterate over new submissions to a subreddit add: subreddit = await reddit.subreddit("AskReddit") async for submission in subreddit.stream.submissions(): # do something with submission + ... Replace ``AskReddit`` with the name of another subreddit if you want to iterate through its new submissions. Additionally multiple subreddits can be specified -by joining them with pluses, for example ``AskReddit+NoStupidQuestions``. All +by joining them with pluses, for example, ``AskReddit+NoStupidQuestions``. All subreddits can be specified using the special name ``all``. Step 3: Analyzing the Submission Titles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Now that we have a stream of new submissions to /r/AskReddit, it is time to see +Now that we have a stream of new submissions to r/AskReddit, it is time to see if their titles contain a simple question. We naïvely define a simple question as: @@ -168,9 +169,6 @@ comment is located: url_title = quote_plus(submission.title) reply_text = reply_template.format(url_title) -.. note:: This example assumes the use of Python 3. For Python 2 replace ``from - urllib.parse import quote_plus`` with ``from urllib import quote_plus``. - Now that we have the reply text, replying to the submission is easy: .. code-block:: python diff --git a/pre_push.py b/pre_push.py index 30612871f..9f2190a93 100755 --- a/pre_push.py +++ b/pre_push.py @@ -108,8 +108,7 @@ def main(): "--all", action="store_true", default=False, - help="Run all of the tests (static and unit). " - "Overrides the unstatic argument.", + help="Run all of the tests (static and unit). Overrides the unstatic argument.", ) args = parser.parse_args() success = True diff --git a/setup.py b/setup.py index a5dbc3c94..702a5fac2 100644 --- a/setup.py +++ b/setup.py @@ -59,13 +59,12 @@ "Topic :: Utilities", ], description=( - "Async PRAW, an acronym for `Asynchronous Python Reddit API Wrapper`, is a " - "python package that allows for simple access to " - "reddit's API." + "Async PRAW, an abbreviation for `Asynchronous Python Reddit API Wrapper`, is a" + " python package that allows for simple access to reddit's API." ), extras_require=extras, install_requires=["asyncprawcore >=1.0.1, <2.0", "update_checker >=0.18"], - keywords="reddit api wrapper async asynchronous praw", + keywords="reddit api wrapper asyncpraw praw async asynchronous", license="Simplified BSD License", long_description=README, package_data={ diff --git a/tests/conftest.py b/tests/conftest.py index 59f2dc1e0..4c689571b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,8 +52,8 @@ def filter_access_token(response): placeholders = { x: env_default(x) for x in ( - "auth_code client_id client_secret password redirect_uri " - "test_subreddit user_agent username refresh_token" + "auth_code client_id client_secret password redirect_uri test_subreddit" + " user_agent username refresh_token" ).split() } diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index fb72c3c1d..ad43df570 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -94,13 +94,13 @@ def use_cassette(self, cassette_name=None, **kwargs): dynamic_name = self.get_cassette_name() if cassette_name: self.logger.debug( - f"Static cassette name provided by {dynamic_name}. The following name " - f"was provided: {cassette_name}" + f"Static cassette name provided by {dynamic_name}. The following name" + f" was provided: {cassette_name}" ) if cassette_name != dynamic_name: self.logger.warning( - f"Dynamic cassette name for function {dynamic_name} does not " - f"match the provided cassette name: {cassette_name}" + f"Dynamic cassette name for function {dynamic_name} does not match" + f" the provided cassette name: {cassette_name}" ) return self.recorder.use_cassette(cassette_name or dynamic_name, **kwargs) diff --git a/tests/integration/models/reddit/test_live.py b/tests/integration/models/reddit/test_live.py index 563a1137a..182060612 100644 --- a/tests/integration/models/reddit/test_live.py +++ b/tests/integration/models/reddit/test_live.py @@ -16,7 +16,7 @@ async def test_attributes(self): update = await thread.get_update("7827987a-c998-11e4-a0b9-22000b6a88d2") assert isinstance(update.author, Redditor) assert update.author == "umbrae" - assert update.name == ("LiveUpdate_7827987a-c998-11e4-a0b9-22000b6a88d2") + assert update.name == "LiveUpdate_7827987a-c998-11e4-a0b9-22000b6a88d2" assert update.body.startswith("Small change") diff --git a/tests/integration/models/reddit/test_removal_reasons.py b/tests/integration/models/reddit/test_removal_reasons.py index 497911896..4d1de9e24 100644 --- a/tests/integration/models/reddit/test_removal_reasons.py +++ b/tests/integration/models/reddit/test_removal_reasons.py @@ -20,7 +20,7 @@ async def test__fetch(self, _): async def test__fetch_int(self, _): self.reddit.read_only = False subreddit = await self.reddit.subreddit(pytest.placeholders.test_subreddit) - with self.recorder.use_cassette("TestRemovalReason.test__fetch"): + with self.use_cassette("TestRemovalReason.test__fetch"): reason = await subreddit.mod.removal_reasons.get_reason(0) assert isinstance(reason, RemovalReason) @@ -28,7 +28,7 @@ async def test__fetch_int(self, _): async def test__fetch_slice(self, _): self.reddit.read_only = False subreddit = await self.reddit.subreddit(pytest.placeholders.test_subreddit) - with self.recorder.use_cassette("TestRemovalReason.test__fetch"): + with self.use_cassette("TestRemovalReason.test__fetch"): reasons = await subreddit.mod.removal_reasons.get_reason(slice(-3, None)) assert len(reasons) == 3 for reason in reasons: diff --git a/tests/integration/models/reddit/test_submission.py b/tests/integration/models/reddit/test_submission.py index 65f88b93e..3ef63feec 100644 --- a/tests/integration/models/reddit/test_submission.py +++ b/tests/integration/models/reddit/test_submission.py @@ -135,8 +135,9 @@ async def test_invalid_attribute(self): with pytest.raises(AttributeError) as excinfo: submission = await self.reddit.submission("2gmzqe") submission.invalid_attribute - assert excinfo.value.args[0] == ( - "'Submission' object has no attribute 'invalid_attribute'" + assert ( + excinfo.value.args[0] + == "'Submission' object has no attribute 'invalid_attribute'" ) async def test_reply(self): diff --git a/tests/integration/models/reddit/test_subreddit.py b/tests/integration/models/reddit/test_subreddit.py index 97ee5d112..0334aa10d 100644 --- a/tests/integration/models/reddit/test_subreddit.py +++ b/tests/integration/models/reddit/test_subreddit.py @@ -484,13 +484,11 @@ async def test_submit_image__large(self, _): '' "" "EntityTooLarge" - "Your proposed upload exceeds the maximum " - "allowed size" + "Your proposed upload exceeds the maximum allowed size" "20971528" "20971520" "23F056D6990D87E0" - "iYEVOuRfbLiKwMgHt2ewqQRIm0NWL79uiC2rPLj9P0PwW55" - "4MhjY2/O8d9JdKTf1iwzLjwWMnGQ=" + "iYEVOuRfbLiKwMgHt2ewqQRIm0NWL79uiC2rPLj9P0PwW55MhjY2/O8d9JdKTf1iwzLjwWMnGQ=" "" ) _post = reddit._core._requestor._http.post diff --git a/tests/integration/models/reddit/test_widgets.py b/tests/integration/models/reddit/test_widgets.py index bad3db021..7f657eb59 100644 --- a/tests/integration/models/reddit/test_widgets.py +++ b/tests/integration/models/reddit/test_widgets.py @@ -223,8 +223,8 @@ async def test_create_and_update_and_delete(self, _): assert isinstance(widget, Calendar) assert widget.shortName == "Upcoming Events" assert ( - widget.googleCalendarId == "ccahu0rstno2jrvioq4ccffn78@" - "group.calendar.google.com" + widget.googleCalendarId + == "ccahu0rstno2jrvioq4ccffn78@group.calendar.google.com" ) assert widget.configuration == config assert widget.styles == styles @@ -234,8 +234,8 @@ async def test_create_and_update_and_delete(self, _): assert isinstance(widget, Calendar) assert widget.shortName == "Past Events :(" assert ( - widget.googleCalendarId == "ccahu0rstno2jrvioq4ccffn78@" - "group.calendar.google.com" + widget.googleCalendarId + == "ccahu0rstno2jrvioq4ccffn78@group.calendar.google.com" ) assert widget.configuration == config assert widget.styles == styles @@ -794,8 +794,8 @@ async def test_repr(self): subreddit = await self.reddit.subreddit(pytest.placeholders.test_subreddit) widgets = subreddit.widgets assert ( - f"SubredditWidgets(subreddit=Subreddit(display_name='" - f"{pytest.placeholders.test_subreddit}'))" == repr(widgets) + f"SubredditWidgets(subreddit=Subreddit(display_name='{pytest.placeholders.test_subreddit}'))" + == repr(widgets) ) async def test_sidebar(self): diff --git a/tests/unit/models/reddit/test_emoji.py b/tests/unit/models/reddit/test_emoji.py index 52aa19140..1a8b243cf 100644 --- a/tests/unit/models/reddit/test_emoji.py +++ b/tests/unit/models/reddit/test_emoji.py @@ -48,7 +48,7 @@ def test_hash(self): def test_repr(self): emoji = Emoji(self.reddit, subreddit=Subreddit(self.reddit, "a"), name="x") - assert repr(emoji) == ("Emoji(name='x')") + assert repr(emoji) == "Emoji(name='x')" def test_str(self): emoji = Emoji(self.reddit, subreddit=Subreddit(self.reddit, "a"), name="x") diff --git a/tests/unit/models/reddit/test_more.py b/tests/unit/models/reddit/test_more.py index 01b2aafbd..dde263417 100644 --- a/tests/unit/models/reddit/test_more.py +++ b/tests/unit/models/reddit/test_more.py @@ -8,10 +8,10 @@ def test_repr(self): more = MoreComments( self.reddit, {"children": ["a", "b", "c", "d", "e"], "count": 5} ) - assert repr(more) == ("") + assert repr(more) == "" more = MoreComments(self.reddit, {"children": ["a", "b", "c", "d"], "count": 4}) - assert repr(more) == ("") + assert repr(more) == "" def test_equality(self): more = MoreComments(self.reddit, {"children": ["a", "b", "c", "d"], "count": 4}) diff --git a/tests/unit/models/reddit/test_removal_reasons.py b/tests/unit/models/reddit/test_removal_reasons.py index 34f045edf..46c99e46f 100644 --- a/tests/unit/models/reddit/test_removal_reasons.py +++ b/tests/unit/models/reddit/test_removal_reasons.py @@ -88,7 +88,7 @@ def test_repr(self): reason = RemovalReason( self.reddit, subreddit=Subreddit(self.reddit, display_name="a"), id="x" ) - assert repr(reason) == ("RemovalReason(id='x')") + assert repr(reason) == "RemovalReason(id='x')" def test_str(self): reason = RemovalReason( diff --git a/tests/unit/test_deprecations.py b/tests/unit/test_deprecations.py index 35476c40a..32ff23730 100644 --- a/tests/unit/test_deprecations.py +++ b/tests/unit/test_deprecations.py @@ -52,12 +52,7 @@ async def test_subreddit_rules_call(self): await subreddit.rules() assert ( excinfo.value.args[0] - == "Calling SubredditRules to get a list of rules is deprecated. " - "Remove the parentheses to use the iterator. View the " - "Async PRAW documentation on how to change the code in order to use the" - "iterator (https://asyncpraw.readthedocs.io/en/latest/code_overview" - "/other/subredditrules.html#asyncpraw.models.reddit.rules." - "SubredditRules.__call__)." + == "Calling SubredditRules to get a list of rules is deprecated. Remove the parentheses to use the iterator. View the Async PRAW documentation on how to change the code in order to use the iterator (https://asyncpraw.readthedocs.io/en/latest/code_overview/other/subredditrules.html#asyncpraw.models.reddit.rules.SubredditRules.__call__)." ) def test_web_socket_exception_attribute(self): @@ -66,9 +61,7 @@ def test_web_socket_exception_attribute(self): _ = exc.original_exception assert ( excinfo.value.args[0] - == "Accessing the attribute original_exception is deprecated." - " Please rewrite your code in such a way that this attribute does " - "not need to be used. It will be removed in Async PRAW 8.0." + == "Accessing the attribute original_exception is deprecated. Please rewrite your code in such a way that this attribute does not need to be used. It will be removed in Async PRAW 8.0." ) def test_gold_method(self): @@ -91,7 +84,5 @@ def test_synchronous_context_manager(self): pass assert ( excinfo.value.args[0] - == "Using this class as a synchronous context manager is deprecated" - " and will be removed in the next release. Use this class as an " - "asynchronous context manager instead." + == "Using this class as a synchronous context manager is deprecated and will be removed in the next release. Use this class as an asynchronous context manager instead." ) diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 304244093..8fae286b5 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -48,8 +48,8 @@ def test_str(self): def test_repr(self): error = RedditErrorItem("BAD_SOMETHING", "invalid something", "some_field") assert ( - repr(error) == "RedditErrorItem(error_type='BAD_SOMETHING', message=" - "'invalid something', field='some_field')" + repr(error) + == "RedditErrorItem(error_type='BAD_SOMETHING', message='invalid something', field='some_field')" ) @@ -100,8 +100,7 @@ def test_inheritance(self): def test_message(self): assert ( str(DuplicateReplaceException()) - == "A duplicate comment has been detected. Are you attempting to " - "call ``replace_more_comments`` more than once?" + == "A duplicate comment has been detected. Are you attempting to call ``replace_more_comments`` more than once?" ) @@ -112,8 +111,7 @@ def test_inheritance(self): def test_str(self): assert ( str(InvalidFlairTemplateID("123")) - == "The flair template id ``123`` is invalid. If you are " - "trying to create a flair, please use the ``add`` method." + == "The flair template id ``123`` is invalid. If you are trying to create a flair, please use the ``add`` method." ) @@ -186,7 +184,5 @@ def test_inheritance(self): def test_message(self): assert ( str(MediaPostFailed()) - == "The attempted media upload action has failed. Possible causes" - " include the corruption of media files. Check that the media " - "file can be opened on your local machine." + == "The attempted media upload action has failed. Possible causes include the corruption of media files. Check that the media file can be opened on your local machine." ) diff --git a/tests/unit/test_reddit.py b/tests/unit/test_reddit.py index f72f1503c..3e9001685 100644 --- a/tests/unit/test_reddit.py +++ b/tests/unit/test_reddit.py @@ -69,15 +69,13 @@ def test_invalid_config(self): Reddit(timeout="test", **self.REQUIRED_DUMMY_SETTINGS) assert ( excinfo.value.args[0] - == "An incorrect config type was given for option timeout. The " - "expected type is int, but the given value is test." + == "An incorrect config type was given for option timeout. The expected type is int, but the given value is test." ) with pytest.raises(ValueError) as excinfo: Reddit(ratelimit_seconds="test", **self.REQUIRED_DUMMY_SETTINGS) assert ( - excinfo.value.args[0] == "An incorrect config type was given for option " - "ratelimit_seconds. The expected type is int, but the given value " - "is test." + excinfo.value.args[0] + == "An incorrect config type was given for option ratelimit_seconds. The expected type is int, but the given value is test." ) def test_info__not_list(self): diff --git a/tools/check_documentation.py b/tools/check_documentation.py index 524281950..b9e2231ec 100644 --- a/tools/check_documentation.py +++ b/tools/check_documentation.py @@ -53,8 +53,8 @@ def check(cls): continue if not cls.HAS_ATTRIBUTE_TABLE.search(subclass.__doc__): print( - f"Subclass {subclass.__module__}.{subclass.__name__} is missing a " - f"table of common attributes." + f"Subclass {subclass.__module__}.{subclass.__name__} is missing a" + " table of common attributes." ) success = False for method_name in dir(subclass): diff --git a/tools/static_word_checks.py b/tools/static_word_checks.py index 1544e8ad6..e87afb4ac 100644 --- a/tools/static_word_checks.py +++ b/tools/static_word_checks.py @@ -8,7 +8,7 @@ class StaticChecker: """Run simple checks on the entire document or specific lines.""" def __init__(self, replace: bool): - """Instantiates the class. + """Initializes the class. :param replace: Whether or not to make replacements. """ @@ -38,7 +38,8 @@ def check_for_code_statement(self, filename: str, content: str) -> bool: print(f"{filename}: Replaced all ``code::`` to ``code-block::``") return True print( - f"{filename}; This file uses the `code::` syntax, please change the syntax to ``code-block::``." + f"{filename}; This file uses the `code::` syntax, please change the" + " syntax to ``code-block::``." ) return False return True @@ -63,11 +64,13 @@ def check_for_double_syntax(self, filename: str, content: str) -> bool: with open(filename, "w") as fp: fp.write(newcontent) print( - f"{filename}: Replaced all instances of ``/r/`` and/or ``/u/`` to ``r/`` and/or ``u/``." + f"{filename}: Replaced all instances of ``/r/`` and/or ``/u/`` to" + " ``r/`` and/or ``u/``." ) return True print( - f"{filename}: This file contains instances of ``/r/`` and/or ``/u/``. Please change them to ``r/`` and/or ``u/``." + f"{filename}: This file contains instances of ``/r/`` and/or ``/u/``." + " Please change them to ``r/`` and/or ``u/``." ) return False @@ -81,7 +84,8 @@ def check_for_noreturn(self, filename: str, line_number: int, content: str) -> b """ if "noreturn" in content.lower(): print( - f"{filename}: Line {line_number} has phrase ``noreturn``, please edit and remove this." + f"{filename}: Line {line_number} has phrase ``noreturn``, please edit" + " and remove this." ) return False return True @@ -101,12 +105,12 @@ def run_checks(self) -> bool: * Full file checks: - * :meth:`.check_for_code_statement` - * :meth:`.check_for_double_syntax` + * :meth:`.check_for_code_statement` + * :meth:`.check_for_double_syntax` * Line checks - * :meth:`.check_for_noreturn` + * :meth:`.check_for_noreturn` """ status = True directory = os.path.abspath(os.path.join(__file__, "..", "..", "asyncpraw")) @@ -134,16 +138,20 @@ def run_checks(self) -> bool: def main(): """The main function.""" parser = argparse.ArgumentParser( - description="Run static line checks and optionally replace values that" - " should not be used." + description=( + "Run static line checks and optionally replace values that should not be" + " used." + ) ) parser.add_argument( "-r", "--replace", action="store_true", default=False, - help="If it is possible, tries to reformat values. Not all checks " - "can reformat values, and those will have to be edited manually.", + help=( + "If it is possible, tries to reformat values. Not all checks can reformat" + " values, and those will have to be edited manually." + ), ) args = parser.parse_args() check = StaticChecker(args.replace)