Meet The Client#
Let’s take a deeper look into what the client object is capable of.
Instantiation#
The grant
parameter#
We’ve already seen several ways of constructing a client instance by directly inputting various combinations of credentials, but there’s an important constructor overload we haven’t yet covered:
@overload
def __init__(self, client_id: str, client_secret: str, /, *, grant: AuthorizationGrant) -> None: ...
This overload with the grant
keyword is the universal one; the other
overloads are shorthands for this one.
The grant
keyword takes a mapping (Mapping[str, str]
) of grant credentials.
There are built-in mapping object types found in redditwarp.auth.grants
that
make expressing grant credentials cleaner. E.g.:
from redditwarp.auth import grants
from redditwarp.SYNC import Client
Client(CLIENT_ID, CLIENT_SECRET)
Client(CLIENT_ID, CLIENT_SECRET, grant=grants.ClientCredentialsGrant())
Client(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN)
Client(CLIENT_ID, CLIENT_SECRET, grant=grants.RefreshTokenGrant(REFRESH_TOKEN))
Client(CLIENT_ID, CLIENT_SECRET, USERNAME, PASSWORD)
Client(CLIENT_ID, CLIENT_SECRET, grant=grants.ResourceOwnerPasswordCredentialsGrant(USERNAME, PASSWORD))
The need for the grant
keyword parameter should be rare. The only practical
situation where you would want to use this parameter is if you need to use the
installed client grant type. There also happens to be little reason to ever
pass in a redditwarp.auth.grants.AuthorizationCodeGrant
grant type
since that grant type is part of a more complex OAuth flow.
Since the installed client grant type is a Reddit-specified extension grant
type, the helper mapping object is located at
redditwarp.core.grants.InstalledClientGrant
instead of in
redditwarp.auth.grants
.
import uuid
from redditwarp.core import grants as core_grants
grant = core_grants.InstalledClientGrant(str(uuid.uuid1()))
Client(CLIENT_ID, CLIENT_SECRET, grant=grant)
The PRAW config constructor#
RedditWarp doesn’t formalise or prescribe any particular file format for
storing your API credentials, but it does offer support for PRAW’s praw.ini
files for convenience via
Client.from_praw_config()
.
See here for more info.
The access token constructor#
If you have your own way of directly obtaining API access tokens, a client
instance can be instantiated from one using
Client.from_access_token()
.
When the access token expires, API calls will result in a 401 Unauthorized
StatusCodeException
exception.
The
Client.set_access_token()
instance method can be used to assign a new
token.
Setting a user agent#
The
Client.set_user_agent()
method can be used to set a user agent.
>>> from redditwarp.SYNC import Client
>>> client = Client()
>>> client.set_user_agent("u_SuvaBot/1.0.0 (by u/Pyprohly)")
>>> print(client.http.get_user_agent())
RedditWarp/0.7.0 Python/3.10.6 httpx/0.23.0 Bot !-- u_SuvaBot/1.0.0 (by u/Pyprohly)
The full user agent that is used differs slightly. The
client.http.set_user_agent()
function can be used to set the full user
agent. However, doing this is not advised, and if not done correctly would be a
violation of the Reddit API guidelines.
Making requests#
The
client.request()
method is the building block of all the methods in the
procedure index.
>>> d = client.request('GET', '/user/spez/about')['data']
>>> d
{'is_employee': True, [...]
Use of this method is only really appropriate for making calls to the Reddit API and not any other website because of the domain specific post processing that happens with the response data.
If you do want to make requests to other sites, you are welcome to use the
client.http.request()
method:
>>> from redditwarp.http.util.json_loading import load_json_from_response
>>> resp = client.http.request('GET', 'http://httpbin.org/get')
>>> json = load_json_from_response(resp)
Request inspection#
Let’s say you’re curious about the underlying calls that make up a complicated
series of RedditWarp procedure calls. We can inspect the calls that RedditWarp
performed through the client.http.last
object.
>>> it = client.p.front.pull.hot(amount=220)
>>> _ = list(it)
>>> for xchg in client.http.last.exchange_queue:
... print("{0.verb} {0.url}".format(xchg.request))
...
GET https://oauth.reddit.com/hot?limit=100&raw_json=1&api_type=json
GET https://oauth.reddit.com/hot?limit=100&count=100&after=t3_zegunl&raw_json=1&api_type=json
GET https://oauth.reddit.com/hot?limit=20&count=200&after=t3_ze675a&raw_json=1&api_type=json
The last 16 exchanges get recorded. The most recent is the last element.
The HTTP client#
The RedditWarp client uses the HTTPClient
object at client.http
to make
requests.
The HTTPClient
’s main methods are
request()
and
inquire()
.
They share
the same parameters. The request()
function simply invokes the inquire()
method and returns the response object.
The parameters verb
, url
, params
, and headers
are self-explanatory.
The data
parameter is used to send URL-encoded form data,
the json
parameter is used to send JSON data, and
the files
parameter is used to send multipart form data.
The parameters data
, json
, and files
are mutually exclusive,
although the data
parameter can be used with files
just as another
way of writing files={**data, **files}
.
>>> resp = client.http.request('POST', 'http://httpbin.org/post', data={'a': 'b'})
>>> resp.ensure_successful_status()
>>> print(resp.data.decode())
{
"args": {},
"data": "",
"files": {},
"form": {
"a": "b"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "3",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "RedditWarp/0.7.0 Python/3.10.6 httpx/0.23.0 Bot !-- API testing",
"X-Amzn-Trace-Id": "Root=1-63908f05-79dd49354966fbcb081cb9aa"
},
"json": null,
"origin": "47.74.3.224",
"url": "http://httpbin.org/post"
}
If you want to read in JSON data you can use
redditwarp.http.util.json_loading.load_json_from_response(resp)
or
json.loads(resp.data)
.
The client.http
HTTP client can be used to send requests to domains other
than Reddit: the Reddit credentials will not be accidentally forwarded to
non-Reddit domains, nor will those requests be rate limited. On the other hand,
using the client.http
object to make non-Reddit requests is probably not
ideal and a separate HTTP client should be used instead.