Merge pull request #305 from jkawamoto/user_agent

Supports User-Agent header in create_app method
This commit is contained in:
Lorenz Diener 2023-04-23 18:36:06 +03:00 committed by GitHub
commit f862772ea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 17 deletions

View File

@ -14,13 +14,15 @@ from .utility import parse_version_string, api_version
from .internals import Mastodon as Internals from .internals import Mastodon as Internals
_DEFAULT_USER_AGENT = "mastodonpy"
class Mastodon(Internals): class Mastodon(Internals):
### ###
# Registering apps # Registering apps
### ###
@staticmethod @staticmethod
def create_app(client_name, scopes=_DEFAULT_SCOPES, redirect_uris=None, website=None, to_file=None, def create_app(client_name, scopes=_DEFAULT_SCOPES, redirect_uris=None, website=None, to_file=None,
api_base_url=None, request_timeout=_DEFAULT_TIMEOUT, session=None): api_base_url=None, request_timeout=_DEFAULT_TIMEOUT, session=None, user_agent=_DEFAULT_USER_AGENT):
""" """
Create a new app with given `client_name` and `scopes` (The basic scopes are "read", "write", "follow" and "push" Create a new app with given `client_name` and `scopes` (The basic scopes are "read", "write", "follow" and "push"
- more granular scopes are available, please refer to Mastodon documentation for which) on the instance given - more granular scopes are available, please refer to Mastodon documentation for which) on the instance given
@ -36,6 +38,8 @@ class Mastodon(Internals):
Specify `session` with a requests.Session for it to be used instead of the default. This can be Specify `session` with a requests.Session for it to be used instead of the default. This can be
used to, amongst other things, adjust proxy or SSL certificate settings. used to, amongst other things, adjust proxy or SSL certificate settings.
Specify `user_agent` if you want to use a specific name as `User-Agent` header, otherwise "mastodonpy" will be used.
Presently, app registration is open by default, but this is not guaranteed to be the case for all Presently, app registration is open by default, but this is not guaranteed to be the case for all
Mastodon instances in the future. Mastodon instances in the future.
@ -50,8 +54,10 @@ class Mastodon(Internals):
'client_name': client_name, 'client_name': client_name,
'scopes': " ".join(scopes) 'scopes': " ".join(scopes)
} }
headers = {
'User-Agent': user_agent
}
try:
if redirect_uris is not None: if redirect_uris is not None:
if isinstance(redirect_uris, (list, tuple)): if isinstance(redirect_uris, (list, tuple)):
redirect_uris = "\n".join(list(redirect_uris)) redirect_uris = "\n".join(list(redirect_uris))
@ -60,11 +66,12 @@ class Mastodon(Internals):
request_data['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob' request_data['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob'
if website is not None: if website is not None:
request_data['website'] = website request_data['website'] = website
try:
if session: if session:
ret = session.post(f"{api_base_url}/api/v1/apps", data=request_data, timeout=request_timeout) ret = session.post(f"{api_base_url}/api/v1/apps", data=request_data, headers=headers, timeout=request_timeout)
response = ret.json() response = ret.json()
else: else:
response = requests.post(f"{api_base_url}/api/v1/apps", data=request_data, timeout=request_timeout) response = requests.post(f"{api_base_url}/api/v1/apps", data=request_data, headers=headers, timeout=request_timeout)
response = response.json() response = response.json()
except Exception as e: except Exception as e:
raise MastodonNetworkError(f"Could not complete request: {e}") raise MastodonNetworkError(f"Could not complete request: {e}")
@ -83,7 +90,7 @@ class Mastodon(Internals):
### ###
def __init__(self, client_id=None, client_secret=None, access_token=None, api_base_url=None, debug_requests=False, def __init__(self, client_id=None, client_secret=None, access_token=None, api_base_url=None, debug_requests=False,
ratelimit_method="wait", ratelimit_pacefactor=1.1, request_timeout=_DEFAULT_TIMEOUT, mastodon_version=None, ratelimit_method="wait", ratelimit_pacefactor=1.1, request_timeout=_DEFAULT_TIMEOUT, mastodon_version=None,
version_check_mode="created", session=None, feature_set="mainline", user_agent="mastodonpy", lang=None): version_check_mode="created", session=None, feature_set="mainline", user_agent=_DEFAULT_USER_AGENT, lang=None):
""" """
Create a new API wrapper instance based on the given `client_secret` and `client_id` on the Create a new API wrapper instance based on the given `client_secret` and `client_id` on the
instance given by `api_base_url`. If you give a `client_id` and it is not a file, you must instance given by `api_base_url`. If you give a `client_id` and it is not a file, you must

View File

@ -1,6 +1,7 @@
from mastodon import Mastodon from mastodon import Mastodon, MastodonNetworkError
import pytest import pytest
import requests import requests
from requests import HTTPError
import time import time
try: try:
@ -8,7 +9,7 @@ try:
except ImportError: except ImportError:
from unittest.mock import Mock from unittest.mock import Mock
def test_create_app(mocker, to_file=None, redirect_uris=None, website=None): def test_create_app(mocker, to_file=None, redirect_uris=None, website=None, user_agent="mastodonpy"):
# there is no easy way to delete an anonymously created app so # there is no easy way to delete an anonymously created app so
# instead we mock Requests # instead we mock Requests
resp = Mock() resp = Mock()
@ -22,7 +23,8 @@ def test_create_app(mocker, to_file=None, redirect_uris=None, website=None):
api_base_url="example.com", api_base_url="example.com",
to_file=to_file, to_file=to_file,
redirect_uris=redirect_uris, redirect_uris=redirect_uris,
website=website website=website,
user_agent=user_agent
) )
assert app == ('foo', 'bar') assert app == ('foo', 'bar')
@ -38,11 +40,39 @@ def test_create_app_redirect_uris(mocker):
kwargs = requests.post.call_args[1] kwargs = requests.post.call_args[1]
assert kwargs['data']['redirect_uris'] == 'http://example.net' assert kwargs['data']['redirect_uris'] == 'http://example.net'
def test_create_app_multiple_redirect_uris(mocker):
test_create_app(mocker, redirect_uris=['http://example.net', 'https://example.net'])
kwargs = requests.post.call_args[1]
assert kwargs['data']['redirect_uris'] == 'http://example.net\nhttps://example.net'
def test_create_app_website(mocker): def test_create_app_website(mocker):
test_create_app(mocker, website='http://example.net') test_create_app(mocker, website='http://example.net')
kwargs = requests.post.call_args[1] kwargs = requests.post.call_args[1]
assert kwargs['data']['website'] == 'http://example.net' assert kwargs['data']['website'] == 'http://example.net'
def test_create_app_session():
resp = Mock(**{'json.return_value': {'client_id': 'foo', 'client_secret': 'bar'}})
sess = Mock(**{'post.return_value': resp})
app = Mastodon.create_app("Mastodon.py test suite", api_base_url="example.com", session=sess)
assert app == ('foo', 'bar')
sess.post.assert_called()
def test_create_app_error(mocker):
def post(_url, **_kwargs):
raise HTTPError("Unauthorized")
mocker.patch('requests.post', side_effect=post)
with pytest.raises(MastodonNetworkError):
Mastodon.create_app("Mastodon.py test suite", api_base_url="example.com")
def test_create_app_user_agent(mocker):
test_create_app(mocker, user_agent="pytest")
kwargs = requests.post.call_args[1]
assert kwargs['headers']['User-Agent'] == 'pytest'
@pytest.mark.vcr() @pytest.mark.vcr()
def test_app_verify_credentials(api): def test_app_verify_credentials(api):
app = api.app_verify_credentials() app = api.app_verify_credentials()
@ -54,7 +84,7 @@ def test_app_account_create():
# This leaves behind stuff on the test server, which is unfortunate, but eh. # This leaves behind stuff on the test server, which is unfortunate, but eh.
suffix = str(time.time()).replace(".", "")[-5:] suffix = str(time.time()).replace(".", "")[-5:]
test_app = test_app = Mastodon.create_app( test_app = Mastodon.create_app(
"mastodon.py generated test app", "mastodon.py generated test app",
api_base_url="http://localhost:3000/" api_base_url="http://localhost:3000/"
) )
@ -74,7 +104,7 @@ def test_app_account_create():
def test_app_account_create_invalid(): def test_app_account_create_invalid():
suffix = str(time.time()).replace(".", "")[-5:] suffix = str(time.time()).replace(".", "")[-5:]
test_app = test_app = Mastodon.create_app( test_app = Mastodon.create_app(
"mastodon.py generated test app", "mastodon.py generated test app",
api_base_url="http://localhost:3000/" api_base_url="http://localhost:3000/"
) )