Merge branch 'master' into master

pull/306/head
Lorenz Diener 2023-04-23 18:54:56 +03:00 zatwierdzone przez GitHub
commit 3b5c4ae6f5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
20 zmienionych plików z 153 dodań i 107 usunięć

Wyświetl plik

@ -2,6 +2,12 @@ A note on versioning: This librarys major version will grow with the APIs
version number. Breaking changes will be indicated by a change in the minor
(or major) version number, and will generally be avoided.
v1.8.0 (in progress)
--------------------
* Replace some lambdas with list comprenehsions (thanks eumiro)
* Add `resolve` keyword to `account_search` (thanks zevaryx)
* Add support for user agent header in `create_app` (thanks jkawamoto)
v1.8.0
------
* Overall: Support level is now 3.5.5 (last before 4.0.0)

17
Pipfile
Wyświetl plik

@ -1,17 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
[dev-packages]
mastodon-py = {editable = true, extras = ["tests"], path = "."}
pytest = "<4"
pytest-runner = "*"
pytest-cov = "*"
vcrpy = "*"
pytest-vcr = "<1"
pytest-mock = "*"
requests-mock = "*"

Wyświetl plik

@ -143,7 +143,7 @@ class Mastodon(Internals):
def me(self):
"""
Get this user's account. Synonym for `account_verify_credentials()`, does exactly
the same thing, just exists becase `account_verify_credentials()` has a confusing
the same thing, just exists because `account_verify_credentials()` has a confusing
name.
"""
return self.account_verify_credentials()
@ -244,7 +244,7 @@ class Mastodon(Internals):
params)
@api_version("1.0.0", "2.3.0", _DICT_VERSION_ACCOUNT)
def account_search(self, q, limit=None, following=False):
def account_search(self, q, limit=None, following=False, resolve=False):
"""
Fetch matching accounts. Will lookup an account remotely if the search term is
in the username@domain format and not yet in the database. Set `following` to

Wyświetl plik

@ -43,7 +43,7 @@ class Mastodon(Internals):
if role_ids is not None:
if not isinstance(role_ids, list):
role_ids = [role_ids]
role_ids = list(map(self.__unpack_id, role_ids))
role_ids = [self.__unpack_id(x) for x in role_ids]
if invited_by is not None:
invited_by = self.__unpack_id(invited_by)

Wyświetl plik

@ -9,7 +9,7 @@ import collections
from .errors import MastodonIllegalArgumentError, MastodonNetworkError, MastodonVersionError, MastodonAPIError
from .versions import _DICT_VERSION_APPLICATION
from .defaults import _DEFAULT_SCOPES, _SCOPE_SETS, _DEFAULT_TIMEOUT
from .defaults import _DEFAULT_SCOPES, _SCOPE_SETS, _DEFAULT_TIMEOUT, _DEFAULT_USER_AGENT
from .utility import parse_version_string, api_version
from .internals import Mastodon as Internals
@ -21,7 +21,7 @@ class Mastodon(Internals):
###
@staticmethod
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"
- more granular scopes are available, please refer to Mastodon documentation for which) on the instance given
@ -37,6 +37,8 @@ class Mastodon(Internals):
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.
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
Mastodon instances in the future.
@ -51,21 +53,24 @@ class Mastodon(Internals):
'client_name': client_name,
'scopes': " ".join(scopes)
}
headers = {
'User-Agent': user_agent
}
if redirect_uris is not None:
if isinstance(redirect_uris, (list, tuple)):
redirect_uris = "\n".join(list(redirect_uris))
request_data['redirect_uris'] = redirect_uris
else:
request_data['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob'
if website is not None:
request_data['website'] = website
try:
if redirect_uris is not None:
if isinstance(redirect_uris, (list, tuple)):
redirect_uris = "\n".join(list(redirect_uris))
request_data['redirect_uris'] = redirect_uris
else:
request_data['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob'
if website is not None:
request_data['website'] = website
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()
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()
except Exception as e:
raise MastodonNetworkError(f"Could not complete request: {e}")
@ -84,7 +89,7 @@ class Mastodon(Internals):
###
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,
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
instance given by `api_base_url`. If you give a `client_id` and it is not a file, you must

Wyświetl plik

@ -3,6 +3,7 @@
_DEFAULT_TIMEOUT = 300
_DEFAULT_STREAM_TIMEOUT = 300
_DEFAULT_STREAM_RECONNECT_WAIT_SEC = 5
_DEFAULT_USER_AGENT = "mastodonpy"
_DEFAULT_SCOPES = ['read', 'write', 'follow', 'push']
_SCOPE_SETS = {
'read': [

Wyświetl plik

@ -15,6 +15,11 @@ class Mastodon(Internals):
"""
Fetch the logged-in user's favourited statuses.
This endpoint uses internal ids for pagination, passing status ids to
`max_id`, `min_id`, or `since_id` will not work. Pagination functions
:ref:`fetch_next() <fetch_next()>`
and :ref:`fetch_previous() <fetch_previous()>` should be used instead.
Returns a list of :ref:`status dicts <status dicts>`.
"""
if max_id is not None:
@ -37,6 +42,11 @@ class Mastodon(Internals):
"""
Get a list of statuses bookmarked by the logged-in user.
This endpoint uses internal ids for pagination, passing status ids to
`max_id`, `min_id`, or `since_id` will not work. Pagination functions
:ref:`fetch_next() <fetch_next()>`
and :ref:`fetch_previous() <fetch_previous()>` should be used instead.
Returns a list of :ref:`status dicts <status dicts>`.
"""
if max_id is not None:

Wyświetl plik

@ -48,7 +48,7 @@ class Mastodon(Internals):
since_id = self.__unpack_id(since_id, dateconv=True)
params = self.__generate_params(locals(), ['id'])
return self.__api_request('GET', f'/api/v1/lists/{id}/accounts')
return self.__api_request('GET', f'/api/v1/lists/{id}/accounts', params)
###
# Writing data: Lists
@ -91,7 +91,7 @@ class Mastodon(Internals):
if not isinstance(account_ids, list):
account_ids = [account_ids]
account_ids = list(map(lambda x: self.__unpack_id(x), account_ids))
account_ids = [self.__unpack_id(x) for x in account_ids]
params = self.__generate_params(locals(), ['id'])
self.__api_request('POST', f'/api/v1/lists/{id}/accounts', params)
@ -105,7 +105,7 @@ class Mastodon(Internals):
if not isinstance(account_ids, list):
account_ids = [account_ids]
account_ids = list(map(lambda x: self.__unpack_id(x), account_ids))
account_ids = [self.__unpack_id(x) for x in account_ids]
params = self.__generate_params(locals(), ['id'])
self.__api_request('DELETE', f'/api/v1/lists/{id}/accounts', params)

Wyświetl plik

@ -53,7 +53,7 @@ class Mastodon(Internals):
if status_ids is not None:
if not isinstance(status_ids, list):
status_ids = [status_ids]
status_ids = list(map(lambda x: self.__unpack_id(x), status_ids))
status_ids = [self.__unpack_id(x) for x in status_ids]
params_initial = locals()
if not forward:

Wyświetl plik

@ -321,6 +321,8 @@ class Mastodon(Internals):
the users that are being replied to the status text and retains
CW and visibility if not explicitly overridden.
Note that `to_status` should be a :ref:`status dict <status dict>` and not an ID.
Set `untag` to True if you want the reply to only go to the user you
are replying to, removing every other mentioned user from the
conversation.
@ -334,7 +336,10 @@ class Mastodon(Internals):
# Determine users to mention
mentioned_accounts = collections.OrderedDict()
mentioned_accounts[to_status.account.id] = to_status.account.acct
try:
mentioned_accounts[to_status.account.id] = to_status.account.acct
except AttributeError as e:
raise TypeError("to_status must specify a status dict!") from e
if not untag:
for account in to_status.mentions:
@ -342,8 +347,7 @@ class Mastodon(Internals):
mentioned_accounts[account.id] = account.acct
# Join into one piece of text. The space is added inside because of self-replies.
status = "".join(map(lambda x: "@" + x + " ",
mentioned_accounts.values())) + status
status = " ".join(f"@{x}" for x in mentioned_accounts.values()) + " " + status
# Retain visibility / cw
if visibility is None and 'visibility' in to_status:

Wyświetl plik

@ -1,3 +1,5 @@
from pathlib import Path
from setuptools import setup
test_deps = [
@ -26,15 +28,21 @@ extras = {
"blurhash": blurhash_deps,
}
this_directory = Path(__file__).parent
long_description = (this_directory / "README.rst").read_text()
setup(name='Mastodon.py',
version='1.8.0',
description='Python wrapper for the Mastodon API',
long_description=long_description,
long_description_content_type='text/x-rst',
packages=['mastodon'],
install_requires=[
'requests>=2.4.2',
'python-dateutil',
'six',
'python-magic',
'six',
'python-magic-bin ; platform_system=="Windows"', # pragma: no cover
'python-magic ; platform_system!="Windows"',
'decorator>=4.0.0',
] + blurhash_deps,
tests_require=test_deps,

Wyświetl plik

@ -206,14 +206,14 @@ def test_account_pin_unpin(api, api2):
try:
assert relationship
assert relationship['endorsed']
assert user["id"] in map(lambda x: x["id"], endorsed)
assert any(x["id"] == user["id"] for x in endorsed)
finally:
relationship = api.account_unpin(user)
endorsed2 = api.endorsements()
api.account_unfollow(user)
assert relationship
assert not relationship['endorsed']
assert not user["id"] in map(lambda x: x["id"], endorsed2)
assert not any(x["id"] == user["id"] for x in endorsed2)
@pytest.mark.vcr()
def test_preferences(api):

Wyświetl plik

@ -173,7 +173,7 @@ def test_admin_domain_blocks(api2):
assert block3.public_comment == "sicko behaviour"
assert block3.private_comment == "jk ilu <3"
api2.admin_delete_domain_block(block2)
assert not block3.id in map(lambda x: x.id, api2.admin_domain_blocks())
assert not any(x.id == block3.id for x in api2.admin_domain_blocks())
@pytest.mark.vcr(match_on=['path'])
def test_admin_stats(api2):

Wyświetl plik

@ -1,6 +1,7 @@
from mastodon import Mastodon
from mastodon import Mastodon, MastodonNetworkError
import pytest
import requests
from requests import HTTPError
import time
try:
@ -8,7 +9,7 @@ try:
except ImportError:
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
# instead we mock Requests
resp = Mock()
@ -22,7 +23,8 @@ def test_create_app(mocker, to_file=None, redirect_uris=None, website=None):
api_base_url="example.com",
to_file=to_file,
redirect_uris=redirect_uris,
website=website
website=website,
user_agent=user_agent
)
assert app == ('foo', 'bar')
@ -38,11 +40,39 @@ def test_create_app_redirect_uris(mocker):
kwargs = requests.post.call_args[1]
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):
test_create_app(mocker, website='http://example.net')
kwargs = requests.post.call_args[1]
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()
def test_app_verify_credentials(api):
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.
suffix = str(time.time()).replace(".", "")[-5:]
test_app = test_app = Mastodon.create_app(
test_app = Mastodon.create_app(
"mastodon.py generated test app",
api_base_url="http://localhost:3000/"
)
@ -74,7 +104,7 @@ def test_app_account_create():
def test_app_account_create_invalid():
suffix = str(time.time()).replace(".", "")[-5:]
test_app = test_app = Mastodon.create_app(
test_app = Mastodon.create_app(
"mastodon.py generated test app",
api_base_url="http://localhost:3000/"
)

Wyświetl plik

@ -58,10 +58,11 @@ def test_filter_serverside(api, api2):
time.sleep(2)
tl = api.timeline_home()
try:
assert not status_1['id'] in map(lambda st: st['id'], tl)
assert not status_2['id'] in map(lambda st: st['id'], tl)
assert status_3['id'] in map(lambda st: st['id'], tl)
assert status_4['id'] in map(lambda st: st['id'], tl)
st_ids = {st["id"] for st in tl}
assert status_1["id"] not in st_ids
assert status_2["id"] not in st_ids
assert status_3["id"] in st_ids
assert status_4["id"] in st_ids
finally:
api.filter_delete(keyword_filter_1)
api.filter_delete(keyword_filter_2)
@ -94,16 +95,18 @@ def test_filter_clientside(api, api2):
tl = api.timeline_home()
try:
assert status_1['id'] in map(lambda st: st['id'], tl)
assert status_2['id'] in map(lambda st: st['id'], tl)
assert status_3['id'] in map(lambda st: st['id'], tl)
assert status_4['id'] in map(lambda st: st['id'], tl)
st_ids = {st["id"] for st in tl}
assert status_1['id'] in st_ids
assert status_2['id'] in st_ids
assert status_3['id'] in st_ids
assert status_4['id'] in st_ids
filtered = api.filters_apply(tl, [keyword_filter_1, keyword_filter_2, keyword_filter_3], 'home')
assert not status_1['id'] in map(lambda st: st['id'], filtered)
assert not status_2['id'] in map(lambda st: st['id'], filtered)
assert status_3['id'] in map(lambda st: st['id'], filtered)
assert status_4['id'] in map(lambda st: st['id'], filtered)
st_ids = {st["id"] for st in filtered}
assert status_1['id'] not in st_ids
assert status_2['id'] not in st_ids
assert status_3['id'] in st_ids
assert status_4['id'] in st_ids
finally:
api.filter_delete(keyword_filter_1)
api.filter_delete(keyword_filter_2)

Wyświetl plik

@ -24,15 +24,15 @@ def test_list_add_remove_account(api, api2, mastodon_list):
api.account_follow(user)
api.list_accounts_add(mastodon_list, user)
assert user.id in map(lambda x: x.id, api.list_accounts(mastodon_list))
assert any(x.id == user.id for x in api.list_accounts(mastodon_list))
api.account_unfollow(user)
assert len(api.list_accounts(mastodon_list)) == 0
api.account_follow(user)
api.list_accounts_add(mastodon_list, user)
assert user.id in map(lambda x: x.id, api.list_accounts(mastodon_list))
assert any(x.id == user.id for x in api.list_accounts(mastodon_list))
api.list_accounts_delete(mastodon_list, user)
assert len(api.list_accounts(mastodon_list)) == 0
@ -56,9 +56,8 @@ def test_list_timeline(api, api2, mastodon_list):
status = api2.status_post("I have never stolen a ham in my life.", visibility="public")
time.sleep(2)
list_tl = list(map(lambda x: x.id, api.timeline_list(mastodon_list)))
assert status.id in list_tl
assert any(x.id == status.id for x in api.timeline_list(mastodon_list))
api2.status_delete(status)
api.account_unfollow(user)

Wyświetl plik

@ -172,15 +172,15 @@ def test_scheduled_status(api):
assert scheduled_toot_2.scheduled_at < scheduled_toot.scheduled_at
scheduled_toot_list = api.scheduled_statuses()
assert scheduled_toot_2.id in map(lambda x: x.id, scheduled_toot_list)
assert any(x.id == scheduled_toot_2.id for x in scheduled_toot_list)
scheduled_toot_3 = api.scheduled_status(scheduled_toot.id)
assert scheduled_toot_2.id == scheduled_toot_3.id
api.scheduled_status_delete(scheduled_toot_2)
scheduled_toot_list_2 = api.scheduled_statuses()
assert not scheduled_toot_2.id in map(lambda x: x.id, scheduled_toot_list_2)
assert not any(x.id == scheduled_toot_2.id for x in scheduled_toot_list_2)
if os.path.exists("tests/cassettes/test_scheduled_status_datetimeobjects.pkl"):
the_very_immediate_future = datetime.datetime.fromtimestamp(pickle.load(open("tests/cassettes/test_scheduled_status_datetimeobjects.pkl", 'rb')))
else:
@ -190,9 +190,9 @@ def test_scheduled_status(api):
time.sleep(15)
statuses = api.timeline_home()
scheduled_toot_list_3 = api.scheduled_statuses()
assert scheduled_toot_4.id in map(lambda x: x.id, statuses)
assert not scheduled_toot_4.id in map(lambda x: x.id, scheduled_toot_list_3)
assert any(x.id == scheduled_toot_4.id for x in statuses)
assert not any(x.id == scheduled_toot_4.id for x in scheduled_toot_list_3)
# The following two tests need to be manually (!) ran 10 minutes apart when recording.
# Sorry, I can't think of a better way to test scheduled statuses actually work as intended.
@pytest.mark.vcr(match_on=['path'])
@ -205,7 +205,7 @@ def test_scheduled_status_long_part1(api):
pickle.dump(the_medium_term_future.timestamp(), open("tests/cassettes_special/test_scheduled_status_long_datetimeobjects.pkl", 'wb'))
scheduled_toot = api.status_post(f"please ensure maximum headroom at {the_medium_term_future}", scheduled_at=the_medium_term_future)
scheduled_toot_list = api.scheduled_statuses()
assert scheduled_toot.id in map(lambda x: x.id, scheduled_toot_list)
assert any(x.id == scheduled_toot.id for x in scheduled_toot_list)
pickle.dump(scheduled_toot.params.text, open("tests/cassettes_special/test_scheduled_status_long_text.pkl", 'wb'))
@pytest.mark.vcr(match_on=['path'])

Wyświetl plik

@ -323,17 +323,17 @@ def test_stream_user_direct(api, api2, api3):
conversations = []
edits = []
listener = CallbackStreamListener(
update_handler = lambda x: updates.append(x),
local_update_handler = lambda x: local_updates.append(x),
notification_handler = lambda x: notifications.append(x),
delete_handler = lambda x: deletes.append(x),
conversation_handler = lambda x: conversations.append(x),
status_update_handler = lambda x: edits.append(x),
filters_changed_handler = lambda x: 0,
announcement_handler = lambda x: 0,
announcement_reaction_handler = lambda x: 0,
announcement_delete_handler = lambda x: 0,
encryted_message_handler = lambda x: 0,
update_handler=updates.append,
local_update_handler=local_updates.append,
notification_handler=notifications.append,
delete_handler=deletes.append,
conversation_handler=conversations.append,
status_update_handler=edits.append,
filters_changed_handler=lambda x: 0,
announcement_handler=lambda x: 0,
announcement_reaction_handler=lambda x: 0,
announcement_delete_handler=lambda x: 0,
encryted_message_handler=lambda x: 0,
)
posted = []
@ -384,7 +384,7 @@ def test_stream_user_local(api, api2):
updates = []
listener = CallbackStreamListener(
local_update_handler = lambda x: updates.append(x),
local_update_handler=updates.append,
)
posted = []
@ -412,7 +412,7 @@ def test_stream_direct(api, api2):
conversations = []
listener = CallbackStreamListener(
conversation_handler = lambda x: conversations.append(x),
conversation_handler=conversations.append,
)
def do_activities():

Wyświetl plik

@ -9,14 +9,14 @@ import os
def test_public_tl_anonymous(api_anonymous, status3):
time.sleep(3)
tl = api_anonymous.timeline_public()
assert status3['id'] in list(map(lambda st: st['id'], tl))
assert any(st["id"] == status3["id"] for st in tl)
@pytest.mark.vcr()
def test_public_tl(api, status):
public = api.timeline_public()
local = api.timeline_local()
assert status['id'] in map(lambda st: st['id'], public)
assert status['id'] in map(lambda st: st['id'], local)
assert any(st["id"] == status["id"] for st in public)
assert any(st["id"] == status["id"] for st in local)
@pytest.mark.vcr()
def test_unauthed_home_tl_throws(api_anonymous, status):
@ -27,14 +27,14 @@ def test_unauthed_home_tl_throws(api_anonymous, status):
def test_home_tl(api, status):
time.sleep(3)
tl = api.timeline_home()
assert status['id'] in map(lambda st: st['id'], tl)
assert any(st["id"] == status["id"] for st in tl)
@pytest.mark.vcr()
def test_hashtag_tl(api):
status = api.status_post('#hoot (hashtag toot)')
tl = api.timeline_hashtag('hoot')
try:
assert status['id'] in map(lambda st: st['id'], tl)
assert any(st["id"] == status["id"] for st in tl)
finally:
api.status_delete(status['id'])
@ -58,8 +58,8 @@ def test_conversations(api, api2):
conversations2 = api2.conversations()
api.status_delete(status)
assert conversations
assert status.id in map(lambda x: x.last_status.id, conversations)
assert account.id in map(lambda x: x.accounts[0].id, conversations)
assert any(x.last_status.id == status.id for x in conversations)
assert any(x.accounts[0].id == account.id for x in conversations)
assert conversations[0].unread is True
assert conversations2[0].unread is False
@ -67,16 +67,16 @@ def test_conversations(api, api2):
def test_min_max_id(api, status):
time.sleep(3)
tl = api.timeline_home(min_id = status.id - 1000, max_id = status.id + 1000)
assert status['id'] in map(lambda st: st['id'], tl)
assert any(st["id"] == status["id"] for st in tl)
tl = api.timeline_home(min_id = status.id - 2000, max_id = status.id - 1000)
assert not status['id'] in map(lambda st: st['id'], tl)
assert not any(st["id"] == status["id"] for st in tl)
tl = api.timeline_home(min_id = status.id + 1000, max_id = status.id + 2000)
assert not status['id'] in map(lambda st: st['id'], tl)
assert not any(st["id"] == status["id"] for st in tl)
tl = api.timeline_home(since_id = status.id - 1000)
assert status['id'] in map(lambda st: st['id'], tl)
assert any(st["id"] == status["id"] for st in tl)
@pytest.mark.vcr()
def test_min_max_id_datetimes(api, status):
@ -99,7 +99,7 @@ def test_min_max_id_datetimes(api, status):
time.sleep(3)
tl = api.timeline_home(min_id = the_past, max_id = the_future)
assert status['id'] in map(lambda st: st['id'], tl)
assert any(st["id"] == status["id"] for st in tl)
tl = api.timeline_home(min_id = the_future, max_id = the_far_future)
assert not status['id'] in map(lambda st: st['id'], tl)
assert not any(st["id"] == status["id"] for st in tl)

Wyświetl plik

@ -1,11 +1,8 @@
[tox]
envlist = py36,py37
envlist = py36,py37,py38,py39,py310,py311
skipsdist = true
[testenv]
deps = pipenv==2018.11.14
passenv = HOME
commands =
pipenv sync -d
pipenv run pytest
deps = .[test]
commands = python setup.py test