Added support for recent CPython and PyPy versions; Droped Python v2.7 Support (#680)

This commit is contained in:
Andreas Motl
2022-10-08 02:28:36 +02:00
committed by GitHub
parent f7244cce3d
commit 00afe4e5b6
164 changed files with 746 additions and 2787 deletions

View File

@ -26,16 +26,9 @@
from __future__ import print_function
import re
import sys
import six
import pytest
import requests
try:
# Python 3.x
from unittest import mock
except ImportError:
# Python 2.7
import mock
from unittest import mock
from os.path import dirname
from os.path import join
@ -58,19 +51,14 @@ from apprise.plugins import __reset_matrix
from apprise.utils import parse_list
import inspect
# Sending notifications requires the coroutines to be awaited, so we need to
# wrap the original function when mocking it.
import apprise.py3compat.asyncio as py3aio
# Disable logging for a cleaner testing output
import logging
logging.disable(logging.CRITICAL)
# Sending notifications requires the coroutines to be awaited, so we need to
# wrap the original function when mocking it. But don't import for Python 2.
if not six.PY2:
import apprise.py3compat.asyncio as py3aio
else:
class py3aio:
def notify():
pass
# Attachment Directory
TEST_VAR_DIR = join(dirname(__file__), 'var')
@ -86,7 +74,6 @@ def test_apprise():
apprise_test(do_notify)
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
def test_apprise_async():
"""
API: Apprise() object asynchronous methods
@ -154,12 +141,12 @@ def apprise_test(do_notify):
assert len(a) == 2
# We can retrieve elements from our list too by reference:
assert isinstance(a[0].url(), six.string_types) is True
assert isinstance(a[0].url(), str) is True
# We can iterate over our list too:
count = 0
for o in a:
assert isinstance(o.url(), six.string_types) is True
assert isinstance(o.url(), str) is True
count += 1
# verify that we did indeed iterate over each element
assert len(a) == count
@ -547,7 +534,6 @@ def test_apprise_tagging(mock_post, mock_get):
@mock.patch('requests.get')
@mock.patch('requests.post')
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
def test_apprise_tagging_async(mock_post, mock_get):
"""
API: Apprise() object tagging functionality asynchronous methods
@ -669,7 +655,6 @@ def apprise_tagging_test(mock_post, mock_get, do_notify):
tag=[(object, ), ]) is None
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
def test_apprise_schemas(tmpdir):
"""
API: Apprise().schema() tests
@ -918,20 +903,11 @@ def test_apprise_asset(tmpdir):
must_exist=True) is not None
# Test case where we can't access the image file
if sys.version_info.major <= 2:
# Python v2.x
with mock.patch('__builtin__.open', side_effect=OSError()):
assert a.image_raw(NotifyType.INFO, NotifyImageSize.XY_256) is None
with mock.patch('builtins.open', side_effect=OSError()):
assert a.image_raw(NotifyType.INFO, NotifyImageSize.XY_256) is None
# Our content is retrivable again
assert a.image_raw(NotifyType.INFO, NotifyImageSize.XY_256) is not None
else:
# Python >= v3.x
with mock.patch('builtins.open', side_effect=OSError()):
assert a.image_raw(NotifyType.INFO, NotifyImageSize.XY_256) is None
# Our content is retrivable again
assert a.image_raw(NotifyType.INFO, NotifyImageSize.XY_256) is not None
# Our content is retrivable again
assert a.image_raw(NotifyType.INFO, NotifyImageSize.XY_256) is not None
# Disable all image references
a = AppriseAsset(image_path_mask=False, image_url_mask=False)
@ -1376,7 +1352,7 @@ def test_apprise_details():
assert 'details' in entry['requirements']
assert 'packages_required' in entry['requirements']
assert 'packages_recommended' in entry['requirements']
assert isinstance(entry['requirements']['details'], six.string_types)
assert isinstance(entry['requirements']['details'], str)
assert isinstance(entry['requirements']['packages_required'], list)
assert isinstance(entry['requirements']['packages_recommended'], list)
@ -1403,7 +1379,7 @@ def test_apprise_details():
assert 'details' in entry['requirements']
assert 'packages_required' in entry['requirements']
assert 'packages_recommended' in entry['requirements']
assert isinstance(entry['requirements']['details'], six.string_types)
assert isinstance(entry['requirements']['details'], str)
assert isinstance(entry['requirements']['packages_required'], list)
assert isinstance(entry['requirements']['packages_recommended'], list)
@ -1498,7 +1474,7 @@ def test_apprise_details_plugin_verification():
# A Service Name MUST be defined
assert 'service_name' in entry
assert isinstance(
entry['service_name'], (six.string_types, LazyTranslation))
entry['service_name'], (str, LazyTranslation))
# Acquire our protocols
protocols = parse_list(
@ -1527,10 +1503,10 @@ def test_apprise_details_plugin_verification():
if 'alias_of' not in arg:
# Minimum requirement of an argument
assert 'name' in arg
assert isinstance(arg['name'], six.string_types)
assert isinstance(arg['name'], str)
assert 'type' in arg
assert isinstance(arg['type'], six.string_types)
assert isinstance(arg['type'], str)
assert is_valid_type_re.match(arg['type']) is not None
if 'min' in arg:
@ -1555,7 +1531,7 @@ def test_apprise_details_plugin_verification():
assert isinstance(arg['required'], bool)
if 'prefix' in arg:
assert isinstance(arg['prefix'], six.string_types)
assert isinstance(arg['prefix'], str)
if section == 'kwargs':
# The only acceptable prefix types for kwargs
assert arg['prefix'] in (':', '+', '-')
@ -1566,7 +1542,7 @@ def test_apprise_details_plugin_verification():
if 'map_to' in arg:
# must be a string
assert isinstance(arg['map_to'], six.string_types)
assert isinstance(arg['map_to'], str)
# Track our map_to object
map_to_entries.add(arg['map_to'])
@ -1601,9 +1577,9 @@ def test_apprise_details_plugin_verification():
# Regex must ALWAYS be in the format (regex, option)
assert isinstance(arg['regex'], (tuple, list))
assert len(arg['regex']) == 2
assert isinstance(arg['regex'][0], six.string_types)
assert isinstance(arg['regex'][0], str)
assert arg['regex'][1] is None or isinstance(
arg['regex'][1], six.string_types)
arg['regex'][1], str)
# Compile the regular expression to verify that it is
# valid
@ -1632,10 +1608,10 @@ def test_apprise_details_plugin_verification():
# must be a string
assert isinstance(
arg['alias_of'], (six.string_types, list, tuple, set))
arg['alias_of'], (str, list, tuple, set))
aliases = [arg['alias_of']] \
if isinstance(arg['alias_of'], six.string_types) \
if isinstance(arg['alias_of'], str) \
else arg['alias_of']
for alias_of in aliases:
@ -1687,7 +1663,7 @@ def test_apprise_details_plugin_verification():
# 'alias_of': ('apitoken', 'webtoken'),
# },
# }
if isinstance(arg['alias_of'], six.string_types):
if isinstance(arg['alias_of'], str):
assert len(entry['details'][section][key]) == 1
else: # is tuple,list, or set
assert len(entry['details'][section][key]) == 2
@ -1711,23 +1687,12 @@ def test_apprise_details_plugin_verification():
(tuple, set, list),
)
if six.PY2:
# inspect our object
# getargspec() is deprecated in Python v3
spec = inspect.getargspec(
common.NOTIFY_SCHEMA_MAP[protocols[0]].__init__)
spec = inspect.getfullargspec(
common.NOTIFY_SCHEMA_MAP[protocols[0]].__init__)
function_args = \
(set(parse_list(spec.keywords)) - set(['kwargs'])) \
| (set(spec.args) - set(['self'])) | valid_kwargs
else:
# Python v3+ uses getfullargspec()
spec = inspect.getfullargspec(
common.NOTIFY_SCHEMA_MAP[protocols[0]].__init__)
function_args = \
(set(parse_list(spec.varkw)) - set(['kwargs'])) \
| (set(spec.args) - set(['self'])) | valid_kwargs
function_args = \
(set(parse_list(spec.varkw)) - set(['kwargs'])) \
| (set(spec.args) - set(['self'])) | valid_kwargs
# Iterate over our map_to_entries and make sure that everything
# maps to a function argument
@ -1790,7 +1755,6 @@ def test_apprise_details_plugin_verification():
assert arg in defined_tokens
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
@mock.patch('requests.post')
@mock.patch('apprise.py3compat.asyncio.notify', wraps=py3aio.notify)
def test_apprise_async_mode(mock_async_notify, mock_post, tmpdir):
@ -1902,13 +1866,13 @@ def test_notify_matrix_dynamic_importing(tmpdir):
# Test no app_id
base.join('NotifyBadFile1.py').write(
"""
class NotifyBadFile1(object):
class NotifyBadFile1:
pass""")
# No class of the same name
base.join('NotifyBadFile2.py').write(
"""
class BadClassName(object):
class BadClassName:
pass""")
# Exception thrown